Skip to content

Backup and Restore

Self-hosted deployments are responsible for their own backups — there is no managed backup service in the Docker Compose path. This page covers the two supported approaches: logical dumps (pg_dump/psql, recommended) and volume-level snapshots (faster for large databases, less portable).

Dump

docker compose exec -T postgres pg_dump -U rai -d rai --format=custom \
  > raise-backup-$(date +%Y%m%d-%H%M%S).dump

--format=custom produces a compressed, pg_restore-compatible file — the recommended format for anything beyond trivial data volumes. For a human-readable plain-SQL dump instead (e.g. to diff schema changes):

docker compose exec -T postgres pg_dump -U rai -d rai --format=plain \
  > raise-backup-$(date +%Y%m%d-%H%M%S).sql

Restore

Custom format, into a fresh database (drop and recreate first if restoring over an existing one — pg_restore does not overwrite by default):

docker compose exec -T postgres dropdb -U rai rai
docker compose exec -T postgres createdb -U rai rai
docker compose exec -T postgres pg_restore -U rai -d rai --no-owner \
  < raise-backup-20260827-120000.dump

Plain-SQL format:

docker compose exec -T postgres psql -U rai -d rai < raise-backup-20260827-120000.sql

After restoring, restart the server so any in-process caches (e.g. connection pool state) pick up a clean slate:

docker compose restart server

Verify a restore

curl http://localhost:8080/health   # expect "database": "connected"
docker compose exec postgres psql -U rai -d rai -c \
  "SELECT count(*) FROM organizations;"

Volume-level backup (alternative)

The compose file's data volume is named raise-commons-dev-pgdata (see docker-compose.md). For a full binary snapshot instead of a logical dump — faster for very large databases, but only restorable to a compatible PostgreSQL major version:

# Stop postgres first — a live volume copy while writes are in-flight is not
# a consistent snapshot.
docker compose stop postgres

docker run --rm \
  -v raise-commons-dev-pgdata:/volume \
  -v "$(pwd)":/backup \
  alpine tar czf /backup/pgdata-$(date +%Y%m%d-%H%M%S).tar.gz -C /volume .

docker compose start postgres

Restore by extracting the tarball back into a fresh volume before starting postgres:

docker compose stop postgres
docker volume rm raise-commons-dev-pgdata   # destructive — confirm first
docker volume create raise-commons-dev-pgdata
docker run --rm \
  -v raise-commons-dev-pgdata:/volume \
  -v "$(pwd)":/backup \
  alpine tar xzf /backup/pgdata-20260827-120000.tar.gz -C /volume
docker compose start postgres

There is no built-in scheduler in this Compose stack. Wire the logical dump command into your own cron / systemd timer / CI job, e.g.:

# /etc/cron.d/raise-backup — daily at 02:00
0 2 * * * root cd /path/to/raise-commons && docker compose exec -T postgres pg_dump -U rai -d rai --format=custom > /var/backups/raise/raise-$(date +\%Y\%m\%d).dump 2>> /var/log/raise-backup.log

Retain backups off-host (object storage, another server) — a backup that lives on the same disk as the database it protects does not protect against disk failure.