Topology Spread Constraints in Kubernetes: Balancing Pods Across Failure Domains
Learn how to tell Kubernetes to spread copies of your application evenly across nodes, zones, and regions, how skew is calculated, and how to choose settings that survive real-world failures without leaving pods stuck.
Imagine a baker who sells bread through six market stalls in three different towns. If she puts all six loaves of her most popular bread on a single stall, one rainy day in one town leaves customers everywhere else with nothing. A wiser baker places two loaves in each town, so a bad day anywhere only removes a third of her stock. Topology spread constraints are how you give Kubernetes that same instruction: do not just put my pods anywhere, distribute them fairly across the places that can fail independently.
1Why Spreading Pods Matters
A highly available application is not one with many copies, but one whose copies do not fail together.
Suppose you run six copies of a web service and Kubernetes places five of them on one machine. On paper you have six replicas, but in practice you have one point of failure. If that machine loses power, needs a kernel update, or runs out of memory, five sixths of your capacity vanishes in a moment. The remaining copy is suddenly asked to handle six times its normal load, and it often collapses too.
The same problem exists at a larger scale. Cloud providers divide a region into zones, which are separate data-centre buildings with independent power and networking. If all your copies sit in one zone, a zone outage takes you offline even though you paid for many replicas. The scheduler does not know your application needs protection from these events unless you tell it, and topology spread constraints are the modern, precise way to say so.
Think of a school sending children to a field trip in several buses. If every teacher rides in one bus and that bus breaks down, the other buses have no adult supervision. Putting one teacher on each bus means a breakdown never leaves a group without help. Spreading is about making sure no single breakdown removes everything important.
What the default scheduler does on its own
Out of the box, the scheduler tries to balance resource use, and it has some mild tendency to spread pods of the same group. However, it optimises for many goals at once, such as filling nodes efficiently and keeping free capacity balanced. That mild tendency gives no guarantee. A busy cluster, a new node with lots of free room, or an uneven set of node sizes can easily cause bunching, where many copies land close together.
This guide assumes you know what a pod and a node are, but every new term is explained when it first appears. If you have read about node affinity and pod anti-affinity, you will find that spread constraints solve the balancing part of the problem much more gracefully.
2Where Spread Constraints Fit in Scheduling
The scheduler filters nodes, scores the survivors, and spread constraints take part in both steps.
When a new pod needs a home, the scheduler first filters out nodes that cannot run it, for example because they lack free resources or do not match required rules. It then scores the remaining nodes and picks the best. Topology spread constraints influence both stages, depending on how strict you make them. A strict constraint removes nodes during filtering. A relaxed constraint only adds or removes points during scoring.
flowchart TD
A[New pod arrives] --> B[Filter: resources, affinity, taints]
B --> C[Filter: strict spread constraints
remove nodes that would exceed the allowed skew]
C --> D{Any node left?}
D -->|No| E[Pod stays Pending]
D -->|Yes| F[Score: resource balance and other signals]
F --> G[Score: relaxed spread constraints
favour domains that reduce imbalance]
G --> H[Best node chosen and pod bound]
Decisions are made one pod at a time
The scheduler places pods individually, looking at the cluster as it exists at that moment. It does not plan a perfect layout for all six replicas in advance. Each new pod is placed so that the balance rule is respected given the pods already running. This one-at-a-time nature is important for understanding later behaviours, such as why balance can drift after pods are removed.
People often think spread constraints keep the layout balanced forever. They do not. The rules are checked only when a pod is being placed. If pods are deleted or nodes disappear afterwards, nothing automatically shuffles the remaining pods back into balance.
3Domains and Skew: The Two Core Ideas
Everything in this feature reduces to counting pods in groups and comparing the counts.
Topology domain
A group of nodes that share the same value for a chosen node label. For example, all nodes labelled with the same zone form one domain.
Matching pods
The pods that the constraint counts, chosen by a label selector. Usually these are the copies of one application.
Skew
The imbalance measure: the number of matching pods in a domain minus the smallest number found in any eligible domain.
Maximum skew
The largest imbalance you are willing to accept. The scheduler tries to keep every domain within this limit.
A worked example with three zones
Imagine three zones named A, B, and C, and a service whose pods should be spread with a maximum skew of one. Right now zone A holds two pods, and zones B and C hold one pod each. The smallest count is one. The skew for zone A is two minus one, which is one. The skew for B and C is zero. Everything is within the limit, so the layout is acceptable.
Now a new pod arrives. If it lands in zone A, that zone would hold three pods, and its skew would become three minus one, which is two. That exceeds the limit, so zone A is not allowed. If it lands in zone B, that zone would hold two, the minimum stays at one, and the skew is one, which is fine. The scheduler therefore picks zone B or zone C. Following this simple rule, the pods naturally end up as evenly spread as the numbers allow.
graph TD
subgraph Before
A1[Zone A
2 pods
skew 1]
B1[Zone B
1 pod
skew 0]
C1[Zone C
1 pod
skew 0]
end
subgraph Options for the next pod
A2[Zone A would be 3
skew 2
rejected]
B2[Zone B would be 2
skew 1
allowed]
C2[Zone C would be 2
skew 1
allowed]
end
A1 --> A2
B1 --> B2
C1 --> C2
Dealing cards to three players is a good picture. You always give the next card to a player who has the fewest, or at least never to a player who is already ahead by more than one card. After every deal, nobody has more than one card above the person with the least.
Skew compares each domain to the emptiest eligible domain, not to the average. This is why an empty domain, such as a brand-new zone, has a big influence: while it holds zero pods, the minimum is zero, and every other domain is measured against that.
4The Fields of a Constraint at a Glance
A handful of settings describe a complete rule.
A topology spread constraint lives in the pod specification, and a pod can have several of them. Each constraint is built from a small set of fields. Some are required, and others are optional refinements added in newer Kubernetes versions. Understanding the purpose of each field first makes the deeper chapters easier to follow.
| Field | Required | What It Controls |
|---|---|---|
| maxSkew | Yes | The largest allowed difference in matching pod counts between domains |
| topologyKey | Yes | The node label that defines what a domain is, such as host name or zone |
| whenUnsatisfiable | Yes | Whether to refuse placement or to place the pod anyway when the rule cannot be met |
| labelSelector | Usually | Which pods are counted when measuring balance |
| matchLabelKeys | No | Extra label keys whose values are taken from the incoming pod to narrow the counted group, useful during rolling updates |
| minDomains | No | The minimum number of eligible domains that must exist, used with strict mode to encourage new capacity |
| nodeAffinityPolicy | No | Whether nodes excluded by the pod’s affinity or node selector are counted as domains |
| nodeTaintsPolicy | No | Whether tainted nodes that the pod cannot tolerate are counted as domains |
Think of the constraint as a sharing rule for a family. maxSkew says how unequal the portions may be. topologyKey says who counts as one person, perhaps each individual or each household. whenUnsatisfiable says whether to hold the meal until the rule can be met or to serve it anyway. labelSelector says which dishes are being shared.
In the next chapters we take each field in turn, starting with the most important, maxSkew.
5maxSkew: How Even Is Even Enough?
A single number that balances strictness against flexibility.
The maxSkew field must be a whole number greater than zero. A value of one is the tightest possible: no domain may hold more than one pod above the emptiest domain. A larger value permits more imbalance. Choosing it well is a tradeoff between evenness and the freedom of the scheduler to place pods where they fit.
| maxSkew | Meaning | Six Pods Across Three Zones | Typical Use |
|---|---|---|---|
| 1 | Tightest balance | Always two, two, two | Critical services with strict availability goals |
| 2 | Slightly relaxed | Layouts such as three, two, one are accepted | Services that tolerate small imbalance |
| 3 or more | Loose balance | Layouts such as four, one, one are accepted | Mild guidance in busy clusters |
How maxSkew behaves in each mode
When the constraint is strict, maxSkew is a hard limit: any placement that would push a domain past the limit is refused. When the constraint is relaxed, maxSkew is used as guidance: domains that would exceed it are scored lower, but they remain possible. This distinction explains why teams can use a tight maxSkew for a relaxed rule and still never see stuck pods.
Count matching pods per domain
The scheduler counts existing pods that match the selector in every eligible domain, using only the namespace of the incoming pod.
Find the global minimum
The smallest count among eligible domains becomes the baseline against which everything else is measured.
Simulate placing the pod
For each candidate node, the scheduler pretends the new pod is placed there and calculates the resulting skew of that node’s domain.
Compare with maxSkew
If the resulting skew is above the limit, the node is rejected in strict mode or penalised in relaxed mode.
Setting maxSkew to one with a strict rule on a cluster that has very uneven node counts per zone can cause Pending pods. If one zone has only a single small node that fills up, every other zone is blocked from growing too far ahead of it.
6whenUnsatisfiable: Refuse or Compromise
The field that decides whether a constraint is a hard rule or a friendly hint.
DoNotSchedule
The strict mode. A node that would break the maxSkew limit is filtered out. If no node remains, the pod stays Pending until the situation improves.
ScheduleAnyway
The relaxed mode. The scheduler still schedules the pod, but gives higher priority to placements that reduce the imbalance. The pod is never blocked by this rule.
DoNotSchedule is a seating rule at a formal dinner: nobody is seated until the table plan is respected, even if guests wait outside. ScheduleAnyway is a friendly host who tries to mix guests nicely but will never leave someone standing in the hallway just to keep the plan perfect.
flowchart TD
A[Pod needs a node] --> B{Constraint mode}
B -->|DoNotSchedule| C[Remove nodes that break maxSkew]
C --> D{Any node left?}
D -->|Yes| E[Schedule normally]
D -->|No| F[Pod stays Pending]
B -->|ScheduleAnyway| G[Keep all nodes
score better balance higher]
G --> H[Pod always gets scheduled]
Which one should you choose?
Use DoNotSchedule when spreading is a correctness or safety requirement, for example when losing one zone must never remove more than a third of capacity. Use ScheduleAnyway when spreading is a quality improvement that must not compromise availability, for example in a cluster that autoscales and sometimes has only a few nodes. Many mature teams combine both: a strict rule at the zone level for the major protection and a relaxed rule at the host level for tidiness.
Advantages of strict mode
- A firm guarantee about the maximum concentration in any domain
- Predictable behaviour during zone failures
- Makes capacity gaps visible as Pending pods
- Can trigger the cluster autoscaler to add nodes in the needed zone
Costs of strict mode
- Pods may wait when one domain is full or unhealthy
- Rollouts can stall if surge pods cannot be balanced
- Needs capacity planning in every domain
- Misconfiguration turns into an availability problem
7topologyKey: Choosing What a Domain Is
The label you name decides which failures your spreading protects against.
The topologyKey is the name of a node label. Nodes that carry the same value for that label are grouped into one domain. Pick the host name label, and every node is its own domain, so you spread across machines. Pick the zone label, and every zone is a domain, so you spread across data-centre buildings. Pick the region label, and you spread across geographic regions. The key defines the size of the failure you want to survive.
| Topology Key | Domain Is | Protects Against | Watch Out For |
|---|---|---|---|
| Host name | One node | Single machine failure or maintenance | Many domains, so more counting work and possible tiny groups |
| Zone | One availability zone | Loss of a data-centre building | Uneven node counts per zone |
| Region | One geographic region | Loss of a whole region | Cross-region latency and data transfer costs |
| Custom label such as rack | One rack or hardware group | Shared power or network switch failure | Labels must be added and kept accurate by administrators |
What happens to nodes without the label
Only nodes that carry the topologyKey label take part. A node that lacks the label is skipped when domains are counted and cannot be chosen by a constraint that uses that key. If some nodes are missing the label by accident, they silently fall out of the calculation, and your layout may not be as even as you believe. Always check that every relevant node has the label.
graph TD
subgraph Zone A
N1[node-1]
N2[node-2]
end
subgraph Zone B
N3[node-3]
end
subgraph Zone C
N4[node-4]
N5[node-5]
N6[node-6]
end
Z[Key zone gives 3 domains
A has 2 nodes, B has 1, C has 3]
H[Key host name gives 6 domains
one per node]
Notice that zone B has one node while zone C has three. Spreading pods evenly by zone means zone B’s single node will hold as many pods as three nodes in zone C together. Make sure the smaller zone has enough resources, or its node will become the bottleneck for the entire service.
8labelSelector and matchLabelKeys: What Gets Counted
A constraint only balances the pods you tell it to look at.
The labelSelector picks which existing pods count towards the balance. Almost always you select the pods of the same application, so that the copies of that application are spread among themselves. Only pods in the same namespace as the incoming pod are considered. Pods in other namespaces are invisible to the calculation, even if they carry the same labels.
Too broad a selector
If the selector matches pods from several different applications, the constraint balances the total mix rather than each application. One application can end up concentrated in a single zone while the combined count still looks even.
Too narrow a selector
If the selector accidentally matches nothing, the constraint has no pods to compare, and it has little or no effect. A typo in a label is enough to switch off your protection without any warning.
The right selector
Match exactly the labels that identify the copies of this application, and no others. Reuse the same labels that the Deployment uses to select its own pods, so the two always agree.
The rolling update problem and matchLabelKeys
During a rolling update, old and new versions of the pods exist together. If the selector matches both, the scheduler counts them as one group. That sounds harmless, but it can produce a poor layout: the new version might end up concentrated in one zone because the old version fills the others. When the rollout completes and the old pods disappear, the new pods are left unbalanced.
The optional matchLabelKeys field solves this. You list label keys, and the scheduler reads their values from the incoming pod and adds them to the selector. By listing the label that identifies the revision of a Deployment, each version is balanced on its own. The result is that the final set of new pods is properly spread, independent of what the old pods looked like.
sequenceDiagram
participant D as Deployment rollout
participant S as Scheduler
participant Z as Zones A B C
D->>S: Create pod of new revision
S->>Z: Count only pods with the same revision label
Z-->>S: New revision counts per zone
S->>S: Apply maxSkew to new revision counts
S->>Z: Place pod in the least loaded zone for this revision
Note over Z: Old revision pods do not distort the new layout
If your cluster runs a version of Kubernetes that supports matchLabelKeys, using it with the revision label is a simple way to make every rollout land in a well-spread final state. Check your version’s documentation to confirm support.
9minDomains and Autoscaling
Encouraging the cluster to grow into the shape you want.
Suppose you want your pods spread across at least three zones, but your cluster currently has nodes in only two. With a plain constraint, the scheduler sees two domains, balances between them, and everything looks fine. You have quietly lost the third-zone protection you wanted. The minDomains field lets you state the minimum number of domains you expect to exist.
When the number of eligible domains is lower than minDomains, the scheduler treats the global minimum count as zero. That makes the skew of any domain that already holds pods large enough to block further placement in strict mode. Pods stay Pending, and if a cluster autoscaler is running, those Pending pods signal that a new node in a new zone would help. This field only works together with the strict mode.
A festival organiser says, “We will not open the gates until at least three food stalls are ready.” Even if two stalls are ready and hungry visitors are waiting, the rule forces the organiser to bring in the third stall, rather than being content with two.
Enough domains exist
If the count of eligible domains meets or exceeds minDomains, the field has no effect and the normal skew calculation applies.
Too few domains
The baseline is treated as zero, so pods beyond the allowed skew wait. This produces a visible signal that capacity in a new domain is needed.
Relaxed mode
The field is meant for strict mode only. With the relaxed mode there is no blocking, so there is nothing for the field to enforce.
If the cluster cannot actually grow a new domain, for example because no autoscaler exists or the zone has no capacity, a high minDomains value leaves pods Pending indefinitely. Use it only where you have a real plan for adding domains.
10Node Inclusion Policies: Which Nodes Count
Two settings decide whether nodes the pod cannot use still take part in the balance.
Not every node in a cluster is a valid home for a given pod. Some are excluded by the pod’s node selector or node affinity, and some are tainted so the pod is not welcome. A subtle question arises: should such nodes and their domains be counted when calculating the minimum and the skew? Two optional fields answer this.
nodeAffinityPolicy
With Honor, which is the default, nodes that do not match the pod’s node selector or required node affinity are left out of the calculation. With Ignore, they are counted anyway.
nodeTaintsPolicy
With Ignore, which is the default, tainted nodes are counted even when the pod cannot tolerate the taint. With Honor, nodes the pod cannot tolerate are left out.
| Policy | Default | Effect of the Default | Risk |
|---|---|---|---|
| nodeAffinityPolicy | Honor | Only nodes that the pod could actually use are counted | Rarely surprising; matches intuition |
| nodeTaintsPolicy | Ignore | Domains that contain only tainted, unusable nodes still count as empty | Zero-count domains keep the minimum at zero and can block pods |
The tainted-zone surprise
Consider a zone where every node is tainted for a special workload, and your pod does not tolerate that taint. With the default taints policy, that zone still counts as a domain with zero pods. The global minimum is therefore zero forever, because your pod can never go there. In strict mode this can stop the other zones from growing beyond the allowed skew, and pods stay Pending even though plenty of usable capacity exists elsewhere. Setting nodeTaintsPolicy to Honor removes such unusable domains from the count and cures the problem.
Imagine sharing sweets among classes, but one class is locked in an exam hall and cannot receive any. If you count that class as empty, you will refuse to give more sweets to the others, since they are ahead of the empty class by too much. The fair approach is to leave the locked class out of the count.
If your cluster contains dedicated or tainted node groups that some workloads cannot use, review the taints policy on any strict constraint those workloads carry.
11Multiple Constraints and Cluster Defaults
Several rules can apply to one pod at the same time, and Kubernetes provides sensible defaults.
A pod may carry more than one topology spread constraint, for example one for zones and one for host names. The scheduler treats them together with a logical AND: a node must satisfy every strict constraint to remain a candidate. Relaxed constraints each add to the scoring. This combination is the standard pattern for protecting against both zone failures and machine failures at once.
flowchart TD
P[Incoming pod] --> C1[Constraint 1
zone, maxSkew 1, DoNotSchedule]
P --> C2[Constraint 2
host name, maxSkew 1, ScheduleAnyway]
C1 --> F[Nodes in zones that keep zone skew within 1]
C2 --> S[Score nodes that keep host skew low]
F --> W[Candidate nodes]
S --> W
W --> B[Best scoring candidate wins]
Cluster-level default constraints
If a pod defines no spread constraints of its own, and it belongs to a Service, ReplicaSet, StatefulSet, or similar group, the scheduler applies built-in default constraints. These are relaxed rules: one across host names with a maximum skew of three, and one across zones with a maximum skew of five. They give a gentle nudge toward spreading without ever blocking a pod. Cluster administrators can replace these defaults with their own through the scheduler configuration.
Host name spreading
Relaxed, with a generous maximum skew. Pods of the same group prefer different nodes but are never refused for it.
Zone spreading
Relaxed, with an even more generous limit. It gives a light preference for using more than one zone.
Pod-level constraints
Any constraint defined directly on a pod replaces the cluster defaults for that pod. You do not get both.
Teams sometimes believe the defaults give them strong high availability. They do not: the limits are loose and never block scheduling. Critical services should state their own explicit constraints.
12Spread Constraints vs Other Placement Tools
Choosing between spreading, anti-affinity, affinity, and taints.
Kubernetes offers several tools that influence placement. Spread constraints overlap most with pod anti-affinity, since both can be used to keep replicas apart. The difference is in the promise they make. Anti-affinity says “not together with these pods.” Spread constraints say “keep the counts in each domain close to each other.” That difference in wording has large practical consequences.
| Aspect | Topology Spread Constraints | Pod Anti-Affinity |
|---|---|---|
| Goal | Even distribution across domains | Keep matching pods out of the same domain |
| More replicas than domains | Works fine, pods double up evenly | Required form leaves extra pods Pending |
| Tunable tolerance | Yes, through maxSkew | No, it is all or nothing per domain |
| Cost in large clusters | Lower | Higher |
| Can attract pods together | No | No, that is pod affinity |
| Best for | Balanced replicas of one application | Keeping two specific workloads apart |
| Tool | Question It Answers |
|---|---|
| Node affinity | What kind of node should this pod run on? |
| Pod affinity | Which other pods should this pod sit near? |
| Pod anti-affinity | Which other pods should this pod stay away from? |
| Topology spread constraints | How evenly should my copies be distributed? |
| Taints and tolerations | Which pods are allowed onto these nodes at all? |
Anti-affinity is a rule that says “no two siblings in the same classroom.” With more siblings than classrooms, someone has no class to go to. A spread constraint says “keep the number of siblings in each classroom about equal,” which still works when there are more siblings than classrooms.
The tools are complementary. A common combination is node affinity to pick the right pool of machines, taints to reserve that pool, and topology spread constraints to distribute the pods evenly across zones inside the pool.
13When to Use Topology Spread Constraints
Everyday situations where a spreading rule pays for itself.
Stateless web services and APIs
The most common case. Copies are spread over zones and machines so one failure removes only a fraction of the capacity, and traffic continues to flow to the survivors.
Ingress controllers and gateways
These are entry points for all traffic. Spreading them over zones and nodes makes sure that the front door of your platform does not fail because one node was drained.
Cluster-critical add-ons
Services such as internal name resolution or metrics collectors benefit from spreading, because their loss affects every application in the cluster.
Replicated data stores
Members of a replicated database or cache spread across zones so that quorum survives the loss of a zone. For very strict one-member-per-machine needs, anti-affinity may still be added.
Batch and parallel processing
Large numbers of worker pods spread across nodes avoid overloading a single machine and finish sooner, because they do not compete for the same disks and network cards.
Cost and capacity balancing
Spreading also helps use capacity evenly across node groups, which reduces hot spots and makes autoscaling more predictable.
14Rollouts, Scale-Downs, and Failures
Balance is created at placement time, and real events can disturb it afterwards.
Rolling updates
During a rolling update, surge pods must satisfy strict constraints just like any other pod. If a zone is full and the constraint blocks other zones from getting further ahead, the surge pod waits, and the rollout pauses. Using matchLabelKeys with the revision label keeps each version balanced separately, and leaving a little spare capacity in every zone keeps rollouts moving smoothly.
Scaling down
When you reduce the number of replicas, the controller chooses which pods to delete. It has its own ordering rules, and they do not guarantee that the remaining pods stay balanced across domains. A scale-down from nine to six may leave you with a layout such as three, three, zero instead of two, two, two. The constraint will only matter again when new pods are placed.
Node and zone failures
When a node fails, its pods are recreated and each new pod is placed according to the constraints. If a whole zone is down and the constraint is strict with a small maxSkew, replacement pods may be blocked from piling into the surviving zones, and they wait until the zone recovers. This is exactly the behaviour some teams want for stability, and exactly what other teams must avoid by choosing a relaxed mode or a larger maxSkew.
sequenceDiagram
participant Z as Zone C
participant K as Controller
participant S as Scheduler
participant A as Zones A and B
Z-->>K: Zone C goes down, pods lost
K->>S: Create replacement pods
S->>A: Check skew with zone C counted as a domain with zero
alt Strict mode and small maxSkew
S-->>K: Replacement pods stay Pending
else Relaxed mode or larger maxSkew
S->>A: Place replacement pods in zones A and B
end
Decide in advance what you want during a zone outage: full recovery on the remaining zones, or protection of the surviving zones from overload. Your choice of strict or relaxed mode is that decision written down.
A separate tool called the descheduler can evict pods that violate spread constraints so they are placed again in a better layout. It is not part of the core scheduler and must be installed and configured separately.
15Performance and Scale Considerations
Spread constraints are cheaper than anti-affinity but not free.
For each pod, the scheduler must count matching pods per domain and simulate the effect of every candidate placement. Constraints on the host name label create as many domains as there are nodes, which means more counting in very large clusters. Constraints on zones create only a handful of domains and are inexpensive. Broad selectors that match many pods increase the counting work too.
Advantages
- Lower scheduling cost than pod anti-affinity at large scale
- Works with more replicas than domains without special handling
- Tunable tolerance through maxSkew
- Cluster defaults give a free baseline of spreading
Disadvantages / Trade-offs
- Counting cost still grows with node count for host-level rules
- Balance is only enforced at placement, not afterwards
- Several constraints together can be hard to reason about
- Uneven domain sizes can produce hot small domains
In a large cluster, prefer a strict zone rule and a relaxed host rule. The zone rule is cheap and delivers the major protection, while the relaxed host rule adds tidiness without blocking pods.
16Security and Multi-Tenancy Considerations
Placement rules affect capacity, and capacity is shared.
Topology spread constraints are not a security boundary, but they do interact with security and fairness in a shared cluster. Because the rules live in the pod specification, anyone who can create pods can write them. In a multi-tenant cluster, misuse can waste capacity or block other teams’ workloads, and node labels that feed the rules deserve protection.
Protect the topology labels
If someone can change zone or rack labels on nodes, they can change how domains are formed and therefore where pods are placed. Restrict who can edit node labels and use the protected label prefix for labels that matter to isolation.
Limit aggressive strict rules through policy
A team that sets a very strict rule with a large minDomains value can create permanently Pending pods and noisy alerts. Admission policies can require sensible ranges for maxSkew and restrict the use of minDomains.
Remember the namespace boundary
Spread counts only look at pods in the same namespace. A tenant cannot see or be affected by the pod counts of another namespace through this feature, which is useful for isolation but also means teams must not expect global balance across namespaces.
Spread does not equal isolation
Spreading pods across nodes does not stop different tenants from sharing a node. Use taints, dedicated node pools, or stronger runtime isolation when tenants must not share machines.
17Troubleshooting, Monitoring, and Metrics
Most spread problems show up as Pending pods or a layout that looks lopsided.
When a strict constraint cannot be satisfied, the scheduler records an event on the pod saying that a number of nodes did not match the pod topology spread constraints. That message is your starting point. Combined with a view of how many matching pods run in each domain, it usually reveals the cause within minutes.
Read the scheduling event
Look for the count of nodes rejected because of the spread constraints, alongside counts rejected for other reasons such as resources or taints.
Count pods per domain
List the matching pods with their nodes and group them by the topology label. Compare the counts with the maxSkew you set.
Check for empty or unusable domains
A domain with zero pods that the pod cannot use, for example because of taints, keeps the minimum at zero and blocks others. Review the node inclusion policies.
Verify labels and selectors
Confirm that every node has the topology label and that the selector matches the intended pods, not too many and not too few.
Check capacity in the smallest domain
The smallest or fullest domain often sets the pace for the whole service. Add nodes there, or relax the rule.
Pods per zone per service
A chart of replica counts by zone shows drift immediately, especially after scale-downs and node failures.
Pending pods over time
Alert on pods that remain Pending for several minutes, since strict constraints turn capacity gaps into Pending pods.
Scheduling latency
Rising placement times can reveal heavy host-level constraints in a growing cluster.
Node utilisation per domain
Uneven utilisation between zones shows where the spread rule is pushing more pods than a small zone can hold comfortably.
18Design Patterns for Spreading
Proven combinations you can adapt to your own services.
Pattern: Strict zones, relaxed nodes
Use a strict zone constraint with a maximum skew of one and a relaxed host constraint. You get a firm zone-level guarantee and a best-effort spread within each zone, with no risk of blocking on small node counts.
Pattern: Relaxed everywhere for elastic clusters
In clusters that scale to very few nodes at quiet times, use relaxed rules at both levels. Pods always run, and balance improves naturally as the cluster grows.
Pattern: Grow into the shape you want
Combine a strict zone rule with minDomains equal to the number of zones you need. Pending pods then act as a request for the autoscaler to create nodes in the missing zone.
Pattern: Revision-aware spreading
Include the revision label through matchLabelKeys so that every rollout ends in a balanced layout, regardless of how the previous version was placed.
Pattern: Spread plus dedicated pool
Use node affinity and a taint to select a dedicated pool, and add a spread constraint with the taints policy set to honor unusable nodes, so the count includes only zones the workload can really use.
Pattern: Spread plus targeted anti-affinity
Use spread constraints for even distribution of replicas, and add a narrow anti-affinity rule only for a pair of specific workloads that must never share a node.
19Anti-patterns to Avoid
Habits that lead to stuck pods, false confidence, and confusing layouts.
Problem
Using a strict rule with maxSkew of one on a cluster with very uneven zones.
Why It’s Harmful
A small zone that fills up blocks all other zones from growing, and pods stay Pending while capacity sits unused elsewhere.
Correct Approach
Balance node counts across zones, raise maxSkew, or use the relaxed mode for the host level rule.
Problem
A label selector that matches nothing or matches too much.
Why It’s Harmful
The constraint quietly stops protecting your application, or balances a mix of unrelated pods, giving false confidence.
Correct Approach
Reuse the same labels that identify the application’s own pods, and verify with a count per domain after deployment.
Problem
Ignoring tainted node groups when using strict constraints.
Why It’s Harmful
Domains the pod can never use still count as empty by default, keeping the minimum at zero and blocking placement in the usable zones.
Correct Approach
Set the taints policy to honor, or design taints so that they do not remove whole domains from usable capacity.
Problem
Assuming balance is maintained after scale-downs and failures.
Why It’s Harmful
Layouts drift, and the service may end up concentrated in one zone without anyone noticing until an outage.
Correct Approach
Monitor pods per domain and use a descheduler or planned restarts to correct imbalance.
Problem
Setting a high minDomains without any way to add domains.
Why It’s Harmful
Pods wait forever for a zone that never appears, and the service cannot scale.
Correct Approach
Use minDomains only where an autoscaler or an operations process can create nodes in the required domains.
Problem
Relying on default constraints for critical services.
Why It’s Harmful
The defaults are loose and never block scheduling, so a critical service can still end up concentrated.
Correct Approach
Define explicit constraints matching the level of protection each service needs.
20Best Practices and Common Mistakes
A compact checklist for safe and predictable spreading.
Best Practices
- Use a zone-level rule for the big protection and a host-level rule for tidiness
- Choose strict or relaxed mode deliberately, based on outage behaviour you want
- Keep selectors exact and reuse the application’s own labels
- Ensure every node carries the topology labels you rely on
- Keep spare capacity in each domain for rollouts and failures
- Use matchLabelKeys with the revision label where supported
- Monitor pods per domain, not only total replicas
Common Mistakes
- Confusing maxSkew with a maximum number of pods per domain
- Forgetting that only same-namespace pods are counted
- Expecting rebalancing after pods are removed
- Leaving tainted or unusable domains inside the count
- Copying constraints between clusters with different zone layouts
- Using strict rules with no capacity plan
maxSkew is a limit on the difference between domains, not on the number of pods in a domain. A skew of one with a hundred pods per zone is perfectly valid, as long as the zones stay within one pod of each other.
21Real-World Scenarios
How teams apply spread constraints in production.
A streaming service protecting its API tier
A video platform runs its API across three zones with a strict zone constraint at a maximum skew of one and a relaxed host constraint. When a zone suffers a network fault, only a third of capacity is affected. The team has capacity headroom in each zone, so the surviving zones absorb the load with a modest CPU increase.
A retailer growing into a third zone
A retailer started with two zones and later wanted three. By adding minDomains of three to the strict rule, new pods that could not be balanced waited, which prompted the autoscaler to create nodes in the third zone. Within minutes, the service had the intended layout.
A bank with tainted compliance nodes
A bank keeps one zone tainted for a regulated workload. General services began stalling because the zone counted as empty. Setting the taints policy to honor removed that zone from the calculation, and the services scaled normally in the remaining zones.
A platform team fixing lopsided rollouts
After each release, one service ended with most pods in one zone because old pods had occupied the others. Adding the revision label through matchLabelKeys made each rollout balance its own pods, and lopsided layouts stopped appearing.
A data team spreading batch workers
A data-processing team spreads hundreds of worker pods across nodes with a relaxed host-level constraint. Jobs finish faster because workers no longer compete for the same disks, and a sudden node loss removes only a small share of workers.
22Frequently Asked Questions
Skew is the number of matching pods in a domain minus the smallest number of matching pods in any eligible domain. The scheduler makes sure this difference stays within maxSkew.
DoNotSchedule is strict: nodes that would exceed maxSkew are removed, and the pod may stay Pending. ScheduleAnyway is relaxed: the pod is always scheduled, but placements that reduce imbalance score higher.
Yes. Unlike required pod anti-affinity, spread constraints simply keep the counts in each zone close together, so extra replicas double up evenly.
No. The rules apply only when a pod is scheduled. A separate descheduler component can evict pods that violate constraints so that they are placed again.
No. Only matching pods in the same namespace as the incoming pod are counted.
They are ignored by that constraint. They are not counted as a domain and cannot be selected for a pod that uses that key, so make sure the label is present on every relevant node.
A strict constraint left no node that keeps the skew within the limit. Common causes are a full or small zone, a tainted domain counted as empty, a high minDomains value, or a selector that counts more pods than expected.
It sets the minimum number of eligible domains you expect. If fewer exist, the baseline count is treated as zero, so strict mode blocks pods beyond the allowed skew and signals that capacity in a new domain is needed.
Surge pods must satisfy strict rules like any other pod. If the selector matches both old and new versions, the new version may end up unbalanced. Using matchLabelKeys with the revision label balances each version independently.
Yes. For pods that belong to a Service or similar controller and define no constraints, the scheduler applies relaxed defaults across host names and zones. They give a gentle nudge and never block scheduling.
For balancing replicas of one application, spread constraints are usually simpler, cheaper, and more flexible. Use anti-affinity to keep two specific workloads apart or to demand strictly one pod per domain.
No. It limits the difference between domains. Each domain can hold many pods as long as the domains stay within the allowed difference of each other.
23Summary and Key Takeaways
Topology spread constraints let you describe how evenly copies of an application should be distributed across nodes, zones, regions, or any other grouping you define with node labels. The scheduler measures balance with skew, the difference between a domain and the emptiest eligible domain, and keeps it within your maxSkew. Whether the rule blocks placement or only guides it is decided by the whenUnsatisfiable setting.
The feature is powerful because it is flexible, but its behaviour depends on details: which pods are counted, which nodes count as domains, how tainted or excluded nodes are treated, and when the rules are applied. Balance is created at placement time and can drift afterwards, so monitoring and occasional correction matter. Used with care, spreading turns a pile of replicas into a truly resilient service.
Key Takeaways
- Skew is the core measure — it compares each domain’s matching pod count to the smallest count among eligible domains, and maxSkew caps that difference.
- Strict versus relaxed is a deliberate choice — DoNotSchedule can leave pods Pending, while ScheduleAnyway only nudges placement and never blocks.
- The topology key defines the failure you survive — host name protects against machine loss, zone against building loss, and region against geographic loss.
- Selectors and namespaces decide what is counted — only matching pods in the same namespace count, so an inaccurate selector silently weakens the protection.
- Watch nodes that should not count — use the node inclusion policies, especially the taints policy, so that unusable domains do not keep the minimum at zero.
- Balance is not maintained after placement — scale-downs, failures, and label changes can cause drift, so monitor pods per domain and use a descheduler when needed.
- Prefer spread constraints for balancing replicas — they are cheaper and more flexible than anti-affinity, and work well combined with node affinity, taints, and targeted anti-affinity.