Backup and restore
The production overlay runs two backup sidecars automatically.
| Sidecar | What it backs up | Destination | Default retention |
|---|---|---|---|
postgres-backup | The projectbrain database with pg_dump | ./backups/postgres/ | 7 daily, 4 weekly, 6 monthly |
workspace-backup | Graphify workspace volume | ./backups/workspace/ | 14 days |
No host cron job is needed for local backups. The sidecars start with:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Backup locations
./backups/
+-- postgres/
| +-- daily/
| +-- weekly/
| +-- monthly/
| +-- projectbrain-pre-upgrade-*.sql.gz
+-- workspace/
+-- workspace-2026-04-29T03-00-00.tar.gz
The ./backups/ directory is mode 0700 and owned by the user running Compose.
Database dumps are gzipped and are usually much smaller than the live database.
Copy backups off host
Local backups are not enough. Sync ./backups/ to durable storage on a schedule.
Choose one option:
# AWS S3
aws s3 sync ./backups/ s3://acme-projectbrain-backups/$(hostname)/
# Google Cloud Storage
gsutil -m rsync -r ./backups/ gs://acme-projectbrain-backups/$(hostname)/
# Azure Blob
az storage blob sync --account-name acmeprojectbrain --container backups --source ./backups/
# rclone: S3, B2, R2, OneDrive, and others
rclone sync ./backups/ remote:projectbrain-backups
Run the sync after the local backup, for example at 03:30:
30 3 * * *
Restore the database
Use scripts/restore.sh for database restores. It stops app services, rebuilds the database, restores the dump, runs migrations defensively, and starts the app again.
./scripts/restore.sh ./backups/postgres/daily/projectbrain-2026-04-29.sql.gz
The script asks you to type restore before it changes data.
For automation:
RESTORE_ASSUME_YES=true ./scripts/restore.sh ./backups/postgres/daily/projectbrain-2026-04-29.sql.gz
Restores are in-place and destructive. The current database is dropped and recreated.
Expected downtime is 1-5 minutes for small instances and longer for large dumps.
Restore the workspace volume
The workspace contains shallow Git clones and graphify artifacts. Losing it does not lose product data, but the next graph rebuild starts cold. The worker scrubs the authenticated GitHub remote before each graphify run and verifies the scrub by re-reading origin; a run that cannot verify the scrub aborts before any data leaves the worker, so a snapshot of an idle workspace volume should not contain live GitHub installation tokens. (A snapshot taken mid-build can briefly include one, and installation tokens expire one hour after issuance regardless.)
Restore it only if you want to avoid the cold-start cost.
docker compose -f docker-compose.yml -f docker-compose.prod.yml stop worker graphify-sidecar graphify-egress-proxy
docker run --rm \
-v project-brain_graphify_workspace:/workspace \
-v "$PWD/backups/workspace:/archive:ro" \
alpine sh -c 'cd /workspace && tar -xzf /archive/workspace-<timestamp>.tar.gz --strip-components=1'
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d worker graphify-sidecar graphify-egress-proxy
Replace:
<timestamp>with the snapshot timestampproject-brain_graphify_workspacewith the volume name fromdocker volume ls
Run a restore drill
A backup is only useful after you have proven it restores.
Run this once before launch, then quarterly:
- Create a clean staging VM.
- Run
./scripts/install.shwith throwaway secrets. - Copy a recent production backup to staging.
- Run
./scripts/restore.sh <path>. - Sign in and verify expected data exists.
- Destroy the staging VM.
If the drill fails, fix the restore path before relying on the backup.
Disable backup sidecars
If your platform already handles backups, such as Velero, pgBackRest, or managed Postgres backups, remove the sidecars from the production overlay or override them with a third Compose file.
scripts/upgrade.sh still creates a pre-upgrade database snapshot even when the sidecars are disabled.
Not backed up
| Data | Reason |
|---|---|
| Redis | BullMQ queues can be rebuilt. In-flight jobs may re-run. |
| Caddy data volume | Caddy can re-issue certificates on startup. |
| Customer Git repositories | Source code remains in the customer GitHub organization. Project Brain stores pointers and derived context. |