High Availability & Multi-Region Topologies

9 min read Module 9 of 10 Lesson 26 of 28

What you'll learn

  • Identify which gateway state must be replicated across regions and which can safely stay region-local
  • Explain the split-brain risk multi-region budget enforcement introduces and a practical mitigation
  • Configure geo-aware routing so callers reach the nearest healthy gateway region by default
  • Design the failover behavior when an entire region, not just one deployment, becomes unavailable
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

Lesson 25 deployed the gateway with multiple replicas and autoscaling, which survives a pod or a node failing. It does not survive an entire region going down, a rarer but real event, and one a gateway sitting in front of every LLM call in a company cannot treat as acceptable downtime. This lesson designs the topology that does survive it, and the specific new problem multi-region introduces: state that used to be safely local now needs a real answer for how it behaves when split across regions.

What Needs to Replicate, and What Doesn’t

flowchart TD
    subgraph RegionA["Region: us-east"]
        GWA["Gateway replicas"]
        RedisA[("Local Redis:\ncache, region-local rate limits")]
    end
    subgraph RegionB["Region: eu-west"]
        GWB["Gateway replicas"]
        RedisB[("Local Redis:\ncache, region-local rate limits")]
    end
    GlobalState[("Global state store:\ncross-region key budgets\n(Postgres, or a per-region\nbudget allocation, see below)")]

    GWA --> RedisA
    GWB --> RedisB
    GWA -.->|budget checks| GlobalState
    GWB -.->|budget checks| GlobalState

    style GlobalState fill:#4338CA,stroke:#4338CA,color:#ffffff,rx:8px,ry:8px
    style RedisA fill:#F7F8FC,stroke:#0D9488,color:#0F172A,rx:8px,ry:8px
    style RedisB fill:#F7F8FC,stroke:#0D9488,color:#0F172A,rx:8px,ry:8px

Not all state carries the same replication requirement. The exact-match and semantic caches from Lesson 16 are safe to keep region-local: a cache miss in one region that would have been a hit in another just costs one extra provider call, a correctness non-issue, purely a minor efficiency loss. Rate-limit counters (Lesson 15) can also often stay region-local with an acceptable trade-off, a caller who happens to be load-balanced across regions gets a rate limit that’s effectively looser by a small factor, usually tolerable. Budget state (Lesson 18) is the one that actually needs a real answer: a $50 monthly cap enforced independently by each region’s local state could let a caller spend up to $50 in every region it touches, multiplying the real intended limit by the number of regions.

The Split-Brain Mitigation

Full synchronous cross-region budget checks on every request add real latency and a new failure mode (what happens to the request if the cross-region check itself times out?). The standard practical compromise is pre-allocating a fraction of the total budget to each region:

def region_local_budget(total_budget: float, region: str, region_traffic_weights: dict[str, float]) -> float:
    # Split a key's total budget across regions proportional to expected
    # traffic share, so each region enforces a fast, local check against
    # its own allocation rather than a synchronous cross-region lookup
    # on every single request.
    return total_budget * region_traffic_weights.get(region, 1 / len(region_traffic_weights))

# A key with a $500/mo budget, expected 70% traffic in us-east, 30% in eu-west
us_east_cap = region_local_budget(500, "us-east", {"us-east": 0.7, "eu-west": 0.3})   # $350
eu_west_cap = region_local_budget(500, "eu-west", {"us-east": 0.7, "eu-west": 0.3})   # $150

This trades perfect global precision for enforcement that stays fast and available: the true worst-case overspend is bounded by the sum of regional allocations rather than unbounded, and a periodic (not per-request) reconciliation job can true up actual global spend against the intended total and adjust regional allocations going forward. For most teams, this bounded imprecision is an acceptable trade against the latency and availability cost of synchronous cross-region coordination on every request; a company with a harder financial requirement can instead run budget checks against a strongly consistent global store and accept the added latency, a legitimate but different choice.

Detecting and Routing Around a Region Failure

Lesson 14’s fallback chains operate entirely inside one running gateway process: they choose among provider deployments, with no visibility into whether the gateway process itself, or the whole region hosting it, is reachable at all. Region-level failure needs a layer above the gateway:

# Example: a DNS-based health check configuration (conceptually,
# using a provider like Route 53 or Cloudflare's load balancer)
# routes callers to the nearest healthy region, and away from a
# region whose gateway health endpoint stops responding.
healthCheck:
  endpoint: /health/readiness   # the same readiness endpoint from Lesson 25's Kubernetes config
  regions: [us-east, eu-west]
  failoverPolicy: nearest-healthy
sequenceDiagram
    participant Client
    participant DNS as Geo DNS / Anycast LB
    participant USEast as us-east gateway
    participant EUWest as eu-west gateway

    Client->>DNS: resolve ai-gateway.company.com
    DNS->>USEast: health check
    USEast--xDNS: unreachable (region down)
    DNS->>EUWest: health check
    EUWest-->>DNS: healthy
    DNS-->>Client: route to eu-west
    Client->>EUWest: request proceeds normally

This is the same “chokepoint enforced once, benefits everyone” principle that has run through this entire course, applied at the infrastructure layer: application code never needs to know a region failed, it always resolves ai-gateway.company.com and the DNS/load-balancer layer handles routing it to whichever region is actually healthy. With deployment and multi-region resilience covered, Lesson 27 closes Module 9 by comparing LiteLLM against the other three gateways introduced in Lesson 2, Portkey, TensorZero, and Envoy AI Gateway, now that you have enough hands-on context to evaluate the trade-offs directly.

Knowledge Check

3 questions to test your understanding

1 Why can't per-key budget state (Lesson 18) simply live independently in each region's local Redis with no cross-region coordination, the way region-local caching (Lesson 16) safely can?

2 What is a practical mitigation for the split-brain budget risk in a multi-region gateway, when full synchronous cross-region state replication is too slow or unreliable to rely on for every request?

3 When an entire gateway region becomes unavailable, not just one provider deployment within it, what needs to detect this and redirect traffic, and how does this differ from the single-deployment fallback chains built in Lesson 14?

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan