Skip to content

Backups

Everything an inbox holds is in one SQLite file: requests, threads, settings, keys, receipts, and the secrets it keeps sealed. Two things restore it whole:

  1. A backup of the database. A copy made by the inbox itself, never a copy of the file while it runs, which can be caught halfway through a write.
  2. INBOX_SECRET_KEY. The sealed secrets (calendar and payment connections, webhook secrets, the key that signs receipts) open only with it. Without it a backup restores every request and setting, and those connections have to be made again. Keep it apart from the backups, in a password manager.

A backup on the same disk as the inbox protects from mistakes, not from losing the server: copy backups somewhere else too.

The installer makes a backup every night at about 03:30, server time, and keeps the last seven. Any time:

Terminal window
sudo inbox backup

Each is one file in /opt/inbox/data/backups, named by its time (inbox-20261020T033012Z.db), readable by its owner only. The inbox writes it while it keeps serving (VACUUM INTO), checks it, and only then gives it its name. sudo inbox update makes one before every update, and sudo inbox restore one before every restore.

To copy them off the server, any tool that copies files will do. For example, every night after the backup, with rclone set up for your storage:

Terminal window
# /etc/cron.d/inbox-offsite (or a systemd timer): the backups to your own storage, at 04:30
30 4 * * * root rclone copy /opt/inbox/data/backups yourstorage:inbox-backups --max-age 48h
Terminal window
sudo inbox restore /opt/inbox/data/backups/inbox-20261020T033012Z.db

It looks at the file first (that it is an inbox’s database, sound, and from this version or an older one), says what it holds, and asks. Then it makes a backup of the inbox as it is now, copies the backup into place, stops the inbox only for the swap, and starts it. If the inbox does not start on it, it puts back what was there. The last five backups made before a restore are kept. A backup from an older version is brought up to date when the inbox starts; one from a newer version is refused until you update.

On a new server: run the installer with the same address, then put your INBOX_SECRET_KEY in /opt/inbox/.env in place of the one it generated (sudo nano /opt/inbox/.env), sudo inbox restart, and restore.

Terminal window
docker exec inbox node /opt/inbox/server.mjs backup /opt/inbox/data/backups/inbox-$(date -u +%Y%m%dT%H%M%SZ).db
docker exec inbox node /opt/inbox/server.mjs check-db /opt/inbox/data/backups/<file> # before a restore

The backup command makes the backups folder when it is not there yet.

To restore by hand: stop the container, replace inbox.db in the data folder with the backup (and delete inbox.db-wal and inbox.db-shm beside it), give it to uid 10001, and start the container.

Litestream copies the database to S3-compatible storage within seconds of every change, so a lost server loses at most those seconds. It runs beside the inbox, as a second line next to the nightly backups. On a server the installer set up:

Terminal window
cd /opt/inbox
sudo curl -fsSLO https://raw.githubusercontent.com/surfingdogai/inbox/main/deploy/litestream/docker-compose.litestream.yml
sudo curl -fsSLO https://raw.githubusercontent.com/surfingdogai/inbox/main/deploy/litestream/litestream.yml
sudo curl -fsSL https://raw.githubusercontent.com/surfingdogai/inbox/main/deploy/litestream/litestream.env.example -o litestream.env
sudo chmod 600 litestream.env && sudo nano litestream.env # the bucket, its endpoint, and a key for that bucket only
sudo inbox start

Its keys stay in litestream.env, apart from the inbox’s own settings. With the file there, the inbox command runs Litestream beside the inbox from then on, and stops it while a restore, a rollback or an update swaps the database. To restore from it, into a file the inbox then restores from:

Terminal window
sudo docker compose -f docker-compose.yml -f docker-compose.litestream.yml run --rm litestream \
restore -o /opt/inbox/data/from-litestream.db /opt/inbox/data/inbox.db
sudo inbox restore /opt/inbox/data/from-litestream.db

Fly.io and Render snapshot their volumes and disks daily, and DigitalOcean can back up a whole Droplet; the platforms page says how each keeps its data. The inbox’s own backup command works on each of them, and Litestream does too.

On Workers the database is D1, and D1 keeps its own history: Time Travel restores it to any minute of the last 30 days (7 on the free plan).

Terminal window
npx wrangler d1 time-travel info surfingdog-inbox
npx wrangler d1 time-travel restore surfingdog-inbox --timestamp=2026-10-20T03:30:00Z
npx wrangler d1 export surfingdog-inbox --remote --output=inbox.sql # a copy to keep elsewhere

Apart from backups, the owner app downloads everything the inbox holds, without sign-ins or secrets, ready to bring into another inbox.