Backup and restore

Everything SchellingBoard stores lives in one Docker volume, mounted at /data: the database (/data/data.db) and any files uploaded through the admin UI, such as guest avatars and the venue map (/data/uploads).

Your .env is not in that volume. Back it up separately — it holds AUTH_SECRET, SITE_PASSWORD, ADMIN_PASSWORD and your SMTP credentials, and restoring the data without the same AUTH_SECRET logs everyone out.

Find the volume

The examples below use $VOL for the volume name. With docker compose it is your project directory plus _data; with the plain docker run command from Deployment it is schellingboard_data.

docker volume ls | grep data
VOL=schellingboard_data

Back up without stopping the site

Don’t copy data.db with cp or tar while the site is running — a write in progress leaves you with a truncated file that looks fine until you try to restore it. Use SQLite’s own backup command, which takes a consistent snapshot of a database that is being written to:

mkdir -p ./backups
STAMP=$(date +%Y%m%d-%H%M%S)

docker run --rm \
  -v "$VOL:/data" \
  -v "$PWD/backups:/backup" \
  alpine sh -c "apk add --no-cache sqlite >/dev/null && \
    sqlite3 /data/data.db \".backup '/backup/data-$STAMP.db'\" && \
    tar czf /backup/uploads-$STAMP.tar.gz -C /data uploads"

This leaves backups/data-<stamp>.db and backups/uploads-<stamp>.tar.gz on the host. It runs against the live volume, so no downtime and no dropped requests.

Run it from cron to get regular backups, and copy the results somewhere off the machine — a backup that only exists on the server it came from does not survive losing the server.

Back up with the site stopped

If a few seconds of downtime is fine, stopping the app first means a plain archive of the whole volume is safe, and there is nothing to keep in sync:

docker compose stop app
docker run --rm -v "$VOL:/data:ro" -v "$PWD/backups:/backup" \
  alpine tar czf "/backup/volume-$STAMP.tar.gz" -C /data .
docker compose start app

Restore

Stop the app, put the files back, and start it again:

docker compose stop app

docker run --rm -v "$VOL:/data" -v "$PWD/backups:/backup" alpine sh -c "
  rm -rf /data/data.db /data/data.db-wal /data/data.db-shm /data/uploads &&
  cp /backup/data-$STAMP.db /data/data.db &&
  tar xzf /backup/uploads-$STAMP.tar.gz -C /data &&
  chown -R 1001:1001 /data"

docker compose start app

The chown matters: SchellingBoard runs as user 1001 inside the container, but the helper above writes as root, so without it the app cannot open its own database.

Restoring into a newer version of SchellingBoard works — it migrates the database on startup. Restoring into an older one does not, so pin SCHELLINGBOARD_VERSION to the version the backup came from if you are rolling back.

Check the restore afterwards by opening the site: the events, proposals and sessions should be there, and the venue map and guest avatars should load.