Skip to content

Backup & restore

  • Logical: scripts/backup.sh [PG_URL] [OUT_DIR] → timestamped pg_dump custom-format file. Schedule daily (cron/CI) and ship off-host (object storage) with retention.
  • Managed Postgres: also enable the provider’s automated backups + point-in-time recovery. Keep both; logical dumps are portable, PITR is finer-grained.

Restore drill (run regularly — proves the backup, not just that it exists)

Section titled “Restore drill (run regularly — proves the backup, not just that it exists)”
Terminal window
make restore-drill # restores the latest ./backups dump into riser_restore_test
# or explicitly:
bash scripts/restore.sh <dump-file> riser_restore_test

The drill drops + recreates the target DB and pg_restores into it. Verify after: table count, seeded plans, and a known row are present (this was exercised in Phase 10 — 22 tables + data restored cleanly). Drop the scratch DB when done.

  1. Stop writes to the affected DB (scale backend to 0 / maintenance mode).
  2. Restore the latest good dump into a new database; do not overwrite the live one until validated.
  3. Validate: row counts, latest telemetry timestamps, a login + dashboard load.
  4. Cut over DATABASE_URL to the restored DB; redeploy backend; verify GET /health.
  5. Note the data-loss window (since the last good backup) and communicate it.
  • RPO: ≤ 24h via daily logical dumps; tighter with managed PITR.
  • RTO: restore + cutover, target < 1h for a single-region failure.
  • Heating during DR: unaffected — controllers run locally and buffer telemetry; the platform DR is about visibility/history/ops, never about keeping buildings warm.