Hotshard
Distributed systems

Multi-Region & Geo-Replication

Your users are global but your database lives in one region — what breaks, and what does going multi-region actually cost?

Your app runs in one region. The servers are there, the database is there, and for users near that region everything feels instant. Then the product works: someone in Sydney signs up, someone in Frankfurt, someone in São Paulo. Every one of their requests now crosses an ocean to reach your one region and crosses back, and the app feels slow in a way no amount of caching fully fixes. Worse, that one region is a single point of failure for the entire planet — when it has a bad day, everyone is down, not just the users near it. And a growing pile of laws says some of that data is not even allowed to leave the country it came from. Going multi-region — putting copies of your system in several parts of the world — answers all three problems. But it runs headfirst into a wall you cannot negotiate with: the speed of light. A write that has to be confirmed on another continent costs tens to over a hundred milliseconds, every time, and no engineering removes it. This page is about that wall, the two topologies that live on either side of it, how you route a user to the nearest healthy region, and the honest verdict most systems land on.

~15 min read

Start here: three reasons one region isn't enough#

TL;DRthe 30-second version
  • One region serving a global audience has three problems: distant users feel every network round-trip (latency), a regional outage takes down every user everywhere (availability), and some data is legally required to stay in a specific country (data residency).
  • Multi-region means running copies of your system in several parts of the world and keeping their data in sync. The sync is the hard part, and the speed of light sets its price: a cross-region round-trip costs tens to over 100 ms, so you cannot keep every region strongly consistent without paying that on every write.
  • Two shapes. Active-passive: one region takes all writes, the others are warm read-only copies ready to be promoted. Simple, but async replication means a failover can lose the last few seconds of writes. Active-active: every region takes writes, which is fast and survives a full region loss — but two regions can now write the same record at once, creating conflicts you must resolve.
  • Send each user to the nearest healthy region with latency-based or geo DNS routing (the same edge idea as /cdn), and detect a dead region to fail over — while guarding against split-brain, where two regions both think they're in charge.
  • The honest verdict: most systems are active-passive. Active-active is expensive and only pays off past a real scale-and-latency threshold.

A region is a cluster of datacenters in one geographic area — AWS us-east-1 in Virginia, eu-west-1 in Ireland, ap-southeast-2 in Sydney. Put your whole system in one of them and you've made an implicit bet: that everyone who matters is close to it. The moment your users are spread across the world, that bet stops paying off, and it fails in three separate ways.

The first is latency. A user in Sydney talking to a database in Virginia is roughly 16,000 km away, and light in fibre isn't instant — a single round-trip is around 200 ms before your server does any work. A page that makes even a handful of sequential round-trips feels sluggish, and no clever caching hides a slow write. The second is availability: that one region is a single point of failure for your whole planet. When it has an outage — and every region eventually does — every user everywhere is down at once, not just the ones nearby. The third is legal. Data-residency and data-sovereignty laws say that certain data about a country's citizens must be stored, and sometimes processed, inside that country's borders — Russia's 242-FZ and China's PIPL are hard examples, and the EU's GDPR restricts moving personal data out of the EEA without an approved safeguard. If your only database is in Virginia, you may simply not be allowed to serve some users at all.

What "multi-region" actually buysPutting a copy of your system in Sydney, Frankfurt, and Virginia fixes all three at once: the Sydney user talks to a nearby copy (latency), losing one region leaves the others serving (availability), and EU data can live in Frankfurt (residency). The catch is that these copies hold the same data, and keeping several copies of the same data agreeing across the world is where every hard problem on this page comes from.

The hard wall: the speed of light#

Before any topology, understand the constraint that shapes all of them, because it's the one thing you cannot engineer away. Light travels about 300,000 km per second in a vacuum, but through fibre-optic cable it's closer to 200,000 km/s — roughly two-thirds of that. So distance has a hard floor on latency: New York to London is about 5,600 km, which is ~28 ms one way at fibre speed, ~56 ms for a round-trip in the best imaginable case. Real networks add routing, switching, and congestion on top, so the round-trip you actually measure between those cities is more like 70–90 ms. Cross-continent links run higher still: US to Australia is commonly 150–250 ms round-trip.

