Cross-Region Replicas
Cross-region replicas provide a read-only warm copy in another geography. They are almost always async; accept replication lag as your cross-region RPO unless you invest in specialized sync architectures.
Recipe
us-east primary, eu-west async standby for DR read and promotion candidate.
# eu-west standby primary_conninfo targets us-east primary
# Use TLS and dedicated replication network path-- On primary: monitor cross-region standby
SELECT application_name, client_addr,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS bytes_behind,
replay_lag
FROM pg_stat_replication
WHERE client_addr << '10.1.%'; -- eu-west CIDR exampleWhen to reach for this: Regulatory residency, regional read scaling, or DR promotion when primary region is lost.
Working Example
Deploy cross-region physical replica with slot and lag SLO:
# eu-west: base backup from us-east primary
pg_basebackup -h primary.us-east.db.internal -U replicator \
-D /var/lib/postgresql/18/main -Fp -Xs -P -R \
-C -S eu_west_dr_slot-- primary_conninfo on eu-west includes sslmode=verify-full
-- Lag alert query (run on primary)
SELECT application_name,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS dr_lag_bytes
FROM pg_stat_replication
WHERE application_name = 'eu_west_dr';# Optional: eu-west serves read-only reporting via HAProxy replica frontend
# Writes still route to us-east only until DR declarationWhat this demonstrates:
- Cross-region link adds WAN RTT to replay lag.
- Physical slot on primary retains WAL for DR replica during outages.
- DR replica is read-only until promotion runbook executes.
Deep Dive
Lag Budget
| Link | Typical RTT | Extra replay lag |
|---|---|---|
| Cross-AZ | < 2 ms | Minimal |
| Cross-region same continent | 20-80 ms | Seconds under load |
| Intercontinental | 100-250 ms | Seconds to minutes |
Set RPO tier 1 cross-region to measured p99 lag plus archive fallback.
Promotion Runbook Outline
- Declare disaster (see DR Runbook & Comms)
- Stop writes to primary region (if partially alive)
- Verify DR replica lag acceptable or accept data loss window
pg_ctl promoteon DR or Patroni failover in DR scope- Repoint DNS / HAProxy / PgBouncer to DR region
- Rebuild replication back to primary region when recovered
Cascading Cross-Region
Hub in us-east feeds multiple regions (see Cascading Replicas) to reduce primary fan-out.
Gotchas
- Treating DR replica as HA - WAN partition causes large lag; not sub-minute failover. Fix: Same-region Patroni for HA; cross-region for DR.
- Sync replication cross-region - Commit latency unacceptable for most apps. Fix: Async with documented RPO.
- No slot on DR replica - Region brief network blip loses WAL forever. Fix: Permanent
eu_west_dr_slot. - Reads on stale DR replica - Users see old data. Fix: Route only analytics to DR; document lag in UI if needed.
- Promotion without etcd in DR - Patroni state confused. Fix: Separate DCS scope per region or manual promote runbook.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Logical replication to DR | Major version skew, subset | Full cluster byte clone |
| Backup restore in DR only | Cost-sensitive tier 2 | Tier 0 warm standby needed |
| Aurora Global Database | AWS-native global | Self-hosted Patroni requirement |
FAQs
How much DR lag is OK?
Whatever tier RPO says. Measure p99 replay_lag and bytes behind during peak load.
Can DR replica take writes?
Only after promotion. Until then reads only (or none if not ready to serve stale data).
Patroni multi-region?
Common pattern: independent Patroni cluster per region; cross-region replica outside Patroni or as non-failover member.
Data residency?
DR region must satisfy legal jurisdiction; replication copies primary data across border.
Cost control?
Smaller instance class on DR; scale up during game day or incident. Storage must fit full dataset.
Related
- DR Basics - RPO/RTO tiers
- Streaming Replication Basics - standby setup
- Cascading Replicas - hub-spoke
- Cloud PITR - managed cross-region backup
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+.