2026
Kubernetes Node Affinity: Steering Pods to the Right Nodes
The scheduler places pods on nodes with enough capacity. When the node itself matters, you need node affinity. It lets you express hard requirements and soft preferences using label matching operators that go well beyond what a simple nodeSelector can do.
The Kubernetes scheduler does a good job of placing pods on nodes that have enough capacity. Left entirely to its own devices, it spreads work across the cluster reasonably well. For many workloads, that is all you need. But clusters are rarely uniform. Some nodes have GPUs. Some are in a specific availability zone. Some run on cheaper spot instances. Some are reserved for particular teams.
When the placement decision matters, you need a way to express that. The blunt tool is a nodeSelector: a map of labels that the node must have. It works but it only supports equality matching. Either the label has exactly this value or the pod does not go there. That covers simple cases but falls short when you need to express anything more nuanced.
Node affinity is the expressive version of nodeSelector. It supports multiple operators, multiple terms that can be combined with logical conditions and a distinction between hard requirements that must be satisfied and soft preferences that the scheduler tries to honour but will compromise on. It gives you real control over where pods land without having to pin them to specific node names.
Required versus preferred
Node affinity has two modes that serve fundamentally different purposes. Choosing the wrong one is the most common source of unexpected scheduling behaviour.
requiredDuringSchedulingIgnoredDuringExecution
A hard requirement. The scheduler will only place the pod on a node that satisfies every expression in this block. If no node matches, the pod stays in Pending indefinitely. Use this when the constraint is non-negotiable: a pod that needs GPU hardware or a specific availability zone where its data lives.
preferredDuringSchedulingIgnoredDuringExecution
A soft preference. The scheduler tries to honour it but will place the pod on a non-matching node if no matching node has capacity. Each preference carries a weight between one and one hundred. The scheduler scores all candidate nodes and picks the highest score. Multiple preferences stack. Use this for optimisations rather than hard requirements.
Worth noting
Both types say IgnoredDuringExecution. A future type called RequiredDuringSchedulingRequiredDuringExecution has been discussed in the Kubernetes community for years. It would evict pods from nodes that no longer satisfy their affinity rules. It does not exist in stable Kubernetes yet. For now, affinity is only enforced at scheduling time, not enforced continuously while the pod runs.
What a node affinity rule looks like
Affinity rules live in the pod spec under the affinity field. The structure has more nesting than nodeSelector but the extra depth is where the expressiveness comes from.
Required affinity — GPU nodes in eu-west-1 only
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- eu-west-1a
- eu-west-1b
- key: accelerator
operator: Exists
The nodeSelectorTerms array uses OR logic between items. A node that satisfies any one term qualifies. Inside each term,matchExpressions uses AND logic. A node must satisfy every expression in the list. In the example above, a node must be in one of the two zones AND must have the accelerator label. Multiple terms would let you express: zone A with accelerators OR zone B with accelerators.
Preferred affinity — spot nodes when available
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: node.kubernetes.io/capacity-type
operator: In
values:
- spot
- weight: 20
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- eu-west-1a
The weight values are relative. The scheduler adds up the weights of all satisfied preferences for each candidate node and scores them accordingly. A spot node in eu-west-1a would score 100. A spot node in a different zone would score 80. An on-demand node in eu-west-1a would score 20. An on-demand node in any other zone would score zero but could still receive the pod if it has the most available capacity.
Matching operators
Each expression in a node affinity rule uses one of six operators. Choosing the right one makes rules both more readable and more correct.
InThe node label value must be one of the listed values. The most common operator for matching a set of allowed zones or instance types.
NotInThe node label value must not match any of the listed values. Used to exclude specific nodes or regions from consideration.
ExistsThe label key must be present on the node. The value does not matter. Useful for matching nodes that have been tagged as having a capability without caring about a specific version.
DoesNotExistThe label key must not be present on the node. The inverse of Exists. Matches nodes that have not been tagged with a particular label at all.
GtThe node label value must be greater than the specified integer. Used for numeric labels such as storage capacity or generation numbers.
LtThe node label value must be less than the specified integer. The counterpart to Gt for numeric comparisons.
Common patterns
Node affinity tends to appear in the same handful of situations. Most production clusters with heterogeneous node pools will use at least one of these.
Zone spreading
A stateful service that should only run in a specific availability zone uses a required affinity matching the zone label. If the zone goes down, the pod does not silently migrate to another zone where its storage might not be accessible.
Hardware targeting
A machine learning workload that needs GPU nodes uses a required affinity matching the accelerator label. Without this, the scheduler might place the pod on a CPU-only node where the container will fail to start.
Cost optimisation
A batch job that can tolerate running on spot or preemptible instances uses a preferred affinity for those node pools. It will land on cheaper nodes when they are available but fall back to on-demand nodes when they are not.
Architecture matching
A cluster with a mix of ARM and x86 nodes uses node affinity to direct container images that are only built for one architecture. Without it, a pod might land on an incompatible node and fail to start with a cryptic exec error.
Node affinity versus nodeSelector
Both mechanisms steer pods toward nodes with specific labels. The difference is in what you can express. A nodeSelectoris a flat map. Every key-value pair must match exactly. There is no way to say "any one of these values" or "this label must not be present." Node affinity adds operators that make those expressions possible.
The other meaningful difference is the required versus preferred distinction. A nodeSelector is always a hard requirement. There is no equivalent of a preferred rule. If the label is not present, the pod does not schedule. Node affinity lets you express a preference that degrades gracefully instead of blocking scheduling entirely.
The Kubernetes documentation treats nodeSelector as a simpler predecessor. New workloads that need any kind of node targeting should use node affinity. The nodeSelector field still works and is not deprecated, but it cannot be extended to express anything beyond exact label matches.
Things that go wrong
Node affinity problems usually show up as pods stuck in Pending or pods landing on nodes that seem wrong. Both are usually a labelling issue.
Labels not applied to nodes
Node affinity rules match against node labels. If the labels you are matching against do not exist on the nodes, the rule either causes the pod to stay Pending forever (required) or is silently ignored (preferred). Always verify that the expected labels are present with kubectl get nodes --show-labels before writing affinity rules that depend on them.
Confusing affinity with anti-affinity
Node affinity attracts pods to nodes. Pod anti-affinity repels pods away from other pods. The two are separate concepts with separate fields. A common mistake is trying to use node affinity to spread pods across nodes, which is actually a job for pod topology spread constraints.
IgnoredDuringExecution misconception
Both affinity types say IgnoredDuringExecution. This means if a node's labels change after a pod is already running there, the pod is not evicted. The affinity is only evaluated at scheduling time. A pod placed on a matching node stays there even if the node later loses the label that qualified it.
Overly specific required rules
A required affinity that matches only one or two nodes in the whole cluster creates a single point of failure for scheduling. If those nodes are full or unavailable, the pod cannot be placed anywhere. For most workloads, preferred rules with fallback behaviour are more resilient than hard requirements on narrow node sets.
Where it fits
Node affinity sits within the broader set of scheduling controls Kubernetes provides. It is the mechanism for expressing constraints about the node itself: what hardware it has, what zone it is in, what pool it belongs to. It does not say anything about the relationship between pods.
For constraints between pods, the related mechanism is pod affinity and pod anti-affinity. Pod affinity attracts pods to nodes that are already running certain other pods. Pod anti-affinity repels pods away from nodes running certain others. A web server that wants to be co-located with its cache uses pod affinity. A set of replicas that should spread across failure domains uses pod anti-affinity. The two families of controls address different questions: where is this node versus what else is running near me.
Topology spread constraints are the newer mechanism for spreading pods evenly across zones and nodes. They replaced the spreading use case that people were previously handling with pod anti-affinity. When the goal is even distribution rather than attraction or repulsion, topology spread constraints express it more clearly. Node affinity remains the right tool when the constraint is about the node's own properties: its hardware, its location, its role in the cluster.