DR Basics
Disaster recovery (DR) prepares for region-wide or total-site failure. Define RPO (how much data you may lose) and RTO (how long until service returns) per application tier, then design backups and replicas to match.
Recipe
Example tier matrix for PostgreSQL 18.4 workloads.
| Tier | RPO | RTO | Pattern |
|---|---|---|---|
| Tier 0 (payments) | < 1 min | < 1 hr | Sync or async replica + off-site WAL |
| Tier 1 (core app) | < 15 min | < 4 hr | Cross-region async replica + PITR |
| Tier 2 (analytics) | < 24 hr | < 24 hr | Daily logical dump to DR region |
-- Document accepted loss window (internal metadata table)
INSERT INTO dr_tier_registry (service, tier, rpo_minutes, rto_minutes, approved_by)
VALUES ('checkout-db', 0, 1, 60, 'cto@example.com');When to reach for this: Before signing customer SLAs or SOC2 controls that mention business continuity.
Working Example
Translate business requirements to technical controls:
# DR region: verify last WAL archive arrived
aws s3 ls s3://prod-pg-wal-dr/ --recursive | tail -5
# Cross-region replica lag (async physical)
psql -h dr-replica.eu-west.db.internal -c "
SELECT now() - pg_last_xact_replay_timestamp() AS replay_age;"-- Stakeholder-facing summary query (runbook attachment)
SELECT tier, rpo_minutes, rto_minutes,
rpo_minutes || ' min data loss max' AS rpo_plain,
rto_minutes || ' min downtime max' AS rto_plain
FROM dr_tier_registry
ORDER BY tier;What this demonstrates:
- RPO/RTO are business numbers backed by replication lag and archive freshness.
- DR region resources must exist before disaster, not provisioned during incident.
- Registry table forces explicit sign-off on data loss windows.
Deep Dive
RPO vs RTO
| Term | Question answered | PostgreSQL levers |
|---|---|---|
| RPO | How much data can we lose? | Sync rep, archive frequency, backup age |
| RTO | How long until users are served? | Restore automation, DNS cutover, app scale |
| MRT | Mean recovery time (historical) | Drill measurements |
HA vs DR
| Scope | HA | DR |
|---|---|---|
| Failure | Node, AZ, primary | Region, provider, ransomware |
| Tooling | Patroni 3.x, PgBouncer | Cross-region replica, off-site backup |
| RTO | Seconds to minutes | Hours |
| Test frequency | Quarterly failover | Annual or semi-annual game day |
Accepted Data Loss
Document plain language: "Tier 1 accepts up to 15 minutes of committed transactions may be unrecoverable if primary and local backups are lost simultaneously."
Gotchas
- Same-region replica as DR - Region loss kills both. Fix: Cross-region WAL and replica.
- RPO on paper only - No measurement of archive lag or replay delay. Fix: Dashboards with SLO-aligned alerts.
- RTO ignores DNS and app - DB restored in 30 min; apps down 6 hr. Fix: End-to-end game day.
- One tier for all databases - Overpay or under-protect. Fix: Per-service tier in registry.
- DR never tested - Cold backups corrupt. Fix: DR Game Days on calendar.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Active-active multi-region | Extreme RTO | Standard B2B SaaS |
| Backup-only DR (no warm replica) | Tier 2 analytics | Tier 0 payments |
| Managed global database | Aurora Global, etc. | Strict data residency |
FAQs
Who approves RPO?
Product + legal + engineering leadership. DBAs implement; they do not unilaterally accept data loss.
Is zero RPO possible?
Sync replication within region approaches zero committed loss; cross-region zero RPO needs specialized architectures and latency cost.
DR for Patroni cluster?
Separate Patroni scope in DR region or restore from pgBackRest to new cluster; document cutover.
Ransomware and DR?
Immutable off-site backups and isolated restore environment; replication alone replicates encrypted payloads.
How relate to uptime SLO?
HA protects monthly error budget; DR protects existential region failures. See Uptime SLOs for Datastores.
Related
- Cross-Region Replicas - async DR sites
- DR Runbook & Comms - incident declaration
- HA Basics - HA vs DR boundary
- PITR Basics - rewind precision
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.