Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Use nodeSelector for Pod Placement

Learn how to use nodeSelector for pod placement in Kubernetes. This lesson covers the core concept, step-by-step setup, hands-on exercise, troubleshooting, and what to study next.

Focus: use nodeSelector for pod placement

Sponsored

You've deployed a pod, but it lands on a node that's overloaded, running on spot instances, or even in a different region — causing latency, cost overruns, or outright failures. That's the problem nodeSelector solves: it's a simple, declarative way to tell Kubernetes "this pod must run only on nodes that have a specific label." Unlike complex affinity rules, nodeSelector is straightforward to understand and perfect for beginners who need predictable pod placement without diving into advanced scheduling logic.

The problem this lesson solves

Kubernetes schedules pods onto nodes based on resource requests and default algorithms — but it has no idea where you want the pod to go. Without nodeSelector, your database pod might end up on a regular worker node instead of a dedicated SSD-backed node, or your GPU-accelerated ML training job could land on a CPU-only machine. This unpredictability leads to:

  • Performance issues: Pods needing fast storage or specialized hardware may fail or run slowly.
  • Cost inefficiency: Expensive GPU nodes might sit idle while GPU workloads run on standard nodes.
  • Compliance risks: Workloads requiring data residency or security isolation may be placed in the wrong cluster zone.

nodeSelector solves this by giving you simple label-based control — you tag nodes with descriptive labels (e.g., disk=ssd, tier=ml) and then tell each pod which label it requires.

Core concept / mental model

Think of nodeSelector as a bouncer at a club. Each node has a set of "passes" (labels), and each pod submits a "VIP list" (a nodeSelector map). The scheduler checks if the node has all the labels the pod requires; if not, the pod is rejected from that node and stays pending.

Key definitions:

  • Node labels: Key-value pairs attached to a node object, e.g., kubernetes.io/os=linux, topology.kubernetes.io/region=us-east-1, or custom tags like size=large.
  • nodeSelector: A map[string]string field in the pod spec. Only nodes with matching labels (all key-value pairs) are considered eligible.
  • Scheduling: The scheduler's first filter is node predicatenodeSelector is evaluated here. If no nodes match, the pod stays Pending with a FailedScheduling event.

Pro tip: nodeSelector uses exact matching — both key and value must match. There's no partial or regex support. For advanced matching (like In, NotIn, Exists), use node affinity (future lesson).

How it works step by step

1. Label your nodes

First, add labels to the nodes that should host specific workloads. This is typically done by cluster admins or via infrastructure-as-code.

# See current node labels
kubectl get nodes --show-labels

# Add a label to a specific node
kubectl label nodes node1 disk=ssd
kubectl label nodes node2 gpu=true

2. Add nodeSelector to your pod spec

Edit your pod or deployment YAML to include nodeSelector under spec:

apiVersion: v1
kind: Pod
metadata:
  name: my-ssd-pod
spec:
  containers:
  - name: app
    image: nginx
  nodeSelector:
    disk: ssd

This pod will only run on nodes that have the label disk=ssd. If a node has disk=ssd and other labels, it's still eligible — the pod doesn't require those other labels.

3. Multiple selectors

You can require multiple labels; all must match:

nodeSelector:
  disk: ssd
  gpu: "true"          # Note: YAML booleans should be strings in nodeSelector values
  tier: ml

Only nodes that have all three labels will be selected.

Hands-on walkthrough

Let's create a multi-node cluster (if you only have Minikube or a single-node cluster, label the single node and observe scheduling).

Step 1: Label a node

kubectl label nodes minikube disk=hdd
kubectl label nodes minikube env=dev

Step 2: Create a pod targeting SSD

Create ssd-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: ssd-pod
spec:
  containers:
  - name: app
    image: nginx
  nodeSelector:
    disk: ssd

Apply and check:

kubectl apply -f ssd-pod.yaml
kubectl get pod ssd-pod -o wide

Expected output (pending):

NAME      READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
ssd-pod   0/1     Pending   0          10s   <none>   <none>   <none>           <none>

The pod stays Pending because no node has disk=ssd. Let's fix that:

kubectl label nodes minikube disk=ssd
kubectl get pod ssd-pod -o wide

Expected output (scheduled):

NAME      READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
ssd-pod   1/1     Running   0          5s    10.244.0.5   minikube   <none>           <none>

