# 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//`). 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-/`, 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.