user in Sydneyclicks save
write (local, ~1 ms)
region: Sydneymust confirm the write is safe elsewhere
replicate + wait for ack →~100–200 ms round-trip — the speed of light, every write
region: Virginiastores the write, replies
A synchronous cross-region write pays the round-trip before it can acknowledge
This is the crux of the whole topicTo guarantee that every region always sees the latest write — strong consistency, the /consistency-models topic — a write has to be confirmed on other regions before it's acknowledged. That confirmation is a cross-region round-trip: tens to over 100 ms, on every write, forever. You can't cache it away and you can't optimize it away, because it's physics. So you face a genuine fork: pay that latency on every write to keep all regions in lockstep, or let regions run slightly out of sync so writes stay fast. Every multi-region design is an answer to that fork.

That's why you can't just flip a switch and make your one-region database "global and strongly consistent." Strong consistency across regions means every write waits for the ocean. Some systems do pay it — for money and correctness it's worth it — but the default for most data is to relax consistency (the /replication-modes and /consistency-models topics are the menu of exactly how much) so the common path stays fast, and to accept that different regions can be briefly out of step.

The two topologies: active-passive vs active-active#

There are two fundamental shapes for a multi-region system, and they sit on opposite sides of that wall. The difference is a single question you already know from replication: which regions are allowed to accept a write?

Active-passive (also called primary-secondary, or single-region-write) keeps one region as the writer. All writes go to that one primary region; the other regions hold copies that serve reads and stand ready to take over. This is single-leader replication (the /replication-modes topic) stretched across the world: the primary streams its change log to the standby regions, which replay it. Because only one region ever writes, there are never two conflicting versions of a record — the whole zoo of conflict problems simply doesn't exist. The replication is almost always asynchronous, because making every write wait for a cross-region acknowledgement would pay the speed-of-light tax on the hot path. Asynchronous is the choice that keeps writes fast, and it's also the source of active-passive's one real weakness, which the failover section gets to.

Active-active (also called multi-region-write) lets every region accept writes. A user in Frankfurt writes to Frankfurt at local speed; a user in Virginia writes to Virginia at local speed. Nobody crosses the ocean on the write path, so writes are fast everywhere and the system survives a whole region vanishing without even a failover — the other regions were already taking writes. This is multi-leader (or leaderless) replication across regions, and it buys the best latency and availability there is. It also opens the one door active-passive kept shut: two regions can now write the same record at the same time. Frankfurt sets a user's display name to "A" while Virginia sets it to "B", neither knew about the other, and when their change logs meet, the system must decide which wins. That is a cross-region write conflict, and resolving it is the entire cost of active-active.

The three ways to resolve a cross-region conflictLast-write-wins: attach a timestamp to each write and keep the latest. Simple, and what DynamoDB global tables do — but it silently discards the losing write, and clock skew across regions can pick the wrong winner. CRDTs (the /crdts topic): data types designed so concurrent writes merge without a lost update — an increment-only counter just adds both increments, no decision needed. A home region per record: pick one region as the owner of each record (by user id, say), and route all writes for that record there — so within one record you're back to single-writer and there's no conflict, while different records are owned by different regions. Most large active-active systems lean on the last one.
Active-passiveActive-active
Who writesOne primary regionEvery region
Write latency for a distant userCrosses to the primary regionLocal — writes the nearby region
Cross-region conflictsNone — one writer orders everythingYes — must resolve (LWW / CRDT / home region)
Surviving a region lossFailover: promote a standby (seconds–minutes)Instant — others already take writes
ComplexityLowHigh
Go deeperUnder the hood: entity-group pinning and per-record home regions

The "home region per record" idea is how systems get active-active's local-write latency without paying active-active's conflict cost everywhere. You partition the data by some key — usually the user — and declare one region the owner of each partition. All writes for user 42 go to user 42's home region (say, the region nearest where they signed up), so those writes are single-writer and conflict-free, while user 99's writes go to a different home region entirely. Google's earlier Megastore called a co-located, same-owner group of records an entity group and gave strong consistency inside a group while keeping cross-group operations cheap.

