Multi-zone: HA within a region
By the end of this page you will know exactly how many availability-zone failures your cluster tolerates, and how to place nodes so that an AZ outage does not take you down.
Quorum arithmeticβ
A ScramDB cluster keeps two kinds of state in agreement across nodes: cluster-wide metadata (membership, schema, timestamps) and, per table, the shard buckets that hold its rows. Both use the same consensus rule: a group of size N needs a strict majority, floor(N/2) + 1, to make progress on a commit or an election.
| Replication factor / group size | Quorum needed | Simultaneous failures tolerated |
|---|---|---|
| 1 (cluster-of-one) | 1 | 0 |
| 3 (the shipped default) | 2 | 1 |
| 5 | 3 | 2 |
The default replication_factor is 3, so the default cluster tolerates exactly one node failure per shard group, and one voter failure for the cluster's metadata group, without losing availability for the affected group. A second simultaneous failure in the same group of 3 stops that group from making progress until a node returns; if that group is the metadata group, cluster-wide DDL and timestamp issuance stall too.
This is why three nodes, not two, is the smallest fault-tolerant topology. A two-node group needs both nodes alive for quorum, which is strictly worse than a one-node group: there is no majority available with only one of two alive.
If fewer members are live than a table's configured replication factor calls for, ScramDB does not refuse writes; it keeps running on the members it has and marks the affected buckets as running with a reduced replica count rather than failing outright. Treat that state as "fix this before you lose another node," not as steady state.
The zone label: visible, not placement-drivingβ
Every node can carry a zone label in its [cluster] config, alongside the region label described in Multi-region:
[cluster]
zone = "eu-central-1a"
zone defaults to unset. It is meant as a finer failure domain inside a region (a rack or an AZ), and, like region, it is replicated cluster state: every node's own zone is visible cluster-wide, not just known to that node.
SELECT node_id, region, zone, role, state FROM scram.nodes;
What zone does not do is drive placement. Bucket-owner ranking uses region when a table is homed (see Multi-region), but it never looks at zone: there is no automatic "spread replicas across zones" behavior, and setting zone on your nodes does not, by itself, change where any bucket's voters land. Placement within a region, or across an unhomed cluster, is topology-blind at the zone level by design; it ranks live members for a given bucket with no zone input.
This means AZ-level fault tolerance is entirely a placement decision you make, not a switch you flip:
Put one node per availability zone. For the default replication_factor = 3, run exactly three nodes in exactly three different AZs. That way the "tolerates one simultaneous failure" property from the quorum table above lines up with "tolerates one AZ outage." For a higher replication factor, use that many AZs (or repeat AZs deliberately if you have fewer available and accept the reduced isolation).
Latency: the defaults already fit this caseβ
The default consensus timings, a 50 millisecond heartbeat and a 1 to 2 second randomized election window, are tuned for low-latency round trips. Cross-AZ round trips within one region are typically low single-digit milliseconds on any major cloud, well inside that budget. That is why multi-zone-in-one-region needs no timeout retuning beyond spreading your nodes across zones; contrast this with Multi-region, where the round trip itself grows large enough to matter.
Kubernetes: add your own topology spreadβ
The shipped StatefulSet manifest does not include any topologySpreadConstraints or podAntiAffinity for the ScramDB pods, so on a multi-zone Kubernetes cluster the scheduler is free to place two or three replicas in the same zone unless you tell it not to. Add your own topology spread constraint (keyed on your cluster's zone label, commonly topology.kubernetes.io/zone) to the pod template so that each of your three (or five) replicas lands in a distinct zone. This is standard Kubernetes scheduling configuration you add on top of the manifests, not something ScramDB provides out of the box.
Failure-domain framing, worked throughβ
For a three-node cluster with one node per AZ and replication_factor = 3:
- One AZ goes down: one node is unreachable, the remaining two form a majority, the cluster keeps serving reads and writes with no data loss.
- Two AZs go down at once: two of three nodes are unreachable, no majority remains, the affected groups (and the metadata group, if its voters were split the same way) stop making progress until a node returns.
Scale this reasoning to five nodes across five AZs at replication_factor = 5 if you need to tolerate two simultaneous zone outages.
Nextβ
- Multi-region once your fault domains need to span further than one region's AZs.
- Failover for exactly what a client observes during the outage window described above.
- Forming a cluster if you haven't stood up the base cluster yet.