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

58 lines
2.8 KiB
Markdown

# 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.