The trick works when operations rarely cross partitions — one user's data mostly stays within that user's activity. It strains when a single operation must touch records owned by different regions (a transfer between two users homed in different regions), because now you're back to a cross-region coordination that pays the round-trip. So the art is choosing a partition key along which most operations are local. It's the same skill as picking a shard key, applied to geography.

Getting the user to the right region#

Copies in three regions are useless if every user still lands on the same one. Something has to send each user to the region that's best for them — usually the nearest healthy one — and it happens before the request even reaches your servers, at the DNS layer. This is the same edge-routing idea behind a CDN (the /cdn topic), applied to whole regions instead of cached files.

  • Geo (geolocation) routing: the DNS resolver looks at where the request came from and returns the IP of the region assigned to that location. EU users resolve to Frankfurt, US users to Virginia. This is also how you enforce data residency — you route a country's users to the region that's legally allowed to hold their data.
  • Latency-based routing: instead of raw geography, the DNS service returns whichever region currently has the lowest measured network latency to the user, which isn't always the geographically closest one. AWS Route 53 and similar services offer this directly.
  • Health-aware failover: the routing layer continuously health-checks each region and stops handing out a region that's failing, so new requests flow to a healthy one automatically. This is what turns "we have a standby region" into "we actually survive an outage".
Why DNS, and its one catchRouting at DNS is attractive because it steers traffic before it touches your infrastructure — the user is directed to Frankfurt or Virginia at name-resolution time, so a dead region never even receives the request. The catch is that DNS answers are cached by resolvers and clients for the length of their TTL (time-to-live), so a routing change isn't instant — some clients keep using the old answer until their cache expires. That's why failover DNS uses short TTLs, and why serious setups pair DNS routing with faster health-aware layers (anycast IPs, global load balancers) that react in seconds rather than minutes.

Data residency: a legal constraint that shapes the design#

Latency and availability are engineering choices — you can decide how much to spend on them. Data residency is not a choice; it's a legal boundary that shapes the architecture from the outside. Laws like Russia's 242-FZ and China's PIPL require that personal data about a region's people be stored, and sometimes processed, within that region's borders; the EU's GDPR takes a softer form, restricting transfers of personal data outside the EEA unless an approved safeguard is in place. Other countries have their own versions. The consequence for a multi-region design is concrete: you cannot freely replicate all data to all regions, because copying an EU user's record to a US region may itself be the violation.

So residency forces a partitioning decision. The common pattern is to home each user's regulated data in the region for their jurisdiction — EU users' data lives only in EU regions, and never replicates out — while keeping non-regulated or anonymized data global. This lines up neatly with the home-region-per-record idea from active-active: the same key you'd pick for conflict avoidance (the user) is often the key the law cares about. When residency is in play, the geography of your data isn't a performance tuning knob anymore — it's a hard constraint that decides which region can hold what, and your routing must send each user to a region that's permitted to serve them.

What it costs: latency budgets, RPO and RTO#

Multi-region has two kinds of cost. One is the per-write latency already covered — the cross-region round-trip you pay whenever a write must be confirmed elsewhere. The other is the cost of a failover, and the industry measures it with two numbers you should be able to name in an interview.

  • RPO (Recovery Point Objective): how much data you can afford to lose, measured as a time window. With asynchronous cross-region replication, a standby region runs a little behind the primary — that lag is your RPO. If the standby is 5 seconds behind and the primary is lost, you lose up to 5 seconds of acknowledged writes. RPO of zero means no data loss, which requires synchronous replication, which means paying the cross-region round-trip on every write.
  • RTO (Recovery Time Objective): how long you can afford to be down, measured as time-to-recover. It's how long it takes to detect the region is dead, promote a standby to primary, and re-point traffic. A warm standby that's already running recovers in seconds to minutes; a cold backup you have to restore and boot recovers in hours.
  • Replication lag budget: the same lag that sets your RPO also causes stale reads — a user routed to a lagging region reads slightly old data. You decide how much lag is acceptable and alert when a region drifts past it, exactly as in the single-region /replication-modes case, but now the lag is amplified by cross-region distance.