What happened? The scheduler re-evaluated the pending pod (it's requeued periodically). Once the label existed, the node matched and the pod was scheduled.

Step 3: Verify in describe

kubectl describe pod ssd-pod | grep -A5 Events

Look for Successfully assigned or FailedScheduling.

Compare options / when to choose what

Feature nodeSelector Node Affinity (requiredDuringScheduling) Pod Anti-Affinity
Matching logic Exact key=value Operators: In, NotIn, Exists, Gt, Lt Same as node affinity but for spreading
Multiple selectors AND (all must match) AND across terms, OR inside expressions Complex topology constraints
Complexity Simple string map More YAML, but flexible Advanced
Use case Quick hardware pinning, environment segregation Fine-grained scheduling, multi-condition High availability, zone awareness

When to choose nodeSelector:

  • You're a beginner and need simple label-based rules.
  • You only have a few distinct node types (e.g., disk=ssd, gpu=true).
  • You don't need NotIn or Exists semantics.
  • Your workload doesn't require dynamic re-evaluation (nodeSelector is evaluated once at scheduling time).

Pro tip: For production, prefer node affinity over nodeSelector because it's more expressive and avoids unneeded string casts. But nodeSelector is faster to write and understand.

Troubleshooting & edge cases

Pod stays Pending indefinitely

Check node labels:

kubectl get nodes --show-labels | grep <expected-label>
kubectl describe pod <pod-name> | grep -A10 Events

If you see FailedScheduling with 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector, you have a label mismatch.

Case sensitivity

Node labels are case-sensitive. Disk=SSD is different from disk:ssd and disk:SSD. Always verify the exact key-value via kubectl describe node <name>.

Multiple labels not matching

If you specify:

nodeSelector:
  disk: ssd
  gpu: "true"

And the node has disk=ssd but no gpu=true, the pod won't schedule. Remove the unmatched label from the spec or add it to the node.

Common mistake: YAML parsing of values like true or false. Always quote them as strings ("true") because nodeSelector values are strings, not booleans.

NodeSelector and taints/tolerations

nodeSelector and taints/tolerations work independently. A pod with nodeSelector: {disk: ssd} on a node that also has a taint will not be scheduled unless the pod has a matching toleration. If you need both, combine them.

What you learned & what's next

You now know how to use nodeSelector for pod placement — from labeling nodes, writing the selector in YAML, to troubleshooting pending pods. You covered:

  • Why nodeSelector is essential for hardware and environment constraints.
  • The mental model of label-based scheduling.
  • Step-by-step commands to label and schedule.
  • A complete hands-on exercise with a pending-to-running example.
  • How nodeSelector compares to node affinity (your next advanced topic).

Next lesson: Node Affinity — you'll learn how to use In, NotIn, Exists operators to make scheduling rules even more powerful. You'll also see how to set soft (preferred) scheduling hints, not just hard constraints.

Ready to master advanced pod placement? Let's go!

Practice recap

Try this mini-exercise: Create a new node label size=small and a pod with nodeSelector: {size: small}. Then, change the label to size=medium and observe the pod — does it move? (Hint: nodeSelector is required during scheduling, not preferred.) Finally, add a second label zone=us-east and update the pod spec to require both. Verify scheduling success.

Common mistakes

  • Forgetting to label the target node before creating the pod — the scheduler can't match a label that doesn't exist, causing the pod to stay Pending until the label is added.
  • Using unquoted boolean or numeric values in nodeSelector (e.g., gpu: true instead of gpu: "true") — YAML interprets true as a boolean, but nodeSelector expects a string, leading to silent failures or errors.
  • Assuming nodeSelector supports OR logic across multiple keys — it only does AND (all must match). For OR, you need node affinity with multiple expressions.
  • Overwriting existing labels when using kubectl label without --overwrite — if a label key already exists, the command fails unless --overwrite is used.

Variations

  1. Node affinity with requiredDuringSchedulingIgnoredDuringExecution — offers operators like In, NotIn, Exists for more flexible matching.
  2. Node selector terms in multi-arch deployments — combine beta.kubernetes.io/arch=amd64 with beta.kubernetes.io/os=linux to pin pods to specific architectures.
  3. Using custom labels for environment segregation (e.g., env=prod, env=staging) — ensures development pods never land on production nodes.

Real-world use cases

  • Pin GPU-training pods to nodes labeled gpu=true to avoid wasting expensive GPU capacity on CPU-bound workloads.
  • Ensure database pods run only on nodes with SSD storage by using nodeSelector: {disk: ssd} for consistent I/O performance.
  • Isolate tenant workloads in a shared cluster by labeling nodes with tenant=team-a and using nodeSelector to prevent cross-tenant pod placement.

Key takeaways

  • nodeSelector is a simple, exact-match filter that tells the scheduler which nodes a pod can run on.
  • Node labels must be added manually (or via automation) before nodeSelector can work — always verify with kubectl get nodes --show-labels.
  • Multiple nodeSelector keys act as AND conditions; all labels must match on the node.
  • Pending pods due to missing labels are automatically scheduled when the label is added — no manual restart needed.
  • For advanced matching (OR, NotIn, Exists), use node affinity instead of nodeSelector.
  • Watch for YAML type coercion — always quote values that look like booleans or numbers.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.