Sign in
← Kubernetes: scheduling
5 min · easy · Lesson 3 of 7

Node Selectors: target a Pod at the right node

How labels on nodes and spec.nodeSelector split the cluster by purpose, and why a typo sends the Pod to Pending.

How to send a Pod only to nodes with the properties you need, what happens when no such node exists, and where to look for the cause.

Why this matters

Nodes in a cluster are rarely identical. Some have GPUs for ML, some have fast SSDs for databases, some are cheap spot instances for batch jobs. You need a way to tell the scheduler: "only place this Pod on nodes like these." The simplest way is nodeSelector.

What nodeSelector is

node-1 · metadata.labels disk: ssd tier: production Pod · spec.nodeSelector disk: ssd tier: production lines match
The scheduler keeps only the nodes that have both lines from the nodeSelector.

nodeSelector is the simplest Pod placement mechanism. You write a map of labels in the spec, and the Pod can only land on nodes that have those labels set. It works like a label selector in a Service, but it targets nodes.

You set labels on nodes, write a nodeSelector in each Pod, and the scheduler picks a matching node on its own.

Unlike nodeName, which pins a Pod to a node by name, nodeSelector is declarative: "I want any node with these properties." If three nodes match, the scheduler picks any one of them, factoring in resources. If none match, the Pod sits in Pending and waits.

Minimal YAML

apiVersion: v1
kind: Pod
metadata:
  name: db
spec:
  nodeSelector:
    disk: ssd
    tier: production
  containers:
    - name: db
      image: postgres:16.4-alpine

All keys in nodeSelector combine with AND. The node needs both disk=ssd and tier=production at the same time. If the node has only disk=ssd, the Pod stays Pending.

In a Deployment, nodeSelector goes into the Pod template, that is spec.template.spec.nodeSelector. The field spec.nodeSelector at the Deployment's own level doesn't exist.

Labels on nodes

Labels are set with kubectl label nodes:

kubectl label nodes node-1 disk=ssd
kubectl label nodes node-1 tier=production
kubectl get nodes --show-labels
kubectl get nodes -L disk -L tier

-L prints the label's value as its own column, which makes nodes easier to compare:

NAME     STATUS   ROLES           AGE   VERSION   DISK   TIER
node-1   Ready    control-plane   6m    v1.35.0   ssd    production

Every node has built-in labels, set by kubelet: kubernetes.io/hostname, kubernetes.io/os, kubernetes.io/arch. You can select nodes by these too, which is handy on clusters with both amd64 and arm64 nodes. In the cloud, you also get node.kubernetes.io/instance-type and topology.kubernetes.io/zone, but the cloud provider fills those in. A local cluster doesn't have them.

How it works

Even on a single-node cluster you can see the whole mechanism. With the right label the Pod starts, and the moment you put a value in the selector that the node doesn't have, the Pod goes to Pending.

The cause shows up in kubectl describe pod, in the Events block:

Warning  FailedScheduling  12s  default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. ...

That means the node is missing that label, or there's a typo in the key or the value.

nodeSelector, nodeName, and nodeAffinity

In production, teams use either nodeSelector, when plain equality is enough, or nodeAffinity, when they need preferences or more complex logic.

kubectl label nodes <name> disk=ssd
kubectl label nodes <name> disk=nvme --overwrite   # change the value
kubectl label nodes <name> disk-                    # remove the label
kubectl get nodes --show-labels
kubectl get nodes -l disk=ssd
kubectl get nodes -L disk
kubectl describe pod <name>                         # Events block, if Pending
kubectl explain pod.spec.nodeSelector

Without --overwrite, kubectl refuses to change a value that's already set.

Common mistakes

What you'll need for the tasks

The tasks live in the labs/ folder: each lab brings up its own kind cluster on your machine, instructions in its README.md.

Homework

Practice the theory on your own cluster: 1 step with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.

./run.sh start k8s-node-selectors     # set up the starting state
./run.sh verify k8s-node-selectors    # check the current step

In short

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson k8s-scheduling-03-node-selectors