PredictYou run active-passive across two regions with asynchronous replication. The standby normally runs about 3 seconds behind the primary, and you're taking 4,000 writes per second. The primary region has a total outage and you fail over to the standby. Roughly how many acknowledged writes are lost — and what single change drives that number to zero, and at what price?

Hint: Lost writes ≈ replication lag × write rate. Zero loss needs the write confirmed in another region before it's acknowledged.

About 12,000 writes. Everything the primary acknowledged in the last ~3 seconds was confirmed to users but hadn't reached the standby yet, so promoting the standby loses all of it: 3 s × 4,000 writes/s = 12,000 acknowledged-but-lost writes. That 3-second lag is exactly your RPO. The one change that drives the loss to zero is synchronous cross-region replication — the primary waits for the standby to confirm each write before telling the user "saved", so there's always a second copy and nothing acknowledged can be lost (RPO of zero). The price is the speed of light on every write: instead of a ~1 ms local acknowledgement, each write now waits a cross-region round-trip of ~70–200 ms depending on the region pair. That's the whole multi-region trade in one number — you buy zero data loss with a 100× write-latency hit, so you pay it only where losing a write is unacceptable (payments, orders) and stay asynchronous everywhere else.

Where it breaks: failover and split-brain

Failover is the moment active-passive exists for, and it's also its most dangerous moment. When the primary region dies, a standby has to be promoted to primary. With asynchronous replication the standby is behind, so it's missing the writes the old primary acknowledged but hadn't shipped — those are the RPO writes, and they don't come back. The new primary starts taking writes as if they never existed. That's the known, accepted cost of async, and it's the same failover data-loss window as single-region /replication-modes, just wider because the ocean makes the lag bigger.

The subtler danger is split-brain: two regions both believing they are the primary at the same time. It happens when the network partitions — the link between regions breaks, but both regions are alive. The standby can't reach the primary, assumes it's dead, and promotes itself. Now both regions accept writes, their histories diverge, and when the link heals you have two conflicting timelines and no clean way to merge them. This is genuinely destructive: it's not stale data, it's two contradictory sources of truth.

How split-brain is preventedThe standard defense is to require a majority (quorum) vote to become primary, so two regions can't both win — the /quorum/sim rule and the reason Raft (/raft) ties leadership to a majority election. With three regions, a promotion needs two votes; a partition can only give a majority to one side, so at most one region can lead. This is why serious multi-region setups run an odd number of regions (or place tie-breaker witnesses in a third location): so a partition always has a clear majority side and a clear minority side, and the minority steps down instead of forming a second brain.
Choosing: which topology, and the honest default

The two topologies are not a ranking — they're a trade, and the right pick is set by what the workload actually needs. The costs line up cleanly against the benefits.

  • Active-passive when you mainly need disaster recovery and read scaling, and your write volume fits one region. You get a much simpler system with no conflict resolution, at the cost of a failover window (some data loss on async, some downtime while you promote). This covers the large majority of systems that just want to survive a region outage.
  • Active-active when distant users need low write latency, or you must survive a full region loss with zero downtime, or writes exceed what one region can take. You get local writes everywhere and instant survival, at the cost of building and operating conflict resolution — which is real, ongoing engineering, not a config flag.
  • Reserve synchronous cross-region replication (RPO zero) for the specific data that can't lose a write — payments, orders, ledgers — and keep everything else asynchronous so the common path stays fast. You rarely make a whole system synchronous across regions; you make the few operations that need it synchronous and pay the round-trip only there.
The honest verdictMost systems are active-passive, and should be. Active-active is genuinely expensive — you take on conflict resolution, harder testing, harder debugging, and a permanent tax on how you model data — and it only pays off past a real threshold: a truly global write-heavy audience, or a zero-downtime requirement that a failover window can't meet. If you're reaching for active-active before you've felt the pain that justifies it, you're buying complexity you don't need yet. Start active-passive; earn your way to active-active.
The four postures, from cheapest to strongest

AWS frames the choices as four disaster-recovery postures that climb in cost and complexity as RPO and RTO shrink. They're a useful ladder even outside AWS, because they name the whole spectrum from "barely multi-region" to full active-active.

