Node affinity and selectors in practice
Once your pools carry labels , you can steer pods onto them. Use nodeSelector for the simple case, and node affinity when you need conditions. 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:
spec:
nodeSelector:
node-role.kubernetes.io/backend: ""
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 "":
# 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 Label nodes and assign roles 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:
spec:
nodeSelector:
node-role.kubernetes.io/backend: ""
autopilot.syself.com/machine-type: baremetal
See Node labels and annotations 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, see Set affinity and anti-affinity . To spread replicas evenly as your node count changes, see topology spread constraints .
Affinity rules read the same node labels a nodeSelector does, so the exact-key rule and the system-label table above 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.