Skip to content

Backup and restore

Pierre edited this page Oct 29, 2026 · 1 revision

A CSV export cannot restore a server. Protect the PostgreSQL database, image directory, deployment configuration and the exact software revision/tag used to read them.

What to keep

Item Why
Logical PostgreSQL dump Users, identities, inventory, transactions, catalogs, prices, schedules and checks
data/images Cached images, custom symbols and expensive source caches
.env and Compose overrides Configuration, signing key, database and provider secrets
Source commit / image tags Reproducible application version
import originals if irreplaceable Source material for manual image imports

Treat the backup as private: database contents and .env contain account data and secrets. Store another protected copy off the application host. Choose retention and encryption according to your own recovery needs.

Create a coordinated backup

Run from the deployment directory. This example stops API and worker writes while PostgreSQL remains available. It creates files in a new timestamped directory; review space first.

backup_dir="backups/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$backup_dir"
chmod 700 "$backup_dir"
docker compose stop api worker
docker compose exec -T db pg_dump -U tcg -d tcg -Fc > "$backup_dir/database.dump"
tar -czf "$backup_dir/images.tar.gz" -C data images
cp .env "$backup_dir/deployment.env"
cp docker-compose.yml "$backup_dir/docker-compose.yml"
# Copy any local Compose overrides into the same backup directory as well.
git rev-parse HEAD > "$backup_dir/source-commit.txt"
docker compose config --images > "$backup_dir/images.txt"
chmod 600 "$backup_dir/deployment.env"
docker compose up -d api worker

Run the sequence step by step. If dumping or archiving fails, resolve the error and bring the API/worker back up; do not report a successful backup from an empty redirected file. pg_dump is transactionally consistent for the database, while stopping the application also avoids inventory/image changes during the coordinated copy.

For source builds, use docker-compose-dev.yml and archive data-dev/images. Preserve the intended Compose project; do not mix production and development data.

Inspect the backup

docker compose exec -T db pg_restore --list < "$backup_dir/database.dump" > "$backup_dir/dump-contents.txt"
tar -tzf "$backup_dir/images.tar.gz" > "$backup_dir/image-contents.txt"

A readable archive directory is useful but insufficient. Periodically restore into an isolated test deployment and sign in to verify real data. Keep a checksum alongside transferred backup files if desired.

Restore into an isolated deployment

Use a fresh deployment directory with the same application version and PostgreSQL major version. Restore .env securely, preserving the signing key and identity-provider settings unless intentionally changing them. Use a different project name and host port if production is running.

  1. Prepare the Compose files and empty data directory.
  2. Start only PostgreSQL: docker compose up -d db.
  3. Wait until the database is healthy: docker compose ps.
  4. Restore the logical dump into the empty tcg database:
docker compose exec -T db pg_restore -U tcg -d tcg --no-owner --no-privileges --exit-on-error < /absolute/path/to/database.dump
  1. With API and worker still stopped, extract the image archive into the deployment's data directory:
mkdir -p data
tar -xzf /absolute/path/to/images.tar.gz -C data
  1. Check image file ownership for the service user, then start API and worker: docker compose up -d api worker.
  2. Inspect migrations/logs, sign in, check known quantities, image display, price history and saved schedules.
  3. For SSO on a different test hostname, configure a separate test client/callback rather than changing production redirects casually.

Do not run the restore command against an existing production database. A logical dump contains creation statements and data; an already populated target can conflict. For an existing deployment, first preserve its state and plan an explicit replacement or migration.

Physical database copies

Copying data/postgres while PostgreSQL is writing is not a valid general-purpose backup procedure. Use pg_dump, a proper PostgreSQL physical-backup mechanism, or a cleanly stopped database with compatible PostgreSQL binaries. The logical-dump procedure above avoids relying on live filesystem copying.

What to validate after recovery

Account identities, inventory counts and representative variants, custom notes/manual prices, recent stock transactions, a known card image, source settings, scheduled jobs and the expected application version. Write down the last verified restoration date.

Implementation references

Clone this wiki locally