Files
roboco/docs/backend/ops/database-backups.md
T
f7f411e112 fix(infra): release builds all 17 registry images; pg_dump backup sidecar (#461)
* fix(infra): release builds all 17 registry images; pg_dump backup sidecar

release.yml was missing roboco-agent-grok-prompter and
roboco-agent-grok-secretary (both FROM the bare local roboco-agent-grok
tag, so agent-grok now builds explicitly ahead of the loop, mirroring
the agent-base special case) — a fresh registry pull could never
succeed. Both compose files gain a backup sidecar on the data network:
pg_dump -Fc on start and every 24h, crash-safe tmp+rename, newest-14
rotation, restore walkthrough in docs/backend/ops/database-backups.md.

* chore(docs): reflow hard-wrapped prose inherited from the six-PR merge train

* chore(foundation): regenerate lifecycle artifacts; reflow inherited prose

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-11 09:20:57 +02:00

2.6 KiB

Database Backups

An interim backup sidecar service ships in both docker-compose.yml and docker-compose.registry.yml: a plain pgvector/pgvector:pg16 container (the same image the postgres service pins) running docker/scripts/backup-entrypoint.sh on the data network alongside postgres, with no feature flag — it is always on, since a default-off backup is pointless.

What it does

On container start, and then every BACKUP_INTERVAL_SECONDS (default 86400, i.e. 24h), the script runs pg_dump -Fc against the roboco database over the network (via PGHOST=roboco-postgres, the same container-name resolution every other data-network consumer uses) and writes a timestamped custom-format dump to ${ROBOCO_DATA_DIR:-./data}/backups/roboco-<UTC-timestamp>.dump on the host. The dump is written to a .tmp suffix first and only renamed into place on success, so a crash mid-dump never leaves a half-written file that looks complete.

Retention

After each attempt the script prunes ${ROBOCO_DATA_DIR:-./data}/backups down to the newest BACKUP_KEEP dumps (default 14, i.e. roughly two weeks at the default 24h cadence) by mtime, deleting the rest. There is no offsite copy and no WAL/PITR archiving — this is a point-in-time pg_dump snapshot only, taken once a day.

Failure behavior

A failed pg_dump (network hiccup, postgres briefly unhealthy, disk full) logs to docker logs roboco-backup and is retried on the next cycle; the loop itself never exits, so the container never crash-loops on a transient failure. restart: unless-stopped covers the rest.

Restoring a dump

Stop anything writing to the database, then restore into a running (empty or throwaway) roboco database with pg_restore, for example: docker exec -i roboco-postgres pg_restore -U roboco -d roboco --clean --if-exists < ./data/backups/roboco-20260711T030000Z.dump (drop the .tmp files if any are present — they are in-progress dumps, not backups). Use pg_restore -l <dump> first if you want to inspect or selectively restore a subset of objects rather than the whole database. For a fresh empty database instead of --clean, create it first (createdb -U roboco roboco_restore) and restore into that.

Known ceiling

This is an interim measure to close the "zero backups" gap, not a full disaster-recovery story: a single daily snapshot on the same host as the database it's backing up is vulnerable to whole-host loss (disk failure, NAS failure). Copying ${ROBOCO_DATA_DIR:-./data}/backups offsite periodically, or moving to WAL-based continuous archiving, is the natural next step if that risk matters more than the current simplicity.