Most GKE outages that platform teams actually suffer are not Google's fault - they're the predictable result of a misunderstanding: the GKE SLA covers the control plane, not your workloads. Getting high availability right means owning the workload half yourself. This guide is the complete picture, from cluster shape to surviving a global Google outage.
What the GKE SLA actually covers
Google commits to the availability of the Kubernetes control plane - the API server, scheduler, and controller manager - at 99.95% for regional clusters and 99.5% for zonal. It does not commit to your pods being up. That distinction is the single most important thing to internalize: Google keeps the control plane running; you keep your workloads running, on top of it.
Regional vs zonal: start here
A zonal cluster runs one control-plane replica in a single zone. Lose that zone and you lose control-plane access (though existing pods keep running - more on that below). A regional cluster replicates the control plane across three zones and earns the 99.95% SLA. For anything you care about, regional is the baseline, not an upgrade. The cost is marginal; the resilience difference is a whole failure class.
Spread the workload across zones
The control plane being regional doesn't help if all your nodes are in one zone. Run node pools across at least three zones, so a zonal failure removes a third of your capacity rather than all of it. Use separate node pools to isolate critical workloads from noisy neighbors, and size for n-1 zone capacity if a zone loss must be invisible.
PodDisruptionBudgets and topology spread
Two Kubernetes primitives do the heavy lifting:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2 # never let voluntary disruptions drop below 2 replicas
selector:
matchLabels:
app: web
A PodDisruptionBudget stops a node upgrade, drain, or autoscale event from taking all replicas at once - a shockingly common self-inflicted outage. Pair it with topology spread constraints so replicas are distributed across zones and nodes rather than piling onto one:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
Decouple your runtime from the control plane
Here's the property that saved teams during the June 2025 Service Control outage: running pods keep serving even when the control plane is unreachable. The kubelet on each node runs independently; what you lose during a control-plane outage is the ability to change things - scheduling, scaling, rollouts. So the goal is to make your steady state survive without the API server:
- Don't call the Kubernetes API on the request hot path. Cache config, use operators that reconcile asynchronously, and avoid patterns that need a live API server per request.
- Make readiness and liveness probes local - don't have them depend on a global Google API that could be the thing that's down, or a healthy pod will kill itself during an unrelated outage.
- Pre-provision headroom so you don't need to autoscale during an incident when scaling is exactly what's unavailable.
Multi-cluster: when to bother
Fleets and multi-cluster gateways add a second cluster (often in a second region) behind a global load balancer, so a whole-cluster or whole-region failure fails over. It's real resilience and real operational cost - justified for workloads whose RTO can't tolerate the time to rebuild a cluster, and overkill for most. Decide with the RTO/RPO discipline in the multi-region GCP guide, not by default.
Game-day the failure modes
A GKE HA design you haven't tested is a hypothesis. Quarterly, simulate the three that matter: zone loss (cordon and drain a zone's nodes), control-plane unavailability (verify pods keep serving and that nothing hot-path depends on the API server), and a global-API outage (confirm your probes and app don't self-destruct when a Google global layer errors). Fix what breaks, then re-test - and wire up outage alerts so detection isn't the weak link.