docs(ops): restore runbook, off-host backups, and media backup (#80) #172

Merged
claude-bot merged 1 commit from feat/80-restore-runbook into main 2026-07-16 01:42:04 +00:00
Contributor

Summary

Scheduled DB backups worked, but the story stopped there: no documented restore procedure, the backups volume sat on the same disk as postgres_data, and the media volume (uploaded images — permanent user data) was never backed up. An operator facing a dead host would have to improvise pg_restore flags and service ordering — and discover all media gone.

Changes (docs/OPERATIONS.md, Backups section)

  • Disaster-recovery restore runbook — end to end: stop writers → pg_restore --clean --if-exists --no-owner of the custom-format dump via a throwaway backend container (which has both pg_restore and the backups volume) against the running db, incl. the fresh-host path (bring up db only so init.sh creates roles first) → start ordering (the one-shot migrate runs first) → verification (alembic current == heads, app boots, spot-check query).
  • Off-host copies — pull-based rsync/rclone cron on a second host (credentials stay off the app host), with the docker volume inspect mountpoint hint.
  • media volume backup + restore — dependency-free tar via a throwaway container, shipped off-host with the DB dumps.
  • Exclusions — explicit statement that audio_temp is transient scratch, deliberately not backed up.

Approach

Implemented as documented operator procedures — the issue explicitly permits documenting the off-host pattern and a separate volume-backup step. No application code changes, so nothing here can regress the running system.

Verification

Markdown structure checked (balanced headings + fenced code blocks). Docs-only — no CI job exercises OPERATIONS.md content.

Closes #80

🤖 Generated with Claude Code

## Summary Scheduled DB backups worked, but the story stopped there: **no documented restore procedure**, the `backups` volume sat on the **same disk** as `postgres_data`, and the `media` volume (uploaded images — permanent user data) was **never backed up**. An operator facing a dead host would have to improvise `pg_restore` flags and service ordering — and discover all media gone. ## Changes (`docs/OPERATIONS.md`, Backups section) - **Disaster-recovery restore runbook** — end to end: stop writers → `pg_restore --clean --if-exists --no-owner` of the custom-format dump via a throwaway backend container (which has both `pg_restore` and the `backups` volume) against the running `db`, incl. the fresh-host path (bring up `db` only so `init.sh` creates roles first) → start ordering (the one-shot `migrate` runs first) → **verification** (`alembic current` == `heads`, app boots, spot-check query). - **Off-host copies** — pull-based `rsync`/`rclone` cron on a second host (credentials stay off the app host), with the `docker volume inspect` mountpoint hint. - **`media` volume backup + restore** — dependency-free `tar` via a throwaway container, shipped off-host with the DB dumps. - **Exclusions** — explicit statement that `audio_temp` is transient scratch, deliberately not backed up. ## Approach Implemented as documented operator procedures — the issue explicitly permits *documenting* the off-host pattern and a separate volume-backup step. **No application code changes**, so nothing here can regress the running system. ## Verification Markdown structure checked (balanced headings + fenced code blocks). Docs-only — no CI job exercises OPERATIONS.md content. Closes #80 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs(ops): add restore runbook, off-host backups, and media backup (#80)
All checks were successful
CI / Frontend tests, audit, and build (pull_request) Successful in 1m28s
CI / Bot tests and audit (pull_request) Successful in 2m19s
CI / Backend lint (ruff) (pull_request) Successful in 2m28s
CI / Backend migration, tests, and audit (pull_request) Successful in 4m7s
CI / Docker image build (pull_request) Successful in 5m26s
abb3bfb424
Scheduled DB backups existed but the story stopped there: no documented
restore procedure, backups sat on the same disk as postgres_data, and the
media volume (uploaded images — permanent user data) was never backed up.

Extend docs/OPERATIONS.md's Backups section with:
- A full disaster-recovery restore runbook: stop writers, pg_restore the
  custom-format dump via a throwaway backend container (has pg_restore + the
  backups volume) against the running db, start ordering, and post-restore
  verification (alembic current==heads, app boots, spot-check query).
- An off-host copy pattern (pull-based rsync/rclone cron on a second host).
- A dependency-free media-volume backup + restore (tar via a throwaway
  container), to be shipped off-host with the DB dumps.
- An explicit statement that audio_temp is transient scratch, deliberately
  excluded from backups.

Implemented as documented operator procedures (the issue explicitly allows
documenting the off-host and media-volume steps); no application code
changes.

Closes #80

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/80-restore-runbook 2026-07-16 01:42:05 +00:00
Sign in to join this conversation.
No description provided.