dorfteich/docs/architecture/adr/0015-backup-strategy.md
Claude Fable 5 0629411966 Add architecture documentation, ADRs, and operations concept
Initial deliverable of the architecture phase: 16 ADRs (stack, CRDT
collaboration, plugin sandbox, import/export, backups, CI/CD), data
model, permission model, real-time collaboration and plugin concepts,
deployment/operations/security documentation, and the milestone roadmap
that the implementation issues are derived from.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:36:16 +02:00

2.8 KiB

ADR 0015: Nightly pg_dump + uploads sync, 30-day retention, off-host mirror

  • Status: accepted
  • Date: 2026-07-04

Context

The operator's standard for database-driven apps applies (global convention): nightly dump via sidecar, ~30 days local retention, manual download/restore via admin UI, mirroring of nightly dumps to the BASEL host over WireGuard (/home/RAID/BACKUPS/<app>/). Dorfteich adds a second persistence root: the uploads/plugins volume (ADR 0011).

Decision

  • What is backed up (together forming a consistent restore set):
    1. PostgreSQL: nightly pg_dump -Fc from a backup sidecar container.
    2. Uploads + plugins volume: nightly tar archive (or rsync snapshot) taken after the dump, referencing the same backup id.
  • Retention: 30 days locally (both artifact types), pruned by the sidecar.
  • Mirroring: nightly rsync of the local backup directory to BASEL, target /home/RAID/BACKUPS/dorfteich-<stage>/, same retention. A dedicated dorfteich-backup user with home under /home/ is created on BASEL (per the operator's "cleaner alternative" note — not reusing the Debian backup system user, avoiding its UID-34/home-path pitfalls).
  • Scope per stage: Prod is fully backed up + mirrored. Test/Int get the nightly dump with 7-day local retention and no off-host mirror (reproducible from Prod data or fixtures). This is the deliberate, ADR-documented deviation from the standard pattern for non-Prod stages.
  • Restore paths:
    • Documented CLI runbook (operations.md): stop stack → restore dump via pg_restore → restore volume archive → start stack. Practiced against Test in a recurring story ("restore drill").
    • Admin UI: Site Admin can download the latest dumps and trigger an on-demand backup. Restore stays a CLI operation (a web-triggered restore of the database that serves the web UI is a foot-gun).
  • Integrity/alerting: the sidecar writes a status file consumed by the health endpoint; a failed or missing nightly backup raises an alert (see operations.md). Dump restorability is verified monthly by an automated restore into a scratch database on Test.

Consequences

  • Consistency between database and file volume is "nightly point-in-time, files may be minutes newer" — acceptable: a restored page referencing a file uploaded after the dump shows a missing image, never corruption.
  • Moving Prod to a dedicated host keeps the identical sidecar; only the WireGuard/rsync route changes (ADR 0014 consequence applies).

Alternatives considered

  • WAL archiving / PITR (e.g. pgBackRest): better RPO, but operationally heavier than the standard pattern warrants at this scale; revisit if Dorfteich.online grows.
  • Volume backup via S3-compatible offsite: no existing infrastructure; BASEL mirror is the established pattern.