PostureStandby region runs…RPO / RTOCost
Backup & restoreNothing — just backups you restoreHours / HoursLowest
Pilot lightCore data replicating, servers offMinutes / ~Tens of minutesLow
Warm standbyA scaled-down live copySeconds / MinutesMedium
Active-active (multi-site)A full live copy taking trafficNear-zero / Near-zeroHighest

Reading top to bottom, each step buys a smaller RPO and RTO by keeping more of the second region running all the time — and pays for it in money and complexity. Backup-and-restore and pilot-light are flavors of active-passive with a colder standby; warm standby is active-passive with a hot one; only the bottom row is true active-active. The skill is picking the cheapest posture whose RPO and RTO your product can actually live with, not the strongest one available.

In the wild
  • DynamoDB global tables — active-active across regions, with last-write-wins conflict resolution on concurrent updates to the same item. Chosen when you want multi-region writes with minimal operational effort and can accept LWW's dropped-write semantics.
  • Google Spanner — the rare system that offers strong consistency (strict-serializable transactions) across regions. It pays for it with tightly-synchronized clocks: TrueTime exposes clock uncertainty as a small interval and the commit protocol waits out that uncertainty (commit-wait) so a global real-time order holds. The /clocksync/sim topic is why this is hard — Spanner solved it with GPS and atomic clocks per datacenter to keep uncertainty under a few milliseconds.
  • PostgreSQL / MySQL cross-region — classic active-passive: one primary region streams its log to read replicas in other regions, promoted on failover. The default and correct choice for most teams that just need regional disaster recovery and nearby reads.
  • CockroachDB / YugabyteDB — Raft-based (/raft) multi-region databases that let you pin a row's home region and keep strong consistency per range, offering the entity-group idea as a first-class table setting.
  • CDNs and edge networks (the /cdn topic) — the read-side complement: static and cacheable content is served from hundreds of edge locations near the user, so only the genuinely dynamic, write-touching requests have to reach a region at all.
Common questions & gotchas
Can't I just replicate my database to another region and call it multi-region?

That gets you active-passive disaster recovery, which is real value — but it's not free and it's not magic. The replication is asynchronous, so a failover loses the lag window of writes (your RPO), and you still need routing to send users to the right region and a promotion process to survive an outage. And it does nothing for write latency: distant users still cross the ocean to reach the one writable primary. "Replicate to another region" is the start of active-passive, not the finish, and it is not active-active.

Why not make everything strongly consistent across regions so I never worry about stale data?

Because strong consistency across regions means every write waits for a cross-region round-trip before it's acknowledged — tens to over 100 ms on every single write, which is the speed of light and can't be optimized away. For a like count or a feed that's an absurd price for a guarantee the feature doesn't need. You reserve cross-region strong consistency for the few operations that truly require it (money, uniqueness) and let the rest be eventually consistent so the common path stays fast. Match the guarantee to the need, per feature — that's the /consistency-models discipline.

How do active-active systems handle two regions writing the same record at once?

Three ways. Last-write-wins keeps the write with the latest timestamp and discards the other (simple, but silently loses data and trusts clocks). CRDTs use data types that merge concurrent writes with no lost update (the /crdts topic). Or you give each record a home region and route all its writes there, so within one record you're single-writer and there's no conflict at all. Large systems usually rely on home regions plus CRDTs for the specific fields that need to merge.

What's split-brain and why is it worse than just losing some data?

Split-brain is two regions both believing they're the primary during a network partition, so both accept writes and their histories diverge. It's worse than a data-loss failover because you don't end up with old data — you end up with two contradictory sources of truth and no clean merge. The fix is a majority-quorum election (the /quorum/sim rule, as in /raft): a region can only become primary with a majority of votes, and a partition can only give a majority to one side, so a second brain can never form.

If Spanner gives strong consistency across regions, why doesn't everyone use it?

Because it still pays the physics. Spanner achieves a global real-time order using specialized hardware — GPS receivers and atomic clocks in every datacenter — to keep clock uncertainty tiny, then waits out that uncertainty on commit (commit-wait). That's a remarkable engineering feat, but cross-region transactions still cost the coordination latency, and you inherit Spanner's model and cost. Most teams don't need global strict-serializability and shouldn't pay for it; they need a nearby read, a surviving standby, and strong consistency on just the few operations that matter.

