
September 10, 2026
10 min read
By Kokil Thapa | Last reviewed: September 2026
Topology Spread Constraints in Kubernetes solve a problem every production cluster eventually hits: replicas pile onto one node or one availability zone until a single failure takes down half your service. Pod anti-affinity can spread workloads, but the rules get brittle fast. Spread constraints give the Kubernetes scheduler a cleaner way to balance pods across failure domains. If you run stateless APIs, queue workers, or microservices behind an ingress, this feature belongs in your pod spec from day one.
What Are Topology Spread Constraints in Kubernetes?
Topology Spread Constraints in Kubernetes are scheduling rules defined in a pod or workload template. They describe how far apart matching pods should sit across topology domains. A domain is any node label the scheduler understands, such as topology.kubernetes.io/zone or kubernetes.io/hostname.
Each constraint references a topologyKey, a maxSkew value, and a whenUnsatisfiable action. The scheduler counts matching pods per domain and picks a node that keeps the skew within your limit. This is simpler than chaining pod anti-affinity expressions for every replica set you deploy.
On client projects where I deploy Laravel APIs on Kubernetes, spread constraints sit alongside resource requests and limits. Requests reserve capacity. Spread rules distribute risk. Both matter when a Kathmandu-hosted cluster shares nodes with other production workloads.
Core fields you must understand
- topologyKey — the node label that defines each domain, commonly
topology.kubernetes.io/zone. - maxSkew — the largest allowed difference in pod count between any two domains.
- whenUnsatisfiable — either
DoNotSchedule(hard rule) orScheduleAnyway(soft preference). - labelSelector — which existing pods count toward the skew calculation.
- matchLabelKeys — optional pod-label keys that scope counting to pods from the same controller revision.
How Does the Kubernetes Scheduler Apply Spread Constraints?
The scheduler evaluates spread constraints after filtering nodes that fail hard requirements like taints and tolerations. For each feasible node, it computes the skew per topology domain. Skew equals the pod count in the candidate domain minus the count in the domain with the fewest matching pods.
If every feasible node would produce skew above maxSkew and whenUnsatisfiable is DoNotSchedule, the pod stays pending. That is correct behaviour. It means your cluster cannot satisfy the rule with current capacity. Soft constraints with ScheduleAnyway still place the pod but may leave an imbalance.
I have seen teams confuse pending pods with a broken deployment when the real issue is an unsatisfiable spread rule on a two-node cluster spanning one zone. Check events first. The scheduler message usually names the constraint that blocked placement.
How Do You Configure Topology Spread Constraints in Kubernetes?
Start with a Deployment that runs at least three replicas across three zones. Without spread rules, the scheduler may place all pods on one node because it optimises for packing, not fault tolerance. Add a constraint block to the pod template spec.
Zone spread with a hard constraint
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 6
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
containers:
- name: api
image: myregistry/api:1.4.2
resources:
requests:
cpu: 250m
memory: 256Mi This manifest spreads six API pods across zones with at most one pod difference between the fullest and emptiest zone. Pair it with a second constraint on hostname if you also want node-level separation within each zone.
Hostname spread inside each zone
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: api-server The zone rule is hard. The hostname rule is soft. During a surge, soft rules let pods schedule even if two replicas temporarily share a node. That trade-off beats leaving pods pending during a traffic spike on a booking platform like Adventure Third Pole Trek during peak season.
Using matchLabelKeys for rolling updates
During a rolling update, old and new pods coexist. Without scoping, skew counts both revisions together and can block new pods. Add matchLabelKeys so each ReplicaSet revision spreads independently:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
matchLabelKeys:
- pod-template-hash The pod-template-hash label is injected automatically on Deployments. This pattern prevents update deadlocks I have hit on production clusters managed through Argo CD GitOps pipelines.
How Do Spread Constraints Compare to Pod Anti-Affinity?
Both tools influence pod placement. They differ in expressiveness and operational cost. Spread constraints count pods per domain. Anti-affinity forbids or prefers specific pairwise relationships. For even distribution across zones, spread constraints usually win.
| Criteria | Topology Spread Constraints | Pod Anti-Affinity |
|---|---|---|
| Primary goal | Even distribution across domains | Avoid or prefer co-location with specific pods |
| Rule style | Declarative skew limit per topologyKey | Required or preferred scheduling terms |
| Scaling behaviour | Rebalances naturally as replicas grow | Can require N² term growth for complex rules |
| Failure during shortage | DoNotSchedule leaves pod pending with clear events | Required anti-affinity also blocks; preferred may ignore |
| Best fit | HA replicas across zones or nodes | Never co-locate two specific workloads on one node |
Use anti-affinity when two particular applications must never share a host. Use spread constraints when you want three of everything in every zone. Many production manifests combine both, plus NetworkPolicies for east-west traffic control.
What Production Gotchas Break Topology Spread Constraints?
Spread constraints assume your nodes expose meaningful topology labels. Managed clusters on AWS, GCP, and DigitalOcean usually set zone labels automatically. Bare-metal clusters built with Kubespray need you to label nodes yourself or every pod lands in one logical domain.
- Insufficient domains — six replicas with
maxSkew: 1across two zones forces a skew of two. Either add nodes in a third zone or relax the rule. - Missing topology labels — run
kubectl get nodes -L topology.kubernetes.io/zoneand confirm values exist before enforcing hard constraints. - DaemonSets inflate counts — system pods on a node do not affect spread unless your labelSelector matches them. Scope selectors tightly to your app label.
- HPA scale-up timing — Horizontal Pod Autoscaler adds pods fast. Soft hostname spreads prevent pending pods during scale events.
- Single-zone dev clusters — use
ScheduleAnywaylocally with Minikube or Kind. KeepDoNotSchedulefor staging that mirrors production topology.
The official Kubernetes documentation on pod topology spread constraints describes minDomains for requiring a minimum number of domains before scheduling. That helps when you need at least two zones represented before accepting placement. Read the reference at kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints before tuning production values.
Debugging pending pods
When a pod stays pending, inspect scheduler output:
kubectl describe pod api-server-7f8b9c-xyz | grep -A5 Events
kubectl get nodes -L topology.kubernetes.io/zone,kubernetes.io/hostname Cross-check replica count against available domains. Validate your manifest with a JSON formatter if you generate specs from Helm or Kustomize overlays. Typos in topologyKey silently reduce the rule to a no-op because no nodes match the key.
For Laravel workloads moving from VMs to containers, spread constraints complement the patterns in Kubernetes for Laravel getting started. Queue workers benefit from hostname spreads. Web pods benefit from zone spreads behind an ingress controller.
Capacity planning with spread rules
Hard zone spreads change how you plan node pools. If each zone needs at least two replicas and maxSkew is one, a zone outage still leaves usable capacity elsewhere. That aligns with multi-AZ design on the highly available control plane guidance. Worker spread protects the app layer. Control-plane spread protects the cluster API.
Teams operating on tight budgets in Nepal often run smaller clusters. A three-node pool across one zone cannot satisfy strict multi-zone spreads. Document that limitation. Use soft constraints until budget allows a second zone at Rs 15,000–25,000 per month (~USD 110–185) for additional worker capacity. Honest architecture notes prevent midnight pages.
Key Takeaways
- Define Topology Spread Constraints in Kubernetes on every HA Deployment using zone and hostname topologyKey values.
- Set
maxSkew: 1withDoNotSchedulefor zone rules when you have at least three zones available. - Add
matchLabelKeys: [pod-template-hash]on Deployments so rolling updates do not deadlock scheduling. - Confirm node topology labels exist before enforcing hard constraints on bare-metal or self-managed clusters.
- Combine spread constraints with resource requests, HPA, and anti-affinity only where pairwise exclusion is required.
- Inspect scheduler events on pending pods instead of blindly lowering replica counts or deleting constraints.
People Also Ask
What is maxSkew in topology spread constraints?
maxSkew is the maximum allowed difference between the number of matching pods in any topology domain and the domain with the fewest pods. A value of one means no zone may have more than one extra pod compared to the least populated zone.
Should I use DoNotSchedule or ScheduleAnyway?
Use DoNotSchedule for failure-domain rules you refuse to compromise, such as cross-zone HA for payment APIs. Use ScheduleAnyway for node-level preferences where brief co-location beats leaving pods unscheduled during scale-up or maintenance.
Do topology spread constraints work with StatefulSets?
Yes. Add the same topologySpreadConstraints block to the StatefulSet pod template. Remember that StatefulSets bind to persistent volumes. Spreading pods also requires volumes available in each target zone, which ties spread planning to your storage class and PersistentVolume strategy.
How is this different from pod topology spread default constraints?
Cluster administrators can set default constraints via the PodTopologySpread scheduling plugin configuration in the scheduler config. Pod-level constraints merge with defaults. Explicit pod spec rules override cluster defaults for the same topologyKey when both exist. See the scheduler configuration reference for plugin options.
Build Resilient Clusters With Intentional Pod Placement
Topology Spread Constraints in Kubernetes turn high availability from a hope into a schedulable rule. Start with one hard zone spread and one soft hostname spread on your most critical Deployment. Validate labels, test a zone failure in staging, and wire the manifest into GitOps. If you want help designing HA Kubernetes for a Laravel app, eCommerce stack, or enterprise application, review the portfolio or reach out via contact us. Spread constraints are small YAML blocks that pay off the first time a node dies without taking your API with it.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

