Skip to main content

Node affinity and selectors in practice

Inspect 1.36

Once your pools carry , you can steer pods onto them. Use nodeSelector for the simple case, and node affinity when you need conditions. The one rule that trips people up: the pod's selector must match the label's exact key and value.

nodeSelector for the simple case#

nodeSelector pins a pod to nodes that carry every label listed:

yaml
		spec:
  nodeSelector:
    node-role.kubernetes.io/backend: ""
	
yaml
		spec:
  nodeSelector:
    autopilot.syself.com/machine-type: baremetal
	

Match the exact key and empty-string value#

Role labels under node-role.kubernetes.io/ use an empty-string value (""), not "true". A selector for "true" does not match a node labelled "":

yaml
		# Node label set by the pool:   node-role.kubernetes.io/backend: ""
nodeSelector:
  node-role.kubernetes.io/backend: "true"
  node-role.kubernetes.io/backend: ""
	
Note

Only labels in the node-role.kubernetes.io, node-restriction.kubernetes.io, and node.cluster.x-k8s.io domains reach the node from a pool definition. Any other label is silently dropped, so a selector for it never matches. See for the full rule.

Combine pool labels with system labels#

Syself also sets labels on every node before your pool's labels land. You can select on these without declaring them anywhere:

Label Value Where
autopilot.syself.com/machine-type baremetal or hcloud every node
instance.hetzner.cloud/is-root-server "true" bare-metal nodes
node.kubernetes.io/instance-type for example cpx42 every node
topology.kubernetes.io/region location on cloud, network zone on bare metal every node
topology.kubernetes.io/zone datacenter on cloud, location on bare metal every node
autopilot.syself.com/gpu "true" GPU nodes
kubernetes.io/hostname the node's hostname every node

nodeSelector requires every listed label to match. Combine one of your pool's labels with a system label to narrow further. This pod schedules only on a node that is in the backend pool and bare metal:

yaml
		spec:
  nodeSelector:
    node-role.kubernetes.io/backend: ""
    autopilot.syself.com/machine-type: baremetal
	

See for the full list.

Need rules, not just labels#

nodeSelector matches labels and stops there. When you need a hard-or-soft choice, matching operators like In or Exists, or placement relative to other pods, use node affinity and pod anti-affinity. Both live on , which owns required-versus-preferred node affinity and one-replica-per-node anti-affinity for databases and quorum members. To spread replicas evenly as your node count changes, reach for instead.

The one thing that stays on this page: affinity rules read the same node labels a nodeSelector does, so the exact-key gotcha above and the system-label table apply to them too.

Why unmatched pods go elsewhere#

A selector or affinity rule attracts. It does not repel. A pod that sets no selector can still land on your labelled nodes. A pod whose preferred affinity cannot be met just schedules somewhere else.