Skip to content

Restoring a backup

Lantern’s worker dumps the whole database every night at 03:00 household time, and when an admin taps Settings > Backups > Back up now, into backups/ in the data directory (./data/backups with docker-compose.yml, the data volume on Arcane). The migrate role also dumps it before every update (pre-migrate-*.dump). Dumps are kept for 14 days (APP_BACKUP_RETENTION_DAYS); the newest is always kept. Each is a pg_dump custom-format file holding the whole database: the household, logins, calendars, events, lists, recipes, and settings. Photo files are not in it: recipe photos, the photo library’s copies from Immich, and members’ and routines’ pictures live in photos/ in the data directory (./data/photos), so back up the whole data directory, not only the dumps, and restore photos/ with the database (step 3b).

A restore replaces the database with the dump. Everything after the dump is lost, and everyone is signed out if their session is newer than it.

Terminal window
ls -l ./data/backups/

lantern-2026-10-17T07-00-00Z.dump was made at that UTC time; pre-migrate-20261017T070000Z.dump just before an update. To undo an update, use the pre-migrate dump from just before it, with the image version that was running before (see docs/runbooks/rollback.md).

2. Stop Lantern, keep the database running

Section titled “2. Stop Lantern, keep the database running”
Terminal window
docker compose stop web worker

The tools are in the Lantern image and the database container. With docker-compose.yml and the default names (POSTGRES_DB=lantern, POSTGRES_USER=lantern; use yours from .env):

Terminal window
# Copy the dump into the database container.
docker compose cp ./data/backups/lantern-2026-10-17T07-00-00Z.dump postgres:/tmp/restore.dump
# Empty the database, then restore into it.
docker compose exec postgres dropdb --username=lantern --force lantern
docker compose exec postgres createdb --username=lantern --owner=lantern lantern
docker compose exec postgres pg_restore --username=lantern --dbname=lantern --no-owner --no-privileges --exit-on-error /tmp/restore.dump
docker compose exec postgres rm /tmp/restore.dump

--exit-on-error stops at the first problem instead of leaving a half-restored database; if it stops, read the message, fix it, and run the three database commands again.

The database’s photo rows name files in photos/. Put back the photos/ folder from the same backup of the data directory, so the files match the rows:

Terminal window
rm -rf ./data/photos && cp -a /path/to/backup/data/photos ./data/photos

A photo whose files are missing shows as a placeholder and is left out of Export everything; nothing else breaks. Files with no row are never served, and can be deleted.

Terminal window
docker compose up -d

The migrate role runs first: it backs up the restored database and applies any migrations the dump predates, so a dump from an older version restores into a newer one. Check /healthz and sign in.

Checking a dump without touching the live database

Section titled “Checking a dump without touching the live database”

Restore it into a scratch database instead (step 3 with lantern_check in place of lantern, skipping dropdb), look around with psql, then dropdb lantern_check. CI does this on every pull request: apps/worker/src/jobs/backup.int.test.ts runs the backup job, restores its dump into a fresh database, and compares the rows.