ADR-0032 Postgres backup & restore
Company standard for database backups — defined per deployment, never by the app, with a written record and a tested restore before go-live.
- Decision
- Defined per deploymentStore & protect
- Status
- Accepted
- Date
- Scope
- Company-wide default for all new apps
Connections
Context
Section titled “Context”We run Postgres ourselves (ADR-0007), and that ADR lists backups and restore drills as ours to own. It does not say how.
- Deployments differ: our own hosts under Nodus (ADR-0016), a client’s infrastructure, or a managed Postgres. Each has its own tools
- Recovery targets are per product: how much data may be lost and how long a restore may take are agreed with each product owner, not company-wide
- The app holds no durable state of its own: it lives in Postgres and in object storage (ADR-0030)
What is open: whether one backup tool is the company standard, and what every deployment owes regardless.
Options considered
Section titled “Options considered”1. One company tool and schedule everywhere
Section titled “1. One company tool and schedule everywhere”Pros: one runbook; restore skills carry between products. Cons: does not fit a managed Postgres or a client’s infrastructure; one recovery target is too strict for some products and too weak for others.
2. Backups run by the app (pg_dump from a scheduled job)
Section titled “2. Backups run by the app (pg_dump from a scheduled job)”Pros: travels with the image.
Cons: the app would hold dump credentials and backup-storage keys; logical dumps only, no point-in-time recovery; the image would need a pg_dump matching the server version.
3. Per deployment, with a required record
Section titled “3. Per deployment, with a required record”Pros: each environment uses the tool that fits it, including what the platform already provides. Cons: no uniformity — and the risk that nobody defines it, unless the record is required.
Decision
Section titled “Decision”Backup and restore are defined per deployment. There is no company-wide tool; there is a company-wide minimum.
Concretely:
- Never the app’s job. No
pg_dumpin the app or its worker (ADR-0028), and no backup credentials in the app’s environment - Every production deployment records, before go-live: the tool and method (physical backups with WAL for point-in-time recovery, or logical dumps), the schedule, the retention, where the copies live (not on the database host), how they are encrypted, the agreed recovery targets (data-loss window and restore time), and who is alerted when a backup fails
- A backup counts once a restore from it has been tested. One restore drill before go-live, then on the schedule the deployment sets; the record holds the date of the last one
- The same record covers the other stores: object storage (ADR-0030) and, where queued jobs must survive losing the host, Valkey (ADR-0028)
The deciding factors: deployments are too different for one tool, recovery targets are a product decision, and backup credentials do not belong in an app. We accept that two products may back up differently.
Consequences
Section titled “Consequences”Easier:
- Each deployment uses what its platform offers instead of working around a mandated tool
- The app image stays free of database tooling and backup secrets
Harder:
- Nothing is uniform: restoring product A teaches little about product B
- The record is the only guard — a deployment without one has no backup we can count on
- Restore drills take time and must be scheduled, or they do not happen
Given up: one runbook for every database. Accepted — a backup that fits its deployment and has been restored beats a standard that does not.

