14

Taints and Tolerations

Video: Day 14/40 — Taints and Tolerations in Kubernetes • https://www.youtube.com/watch?v=nwoS2tK2s6Q • Duration: ~26 min

Key terms

TermMeaning
TaintA node mark that repels pods
TolerationA pod's permission to land on a tainted node
NoScheduleEffect blocking new pods
PreferNoScheduleSoft "avoid if possible" effect
NoExecuteEffect that also evicts non-tolerating pods
tolerationSecondsGrace time before NoExecute eviction
key/value/effectThe three parts of a taint

Problem & solution

By default the scheduler can place any pod on any node, but some nodes are special (GPU, dedicated, control-plane) and should repel general workloads unless a pod explicitly opts in.

Solution: Taint nodes to repel pods, and add matching tolerations to the pods allowed to run there, to dedicate nodes.

The analogy

A busy port marks certain docks with a "hazmat berth, permit required" sign so ordinary ships steer clear, and only a vessel carrying the matching permit is waved in. A Kubernetes taint is that sign painted on a node, and a toleration is the permit a pod carries to be allowed onto it. A ship with no permit is turned away, just as a pod with no matching toleration stays Pending elsewhere.

Where this fits in the cluster

Taints live on nodes; tolerations live on pods. The scheduler reads both to decide placement. This is a node-level admission gate.

The idea

Taints repel pods from nodes. Tolerations let specific pods stick anyway. It's the opposite of attraction — taints push pods AWAY unless they tolerate it.

Mnemonic: Taint = the bouncer on the node. Toleration = the VIP pass on the pod. Note: a toleration allows placement; it does not force it (that's affinity).

Taint a node

You apply a taint to a node with kubectl taint, and remove it by repeating the command with a trailing minus.

kubectl taint nodes cka-worker gpu=true:NoSchedule
kubectl describe node cka-worker | grep -i taint

# remove a taint (trailing minus)
kubectl taint nodes cka-worker gpu=true:NoSchedule-

Taint format:

   key = value : effect
   gpu = true  : NoSchedule

The 3 taint effects

The effect decides how harshly the taint treats pods that don't tolerate it, ranging from soft avoidance to outright eviction.

   NoSchedule        -> new pods without toleration are NOT placed here
   PreferNoSchedule  -> soft; avoid if possible, but allowed if needed
   NoExecute         -> also EVICTS already-running pods that don't tolerate

Toleration on a pod

A toleration in the pod spec must match the node's taint key, value, and effect for the pod to be allowed onto that node.

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  tolerations:
    - key: "gpu"
      operator: "Equal"
      value: "true"
      effect: "NoSchedule"
  containers:
    - name: app
      image: nginx

operator:

   Equal   -> key, value, and effect must all match
   Exists  -> key + effect match; value ignored (tolerate any value)

NoExecute extra field: tolerationSeconds

For NoExecute taints, tolerationSeconds lets a tolerating pod linger for a set time before it is finally evicted.

    - key: "node.kubernetes.io/not-ready"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 300     # stay 5 min after taint, then evict

Why control-plane nodes run no normal pods

Control-plane nodes carry a built-in taint that keeps ordinary workloads off the master unless they explicitly tolerate it.

   kubectl describe node <control-plane> | grep Taints
   -> node-role.kubernetes.io/control-plane:NoSchedule
   This taint keeps your workloads off the master by default.

Taints/Tolerations vs Affinity (don't confuse them)

These solve opposite problems: taints repel pods from a node, while affinity attracts a pod toward nodes.

   Taint/Toleration -> NODE repels pods   (pod needs permission to land)
   Node Affinity    -> POD attracts nodes (pod prefers/requires nodes)  [Day 15]
   Best practice: combine both to truly dedicate nodes.

End-to-end example: dedicate a GPU node

Taint a node so only GPU workloads land there, then deploy a pod that tolerates it. A plain pod is rejected; the tolerating pod is admitted.

# 1) taint the node
kubectl taint nodes cka-gpu gpu=true:NoSchedule

# 2) a plain pod will NOT land on cka-gpu
kubectl run plain --image=nginx

# 3) a tolerating pod is allowed
kubectl apply -f gpu-pod.yaml
kubectl get pods -o wide                 # gpu-pod on cka-gpu, plain elsewhere
kubectl describe node cka-gpu | grep -i taint
# gpu-pod.yaml
apiVersion: v1
kind: Pod
metadata: { name: gpu-pod }
spec:
  tolerations:
    - { key: "gpu", operator: "Equal", value: "true", effect: "NoSchedule" }
  containers:
    - name: app
      image: nginx

To make the node exclusively GPU (force the pod onto it, not just allow it), add node affinity too — see Day 15's "dedicated node" pattern.

End-to-end flow

The scheduler matches a node's taints against a pod's tolerations to decide placement, and NoExecute evicts pods that do not tolerate.

Key takeaways

  • Taint a node to repel pods; add a matching toleration to a pod to allow it.
  • Effects: NoSchedule, PreferNoSchedule (soft), NoExecute (evicts).
  • Toleration only permits, it does not attract — pair with affinity.

Checklist

  • [ ] Tainted a node and saw a pod fail to schedule
  • [ ] Added a matching toleration and saw it schedule
  • [ ] Tested NoExecute evicting a running pod
  • [ ] Inspected the control-plane node's default taint