Two pods that should never co-locate keep landing on the same node — what's wrong with the anti-affinity rule?
Short Answer
The almost-certain cause is that the rule uses preferredDuringSchedulingIgnoredDuringExecution (a soft preference the scheduler tries to honor but can override under pressure) instead of requiredDuringSchedulingIgnoredDuringExecution (a hard constraint the scheduler will never violate) — "preferred" is often chosen by default or copy-pasted from an example without realizing it's not actually a guarantee, and under node-capacity pressure the scheduler will co-locate pods rather than leave one Pending.
Detailed Explanation
Kubernetes affinity rules come in two enforcement strengths that are easy to conflate, and the difference between them is exactly the difference between "this is a hard guarantee" and "this is a best-effort hint the scheduler can and will override when it has to."
Symptoms
- A
podAntiAffinityrule exists on the Deployment, intended to spread replicas across different nodes. kubectl get pods -o wideshows two or more replicas actually running on the same node.- No scheduling error or warning is visible — the pods scheduled successfully, just not the way intended.
Possible Causes
- The rule uses
preferredDuringSchedulingIgnoredDuringExecutionrather thanrequiredDuringSchedulingIgnoredDuringExecution— "preferred" allows the scheduler to violate it under capacity pressure rather than leave a pod unschedulable. - The
topologyKeyused doesn't actually mean what was intended (e.g., using a zone-level topology key when node-level separation was the actual goal). - The
labelSelectorin the anti-affinity rule doesn't actually match the intended pods (a label mismatch, or matching too broad or too narrow a set).
Investigation Steps
Check which affinity type is actually configured: kubectl get deployment <name> -o yaml — look specifically at whether the rule sits under preferredDuringSchedulingIgnoredDuringExecution or requiredDuringSchedulingIgnoredDuringExecution in spec.template.spec.affinity.podAntiAffinity — this single check resolves the most common cause immediately.
Check the topologyKey value against what was actually intended: a rule using topologyKey: topology.kubernetes.io/zone spreads pods across zones but says nothing about individual nodes within a zone — two pods in the same zone but on different nodes would satisfy a zone-level rule while still not achieving node-level separation, if node-level was actually the goal. Confirm topologyKey: kubernetes.io/hostname is used if the intent is genuinely per-node separation.
Verify the labelSelector actually matches the intended pods: kubectl get pods --show-labels compared against the anti-affinity rule's labelSelector.matchLabels/matchExpressions confirms the rule is actually being evaluated against the pods it's meant to apply to — a mismatched selector means the rule silently applies to the wrong (or no) pods, evaluating successfully but accomplishing nothing.
Check cluster capacity at the time of scheduling, if required was already in use: if the rule genuinely is required and co-location still happened, check whether there simply wasn't enough spare capacity elsewhere at the time — a required anti-affinity rule that can't be satisfied results in a Pending pod, not a violated constraint, so if pods are actually co-located with a required rule in place, that points to one of the other causes (selector or topology key), not a capacity issue.
Resolution
Switch the rule to requiredDuringSchedulingIgnoredDuringExecution if a hard guarantee is genuinely needed, understanding the trade-off that this can leave a pod Pending if the cluster doesn't have enough separately-schedulable capacity at that moment — which is the correct behavior for an availability-critical guarantee (better to be Pending and visible than silently co-located). Correct the topologyKey or labelSelector if either was the actual mismatch. Confirm the fix by watching a fresh rollout and verifying kubectl get pods -o wide shows genuine node-level separation.
Interview Follow-Up Questions
- What's the trade-off of using `required` anti-affinity for an availability-critical workload, given it can leave pods `Pending` during a capacity crunch?
- How would you design pod topology spread constraints to keep a Deployment's replicas evenly distributed across availability zones, as an alternative to anti-affinity for this use case?
- How would you test that an anti-affinity rule actually works as intended, before relying on it for a production availability guarantee?
Key Takeaways
preferredDuringSchedulingIgnoredDuringExecutionis a soft hint the scheduler can override under pressure — it's not a guarantee, despite often looking like one at a glance.requiredDuringSchedulingIgnoredDuringExecutionis the actual hard constraint, at the cost of potentially leaving a podPendingif it can't be satisfied.topologyKeydetermines what "same location" actually means (node, zone, region) — a mismatch between the intended and configured topology level produces a rule that's technically satisfied but doesn't achieve the actual goal.- A mismatched
labelSelectormakes a rule evaluate successfully against the wrong (or no) pods, silently accomplishing nothing.
References
Related Questions
- What's the practical difference between node affinity and a plain node selector, given affinity seems strictly more powerful?SuggestedBeginner
- Why might mounting the same ReadWriteOnce PVC work for two pods on some clusters but fail on others?SuggestedAdvanced
- How would you dedicate a set of nodes exclusively to one team's workloads, while still letting that team's pods run elsewhere too?SuggestedAdvanced
Last updated August 22, 2026 · Last reviewed August 22, 2026