Availability is a ladder, not a single design. Climb it only as far as the failure you actually have to survive — each rung costs more to operate than the one below it.
This page covers every self-hosted model: Hybrid, External, and Central control plane. The tiers are the same for all three. What changes per model is small and listed at the end.The ladder
Tier 1 is enough for most deployments. It is also the only rung the public
clouds put a number on, because it is exactly what they sell: a regional,
zone-redundant Kubernetes cluster. GKE, EKS and AKS each commit to 99.95%
monthly uptime for one. Read that as the ceiling your design works within rather
than a promise about your traffic, because it covers the Kubernetes API server
and not the workloads on it. Tiers 2 to 4 are topologies you build yourself, so
no vendor underwrites them.
Start at tier 1 and stop there unless a regional requirement says otherwise. The tiers are additive: each one assumes you
have already done the ones below it, so tier 3 is tier 1 inside each region plus
a promotion procedure, and tier 4 is tier 3 with both regions taking traffic.
The chart now ships most of tier 1: the request path defaults to two replicas
and spreads itself across zones. What is left to you is
the detector replicas, the disruption budgets and the datastores.
Two invariants
Everything below is easier to reason about once these are fixed. They hold at every tier, in every deployment model.Exactly one writable PostgreSQL primary
A single writer is what prevents split-brain. Two clusters cannot diverge if
there is only one place to write, which turns failover into a datastore
operation rather than a distributed decision.
Exactly one active DataAgent per gateway scope
Two DataAgents on the same scope register duplicate streams. This is why
dataagent.replicas is 1 and must stay 1 — it is a correctness
constraint, not a capacity setting.Tier 1: one cluster, across zones
Spread the node pool over three availability zones and use managed PostgreSQL and Redis with automatic failover inside the region. The chart already runs the request path at two replicas and spreads it across those zones. There is no promotion procedure and no DNS work.What the chart does, and what you still set
The chart now ships tier 1 most of the way: the request path defaults to two replicas and spreads itself across zones. Three things are still yours to set — the replica count on the detectors, the disruption budgets, and the datastores, which is the one that decides whether you are actually at tier 1.Replica counts
The request path defaults to two replicas, so a fresh install already survives losing a node. These are the numbers worth checking before you call a cluster tier 1.
The firewall workers hold the SLM detectors and are the expensive pods, which is
why they still ship as singletons. On a GPU node pool, raising them is a cost
decision as much as an availability one.
autoscaling.enabled is false everywhere. Leave it that way until you have a
capacity reason — autoscaling is a throughput decision, not an availability one,
and a minimum replica count is what tier 1 actually depends on.
Zone spread, and budgets for draining
Replicas alone do not survive a zone loss: nothing stops Kubernetes putting them both on one node, and nothing stops a drain taking them at the same time. Two separate settings cover those. Spreading is on by default. Every workload renderstopologySpreadConstraints across topology.kubernetes.io/zone and then
kubernetes.io/hostname. They use whenUnsatisfiable: ScheduleAnyway, which
makes them a preference rather than a requirement — so they cannot block
scheduling on a single-zone or single-node cluster, and there is nothing to turn
on for the common case.
Disruption budgets are opt-in, behind one switch. A budget stops a node
drain evicting every replica at once:
global.highAvailability, then the built-in
default — so one component can opt out again:
A budget never renders on a single replica. One replica with a budget pins
disruptionsAllowed at 0 and deadlocks every node drain on that node, so the
chart suppresses it below two replicas — including for DataAgent, which must
stay a singleton.podDisruptionBudget, topologySpreadConstraints and
affinity — are accepted on every component, and global.affinity merges
underneath a per-component affinity the way global.nodeSelector does.
Then confirm the replicas really did spread:
Managed datastores are what make tier 1 real
The chart deploys PostgreSQL and Redis in-cluster by default, so a first install needs nothing provisioned. That default is for a proof of concept. Each store runs as a single pod, directly under the request path, so spreading the gateway over three zones buys you nothing while one Redis pod can still take the whole path down with it.Tier 2: twin clusters in one region
Two clusters side by side, both pointed at one PostgreSQL primary and one Redis primary. Because they are in the same region, sharing both stores adds no network hop and loses nothing on a switch: state is identical from either side.Tier 3: two regions, active/passive
Both clusters are full installs. The passive one is warm: its workloads run, pass health checks, and keep synchronizing, but it receives no production traffic.Why the two datastores are treated differently
PostgreSQL — one writable primary, one cross-region read replica. It holds the durable state, so a single writer is what prevents split-brain. Promotion is a datastore operation rather than a distributed decision, and your RPO is the replication lag. Redis — region-local, one per cluster. Redis carries semantic cache, rate-limit counters and evaluation progress, and has no persistence requirement. Reaching across a region for that on every request costs latency on the hot path and buys nothing, because the data is worth seconds. After a promotion the new active cluster starts with cold counters and rebuilds them in seconds. The consequence is that rate limits are counted per region. With one active region at a time that is invisible, except in the switchover window, when a client could briefly get a fresh allowance. With two regions active at once the same behaviour becomes permanent — that is the trade tier 4 asks you to accept.What each failure looks like
Restarts during a control-plane outage
Running pods survive a control-plane outage from their in-memory configuration, but a pod that restarts during one needs the last-known-good file on disk. The chart mounts it on anemptyDir, which does not survive a restart, so a
restarted pod with no control-plane connectivity will not become Ready.
If a cluster has to tolerate restarts mid-outage, move that mount to persistent
storage. The CONFIG_SYNC_LKG_KEY that encrypts it is generated per cluster and
needs no distribution. See Secrets for the
key, and Config sync for the mechanism.
Promote the passive cluster
1
Detect and fence
Use regional health checks backed by Kubernetes readiness on every published
endpoint. Remove the failed cluster from all of them and stop its DataAgent, so
it cannot come back mid-recovery and register a second stream.
2
Point PostgreSQL at a writable primary
If the primary is in the failed region, promote the read replica and update
global.postgresql.host in the surviving cluster. If the primary is unaffected,
there is nothing to do here — this is the case the single-primary design is
buying you. Redis needs no attention either way, because the surviving cluster
already has its own.3
Verify configuration and start active-only services
Confirm the request path is Ready with a current or last-known-good
configuration, then enable DataAgent. Only one DataAgent may be active.
4
Switch every traffic endpoint
Route the published hostnames to the promoted cluster. Keep all protocols on the
same cluster, and remember that DNS-based promotion is delayed by resolver and
client caching.
5
Verify enforcement
Run representative allowed and blocked requests, then confirm policy decisions
and telemetry before declaring the failover complete.
Before you rely on it
- Both clusters are deployed, health checked, and on the same chart version.
- Every request-path workload has redundant replicas across zones in each cluster, with PodDisruptionBudgets enabled.
- PostgreSQL is managed, with one primary and a cross-region read replica.
- Each region has its own managed Redis, and you accept per-region counters.
- Last-known-good configuration is on persistent storage.
- Only the active cluster runs DataAgent.
- Every published hostname resolves to exactly one cluster.
- You have rehearsed: block egress to the control plane and confirm traffic still flows; restart a pod during that outage; promote the passive cluster and switch every endpoint.
Tier 4: two regions, active/active
Both regions take production traffic at the same time. Nothing is promoted when a region fails: your traffic manager stops sending clients there, and the survivor carries everything. That is the whole gain — and the reason it is the most expensive rung to run honestly.What stays the same
Still one writable PostgreSQL primary. Both regions write to it. The region that does not host it pays a cross-region round trip on every write — raw payloads in Hybrid, and additionally configuration and console state in External. Measure that latency against your budget before committing, and put the primary in the region that writes most. Losing the primary’s region is the same promotion as at tier 3; the difference is that the surviving region is already serving, so only the datastore step and DataAgent remain. Still exactly one DataAgent. It reads the single PostgreSQL primary, so one agent already serves both regions’ data. Run it in the primary’s region, and start the other one only as part of a promotion. Still region-local Redis, and still independent configuration. Each region pulls its own configuration and keeps its own last-known-good copy, exactly as at tier 3.What it costs you for good
Before you rely on tier 4
Everything on the tier 3 checklist, plus:- Every client is routed to exactly one region under normal conditions, and you know how.
- Each region has been load-tested carrying the whole peak alone.
- You have measured cross-region write latency from the far region and accepted it.
- You have rehearsed withdrawing a region from the traffic manager and watched the other absorb the traffic.
What changes per deployment model
The ladder is identical in all three. These are the only differences, and each model’s page covers its own in detail.Recovery time
Recovery time depends on health-check intervals, traffic-manager convergence, client DNS behavior, and datastore promotion — all of them yours, not the chart’s. Measure yours rather than assuming it, and re-measure after any change to the traffic manager or the datastore topology.Next steps
Requirements and capacity
Node pools, datastore sizing floors, and the ports between components.
Configuration
Managed stores, ingress, TLS, and every values switch.
Validation
The checks that confirm a cluster is actually serving what you think.
Troubleshooting
What to look at when a replica will not become Ready.