QuizA team runs active-passive across two regions with async replication and automatic failover. A network partition cuts the link between regions, but both regions are healthy. The standby can't reach the primary, assumes it's dead, promotes itself, and starts taking writes. Both regions now accept writes. What just happened, and what would have prevented it?

  1. Normal failover — this is working as intended.
  2. Split-brain: both regions believe they're primary and their histories diverge; a majority-quorum election (needing votes from a majority, e.g. a third region or witness) would prevent either side from promoting without a majority.
  3. Replication lag; the fix is to lower the RPO.
  4. A DNS caching problem; shorten the TTL.
Show answer

Split-brain: both regions believe they're primary and their histories diverge; a majority-quorum election (needing votes from a majority, e.g. a third region or witness) would prevent either side from promoting without a majority.This is split-brain: the partition left both regions alive but unable to talk, the standby wrongly concluded the primary was dead and promoted itself, and now two regions accept writes and diverge into two contradictory histories — the most destructive multi-region failure, because there's no clean merge afterward. The prevention is to require a majority (quorum) vote to become primary. With only two regions there's no majority to break a tie, which is exactly why you add a third region or a lightweight witness: a partition can then give a majority to only one side, so the minority side steps down instead of forming a second brain. Lowering RPO and shortening DNS TTLs address other problems (data-loss window, routing speed) but neither stops two regions from both promoting.

In an interview

Multi-region is a favorite scaling question because it rewards knowing the constraint, not the buzzwords. The move that signals depth: lead with the speed-of-light wall, then reason about topologies as answers to it — and don't reach for active-active by default.

  • State why you'd go multi-region at all — latency for distant users, availability against a region outage, data residency law — because the reason picks the design. Residency, especially, is a hard constraint, not a nice-to-have.
  • Name the wall first: a cross-region round-trip is tens to over 100 ms and it's the speed of light, so you can't keep every region strongly consistent without paying it on every write. This is the sentence that shows you understand the trade.
  • Frame the two topologies as who-writes: active-passive (one write region, simple, but async failover loses the RPO window) vs active-active (every region writes, fast and survivable, but you own cross-region conflict resolution — LWW, CRDTs, or home regions).
  • Make the failover concrete with RPO and RTO: RPO is the data-loss window (≈ replication lag × write rate), RTO is time-to-recover. Reserve synchronous replication (RPO zero) for money-critical writes only, and name split-brain and its majority-quorum fix.
  • Land the honest verdict: most systems are active-passive; active-active is expensive and only pays off past a real scale-and-latency threshold. Saying you'd start active-passive and earn your way up reads as judgment, not ignorance.
PredictThe interviewer says: 'We're a single-region app in the US. We're expanding to Europe — EU users complain it's slow, and legal says EU personal data must stay in the EU. Walk me through what you'd change.' What's the strong answer?

Hint: Three needs — latency, residency, write topology — and one partition key (the user) that quietly solves two of them at once.

Separate the three needs and solve each with the cheapest thing that works, rather than declaring "let's go active-active." First, stand up an EU region and route EU users to it with geo/latency-based DNS (the /cdn edge idea), which fixes the slowness for reads and nearby requests immediately. Second, address residency by homing EU users' personal data in the EU region and not replicating it out — this is a partition-by-jurisdiction decision, and it happens to line up with a home-region-per-user model, so it also sets up conflict-free writes later. Third, decide the write topology by the actual need: if EU users' writes mostly touch their own data, give each user a home region (writes for an EU user go to the EU region, US user to the US region) — that's local write latency with no cross-region conflict, and it satisfies residency at the same time. Keep the truly shared, correctness-critical writes (billing, say) on a single primary with synchronous replication only where a lost write is unacceptable, and everything else asynchronous. The strong signal is refusing to make the whole system strongly consistent across the ocean, naming the speed-of-light cost as the reason, and noticing that the residency partition and the conflict-avoidance partition are the same key — so one decision buys both. If pushed on failure, mention majority-quorum promotion to avoid split-brain and an RPO target for the async parts.

References & further reading
References

Feedback on this topic →