Skip to content

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

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.

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.

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.

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_dump in 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.

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.