Skip to content

    System Design

    System Design: Multi-Region and Disaster Recovery

    Learn how to design for the loss of a whole region: RPO and RTO, active-passive versus active-active, global traffic routing, conflict resolution and failover testing.

    By Published 15 min read

    Share

    This lesson is part of Advanced System Design and Case Studies. Enroll for free, then sign in, to read it in full.

    What you'll learn: By the end of this lesson you will be able to set recovery targets (RPO and RTO) from business needs, compare active-passive and active-active designs and their costs, explain how traffic is steered between regions, and choose a conflict-resolution strategy when two regions accept writes.

    Picture a bank with one head office and a second office across the country. If a flood closes the head office, the question is not whether the bank has a second building. It is how much paperwork was lost, how long customers wait before the second office opens, and whether anybody has ever practised the move. A second building that nobody has tested is a hope, not a plan.

    Cloud regions are those buildings. A region is a cluster of data centres in one geographic area, and regions do fail, rarely but seriously: power or networking problems, a faulty change to the provider's control plane, a misconfiguration of your own, or a natural event. Zones inside one region protect you from a single data-centre fault. Only a second region protects you from losing the whole region.

    This lesson is about the design decisions that come with that protection. It builds on replication and CAP and consistency, and reuses ideas from resilience patterns.

    The core idea

    Multi-region design is a trade between three things you cannot all have at the maximum: low data loss, fast recovery and low cost (and complexity). Two numbers make the trade explicit.

    • RPO (Recovery Point Objective): how much recent data you can afford to lose, measured as time. An RPO of 5 minutes means you may lose up to the last 5 minutes of writes.
    • RTO (Recovery Time Objective): how long you can be down. An RTO of 30 minutes means service must be back within 30 minutes of the disaster.

    Neither number is a technical decision. They come from the business: what does an hour of downtime cost, and what does a lost transaction cost? Different parts of one system deserve different targets. A payment ledger may need an RPO near zero; a recommendation cache can have an RPO of "rebuild it".

    flowchart LR L["Last good copy of data"] -->|"RPO window of lost data"| D["Disaster strikes"] D -->|"RTO window of downtime"| R["Service restored"]

    Lower numbers cost more. Replicating every write to another region synchronously gives RPO near zero but adds a cross-region round trip to every write. Keeping a fully running second region gives a small RTO but doubles infrastructure. The art is spending exactly as much as the business justifies.

    Deployment models

    Backup and restore. Take backups, copy them to another region, and rebuild from scratch in a disaster. Cheap and slow: recovery means provisioning infrastructure, restoring data and re-pointing traffic, often hours. RPO equals the time since the last backup copy.

    Pilot light and warm standby. A pilot light keeps data replicated and core infrastructure defined, but compute is off or minimal; on disaster you scale up. A warm standby keeps a smaller running copy of the whole stack. Cost is moderate and RTO is minutes to tens of minutes.

    Active-passive (hot standby). One region serves all traffic. A second holds a full, up-to-date copy and is ready to take over but serves nothing in normal operation. Failover means promoting the standby database and moving traffic.

    Active-active. Both (or all) regions serve live traffic. Users go to the nearest healthy region, giving the best latency and fastest recovery, since the other region is already serving. It demands that both regions can accept writes safely, which is the hard part.

    Model Typical RTO Typical RPO Relative cost Main difficulty
    Backup and restore Hours Since last backup Lowest Slow, restore is rarely rehearsed
    Pilot light / warm standby Minutes to an hour Replication lag Low to medium Scaling up under pressure
    Active-passive Minutes Replication lag (async) or near zero (sync) High Failover automation, standby drift
    Active-active Seconds to minutes Depends on write strategy Highest Write conflicts, data placement
    NOTE

    The RTO and RPO figures in the table are broad, illustrative ranges, not guarantees. Your real numbers come from measuring your own failover drill.

    flowchart TD S["Targets agreed with the business"] --> Q1{"RTO measured in hours?"} Q1 -->|yes| A1["Backup and restore"] Q1 -->|no| Q2{"RTO above 15 minutes and users in one geography?"} Q2 -->|yes| A2["Warm standby or active-passive"] Q2 -->|no| Q3{"Users on several continents needing local writes?"} Q3 -->|no| A3["Hot active-passive with automated promotion"] Q3 -->|yes| A4["Active-active with a write strategy"]

    Read it as a cost ladder: stop at the first rung that meets the targets. Most systems end at a warm or hot active-passive design; active-active is chosen for latency and recovery speed only when the team can afford the conflict handling.

    Cross-region replication

    Replication across regions is mostly asynchronous. A round trip between continents costs on the order of tens to a couple of hundred milliseconds (approximately), and waiting for that on every write would make an application feel slow, so most systems accept that the secondary lags the primary.

    That lag is your realistic RPO. If the primary region disappears with 3 seconds of unreplicated writes, those 3 seconds are gone unless recorded elsewhere. Teams therefore monitor replication lag as a first-class metric and alert when it approaches the RPO.

    Options for tightening the gap:

    • Synchronous replication to the other region: RPO near zero, but write latency includes the cross-region trip, and if the link breaks you must choose between blocking writes and dropping to asynchronous mode (which silently raises your RPO).
    • Quorum or consensus databases spanning regions (see consensus and leader election): a write succeeds when a majority acknowledges. With three regions, losing one still leaves a majority: no data loss and no manual promotion, at the cost of a cross-region acknowledgement per write.
    • Event logs replicated separately: replicate a stream of changes and rebuild state from it in the second region.

    Do not forget everything that is not the main database: object storage buckets, search indexes, queues, secrets, configuration, DNS records and container images. A region with a perfect database copy but no secrets or images cannot start.

    Steering traffic between regions

    Failover only works if clients actually move. Three mechanisms, often combined:

    GeoDNS and latency-based DNS. DNS answers with the closest healthy region; health checks remove a failed one. The weakness is caching: resolvers cache answers for the TTL, and some ignore low TTLs, so some users keep going to the dead region for minutes.

    Anycast. Several regions advertise the same IP address through BGP and the network delivers each packet to the nearest advertiser. If a region stops advertising, traffic reroutes without clients re-resolving DNS. Global load balancers and CDNs commonly use this.

    Application-level routing. A global front door, or the client app, retries against a secondary endpoint when the first fails. Precise control (for example "send this user to their home region") at the cost of client logic.

    Traffic steering between two regionsUser to Global traffic manager; Global traffic manager to Region A stack (nearest); Global traffic manager to Region B stack (failover); Region A stack to Database A; Region B stack to Database B; Database A to Database B (async replication)REGION AREGION BUserUserGlobal traffic managerGlobal trafficmanagerRegion A stack: nearest healthyRegion A stacknearest healthyDatabase ADatabase ARegion B stack: failover pathRegion B stackfailover pathDatabase BDatabase Bnearestfailoverasync replication

    Health checks need care. Check what matters: a region whose load balancer answers but which cannot reach its database is not healthy. Beware flapping: use hysteresis (several consecutive failures to mark down, several successes to mark up) and consider making full failover a deliberate, human-approved action where false positives are costly.

    The hard part: writes in two places

    In active-passive there is one writer, so no conflicts. In active-active two regions can accept writes to the same data simultaneously, and on replication they discover both changed it. Four honest options:

    1. Avoid conflicts by partitioning ownership. Give each record a home region; a user's data is written only there and replicated elsewhere for reads and failover. When the home region fails, ownership moves.
    2. Last write wins (LWW). Keep the version with the latest timestamp. Simple and always converges, but it silently discards one write and depends on clocks that skew (see IDs, clocks and ordering). Acceptable where losing one update is harmless, such as "last seen" markers.
    3. Merge with application logic or versions. Keep both versions, detect the conflict with version vectors, and let the application merge or ask the user. A shopping cart can merge by taking the union of items.
    4. CRDTs. A conflict-free replicated data type is designed so concurrent updates always merge automatically, in any order, to the same result. Examples: a grow-only counter (each region increments its own slot, the total is the sum) and sets with add and remove rules. CRDTs only fit data whose semantics you can express as a merge. They do not suit invariants such as "balance never goes below zero" or "only one person gets the last seat".

    For invariants, you need a single decider: route those operations to one region (or one consensus group) and accept the latency, keeping everything else local.

    flowchart TD W["Two regions may write the same data"] --> I{"Invariant such as balance or unique seat?"} I -->|yes| D1["Single decider for that operation"] I -->|no| H{"Natural home region per record?"} H -->|yes| D2["Home region ownership"] H -->|no| L{"Is losing one update harmless?"} L -->|yes| D3["Last write wins"] L -->|no| M{"Does a merge rule exist?"} M -->|yes| D4["CRDT or application merge"] M -->|no| D1
    Strategy Data loss on conflict Complexity Good for
    Home region ownership None (single writer per record) Medium: routing and ownership moves Per-user data, most apps
    Last write wins Yes, silently Low Preferences, presence, caches
    Application merge None if merge is correct High Carts, documents, profiles
    CRDTs No manual conflicts; the merge rule decides the outcome Medium to high Counters, sets, collaborative text
    Single global decider None Low logic, higher latency Money, inventory, uniqueness

    Backup tiers: the last line of defence

    Replication is not backup. If someone deletes a table or a bug corrupts data, replication copies the damage to every region within seconds. You also need point-in-time recovery and independent backups.

    A widely used guideline is the 3-2-1 rule: at least three copies, on two kinds of storage, with one offsite. In the cloud that means frequent same-region snapshots for fast restores, copies in a second region for disaster recovery, and long-term archives in cheaper tiers with immutability (write-once retention) so ransomware or a compromised account cannot erase them, stored under separate credentials from production. Tiers differ in how quickly you can use them: hot (replicas and recent snapshots, minutes), warm (daily backups in standard object storage, minutes to hours), cold or archive (long retention, hours, cheapest).

    WARNING

    A backup you have never restored is only a belief. Schedule regular restore tests, time them, and check that the restored data is actually usable by the application.

    Testing failover

    Real disasters are chaotic, so rehearse in layers: tabletop exercises (who decides to fail over, who talks to customers?), component drills (promote a replica in staging and time it), a full regional failover game day (move real traffic on a planned date and, importantly, fail back, which is often harder because the original region holds stale data), and continuous verification of replication lag, standby capacity and configuration drift.

    Measure against your targets: how long did detection take, how long did promotion take, how much data was lost, which steps were manual? Each manual step in a runbook is a risk at 3 a.m. Two hidden dependencies recur. Capacity: if region A serves 60 percent of traffic and B 40 percent, B must carry 100 percent or you plan degradation, such as shedding low-priority work as in resilience patterns. Shared control planes: if the failover tooling lives in the failing region, you cannot press the button.

    In practice: a SaaS with contractual RPO and RTO and a regional failover drill

    NOTE

    A realistic scenario, not a case history. This is a composite example built from common production patterns. It does not describe any specific company, and every number in it is an illustrative assumption.

    The situation

    A B2B project-management SaaS has about 400 business customers, mostly in North America and Europe, and a platform team of seven. Enterprise contracts promise an RPO of 5 minutes and an RTO of 60 minutes for a regional outage, with service credits if missed. The stack: Kubernetes in one cloud region, PostgreSQL with an asynchronous replica in a second region, S3-compatible object storage with cross-region replication, Redis for sessions, Kafka for events, Terraform for infrastructure, and Prometheus and Grafana for monitoring. The last failover drill took 3 hours 10 minutes, mostly manual steps; replica lag has a p99 of about 40 seconds but reached 11 minutes during a bulk customer import. The auditors want evidence that the contract is achievable. Budget allows roughly 40 percent extra infrastructure spend (an assumption) and no active-active rewrite this year.

    Starting architecture

    Starting architecture across region A and region BManaged DNS to Load balancer; Load balancer to App pods; App pods to Redis; App pods to PostgreSQL; App pods to Object storage; Nightly backup to Object storage; PostgreSQL to PostgreSQL (async, no automation); Object storage to Object storage (replicated)REGION AREGION BManaged DNS: health checksManaged DNShealth checksLoad balancer: entry pointLoad balancerentry pointApp pods: and workersApp podsand workersRedis: sessionsRedissessionsNightly backup: same accountNightly backupsame accountObject storageObject storagePostgreSQL: primaryPostgreSQLprimaryObject storage: replica bucketObject storagereplica bucketPostgreSQL: async replicaPostgreSQLasync replicaasync, no automationreplicated

    Options considered

    Option What it fixes What it costs Risk
    Do nothing beyond a better runbook Faster manual recovery Engineer time RTO still depends on people at 3 a.m.; lag and drift unmonitored
    Warm standby with scripted promotion, lag alerts and drift checks Targets the 60-minute RTO; lag becomes visible against the 5-minute RPO About 40 percent extra spend, one quarter of work Medium: promotion and fail-back must be rehearsed
    Active-active across both regions Near-zero RTO, local writes for Europe Highest spend and a rewrite of write paths and conflict handling High: conflicts on shared project data, longer timeline than the audit allows

    The decision

    Field Entry
    Context Contractual RPO 5 minutes and RTO 60 minutes; measured RTO 190 minutes; lag spikes to 11 minutes under bulk load.
    Decision Warm standby in region B: database replica, minimal app pods scaled by script, secrets and images replicated. Redis sessions are not replicated (users sign in again) and region B's Kafka starts empty, so consumers must be able to resume from database state. Promotion is a one-command script that a human approves; DNS TTL is 60 seconds. Bulk imports are throttled by lag. Backups move to a separate account with immutability.
    Consequences Good: a measurable path to the targets, no new write-conflict problem. Bad: the standby costs money while idle, promotion is still human-triggered, and failover loses up to the replication lag.
    Revisit when Contracts require an RTO under 15 minutes, European customers demand in-region writes, or lag alerts above 2 minutes happen more than once a month.

    Rollout and verification

    flowchart LR R1["Monitor lag and drift"] --> R2["Terraform parity for region B"] R2 --> R3["Scripted promotion in staging"] R3 --> R4["Production failover drill for one low-risk tenant group"] R4 --> R5["Full drill with fail-back"]

    Make lag and drift visible first: alert when replication lag exceeds 2 minutes (40 percent of the RPO), and run a daily check comparing required secrets, images and configuration between regions. Bring region B to Terraform parity, then script promotion and time it in staging. In production, drain a small group of tenants, promote, run real traffic there, and fail back. Success criteria written before the drill: measured RTO under 45 minutes, data loss equal to the lag recorded at the cut, error rate under 1 percent after the DNS TTL passes, zero manual steps outside the runbook. Dashboards: replication lag, standby capacity versus production load, drift check result, backup restore time.

    The 2 a.m. failure

    During the full drill, promotion completes in 12 minutes but the page reads "Region B readiness probes failing, 0 of 6 app pods ready". The cause is a payment-webhook signing secret that exists in region A only, which the app's startup readiness check requires; the secret was added after the replication job was configured, and the daily check had not yet covered that namespace.

    sequenceDiagram participant OC as On-call participant DNS as Managed DNS participant DB as Region B database participant K as Region B pods OC->>DB: promote replica, record lag at cut DB-->>OC: promoted OC->>K: scale app pods to full size K-->>OC: readiness failing OC->>K: read pod events and application logs K-->>OC: log says webhook signing key not found OC->>K: restore secret from break-glass vault K-->>OC: pods ready OC->>DNS: shift traffic to region B

    First three checks: kubectl get pods, then kubectl describe pod and kubectl logs for one unready pod in region B (the probe failure shows in events, the cause in the logs); the external-secrets sync status to see what did not replicate; and a diff of the secret inventory between regions. Mitigation: restore the missing secret from the break-glass vault, then shift traffic. Follow-up fix: make the secret inventory a required, automatically compared list in CI, extend the daily drift check to every namespace, and add "read events and logs of the first unready pod" as step one of the promotion runbook. Record the actual RTO from the drill, not the planned one.

    Architect's notes

    • Measure lag distribution, not the average. p99 and the worst case during batch work are your real RPO; throttle the workload that causes spikes.
    • Ask who can declare a disaster. A named role with authority and a decision time limit saves more minutes than any script.
    • The cost trap is idle capacity and cross-region traffic. Replication egress and a standby sized for full load are the recurring line items; state them before the drill, not after the invoice.
    • Fail-back is a project. Plan the data reconciliation and a second drill; a one-way failover leaves you running without a standby.
    • Keep the control plane outside the blast radius. Runbooks, pipelines, secrets and the DNS account must be reachable when region A is not.
    • Defer active-active. Take it only when a contract or latency target forces it, and pair it with an explicit per-entity conflict policy.

    Common mistakes

    • Treating zones as regions. Multiple availability zones do not protect against a regional failure. Decide explicitly which failures you design for.
    • Choosing RPO and RTO without the business. Targets set by engineers alone are too expensive or too weak. Agree them per system.
    • Assuming replication is backup. Replication copies deletes and corruption. Keep point-in-time recovery and immutable backups.
    • Active-active without a conflict strategy. Concurrent writes will collide. Decide ownership or merge rules up front.
    • Never testing failover or restore. Untested plans fail on details such as expired credentials or missing images.
    • Forgetting non-database state. Secrets, queues, object storage, search indexes and DNS must exist in the second region too.
    • Underprovisioning the standby. Size it, or plan explicit degradation.

    Key takeaways

    • RPO is data you can lose, RTO is time you can be down. Both come from business impact, per system.
    • Regions, not zones, protect against regional loss. Pick the failure you are designing for.
    • Cost rises as targets fall. Backup and restore, warm standby, active-passive and active-active form a ladder.
    • Asynchronous replication means a non-zero RPO. Monitor lag and treat it as your real recovery point.
    • DNS failover is slowed by caching; anycast and global load balancers react faster. Combine mechanisms and use sensible health checks.
    • Active-active needs a write strategy. Home regions, merges, CRDTs or a single decider; last write wins loses data silently.
    • Replication is not backup. Keep point-in-time recovery, the 3-2-1 idea and immutable copies.
    • Test, including fail-back. Drills turn a document into a capability.

    Practice exercises

    Exercise 1 — Starter (30 min): A service has an RPO of 1 minute and an RTO of 15 minutes. For each deployment model in this lesson (backup and restore, warm standby, active-passive, active-active), say whether it can plausibly meet both targets and why. State one assumption you made about replication lag or startup time for each.

    Exercise 2 — Intermediate (2 hours): Design a two-region deployment for a note-taking app with 5 million users in Europe and North America. Draw the architecture including database, object storage, queue and DNS. Choose active-passive or active-active and justify it. Decide how conflicts are avoided or resolved when a user edits the same note from two devices in two regions, and write the failover steps as a runbook of at most ten numbered steps.

    Exercise 3 — Advanced (half day): Plan a regional failover game day for a system you know or an invented e-commerce platform. Define success criteria (measured RTO, RPO, error rate), list all state that must exist in the second region, specify the traffic-shift procedure and how health-check flapping is prevented, prepare the fail-back plan including data reconciliation, and write a review template for what you would measure and what actions you would expect to come out of it.

    Enroll to keep reading

    Lesson 7 of 14 · Advanced System Design and Case Studies

    Learn how to design for the loss of a whole region: RPO and RTO, active-passive versus active-active, global traffic routing, conflict resolution and failover testing.

    In the rest of this lesson

    • Cross-region replication
    • Steering traffic between regions
    • The hard part: writes in two places
    • Backup tiers: the last line of defence
    • Testing failover
    • In practice: a SaaS with contractual RPO and RTO and a regional failover drill

    Enrolling is free. It unlocks every lesson in Advanced System Design and Case Studies, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments