Architecture Decision Record

ADR 012: A Layered Backup Strategy

Four independent recovery layers with different failure domains — the decision record for "what changed after losing data."

Status: Accepted  ·  Date: Built up over 2025–2026  ·  ← All ADRs


Context

This platform has lost data once. The redesign principle that followed: no single backup mechanism, because any one mechanism shares a failure domain with something — the cluster, the storage, the tool itself. Instead, layers that fail differently.

Decision

Four independent layers, plus monitoring of the backups themselves:

Layer Tool What it protects Cadence
VM images Proxmox Backup Server Entire node/VM state, below Kubernetes Scheduled at the Proxmox layer
Cluster state Velero → MinIO, CSI snapshots via Synology All namespaces’ Kubernetes objects + volume snapshots Daily, 7-day retention
Database PITR CloudNativePG WAL archiving (Barman) → MinIO Continuous Postgres WAL + base backups, point-in-time recovery Continuous; 14-day retention
Logical dumps Nightly pg_dump CronJob → MinIO Per-database portable dumps of the five critical apps Nightly, 14-day prune

The monitoring layer: a CronJob runs 30 minutes after the Velero window, queries the Kubernetes API for the latest backup, and pushes an alert to ntfy if it’s missing, failed, or older than 26 hours. A backup that isn’t checked is a hope, not a backup.

Reasoning

Tradeoffs

Outcome

Honest status on drills: I’ve exercised every layer, but only in pieces, and mostly under duress. I’ve restored a LUN snapshot to get a volume back, restored Postgres from backup, and partially restored Velero manifest backups — each in response to a real problem, never as a single coordinated end-to-end rehearsal. The rebuild-from-scratch events that shaped this platform (Lessons Learned) recovered from Git + backups rather than from memory, and the daily alert has caught real backup failures before they became discoveries.

The gap I’ll name plainly: a full, coordinated restore drill — fresh cluster, every layer, in order — is planned and hasn’t happened. The pieces have all worked; the whole has never been rehearsed in one sitting. That’s the honest difference between “I have backups” and “I have a tested recovery.”