Sign in
← Kubernetes: scheduling
6 min · medium · Lesson 5 of 7

Taints and tolerations: who gets onto a node

How a node fences off Pods that don't belong, how NoSchedule, PreferNoSchedule and NoExecute differ, and what to write in tolerations.

How a node refuses Pods it isn't expecting on its own, how a Pod gets permission to land on it, and how a taint evicts Pods that are already running.

Why this matters

nodeSelector and nodeAffinity solve the problem from the Pod's side: "I want this kind of node." There's also the reverse problem. Some nodes have GPUs, some are for the database, and one gets taken down for maintenance tomorrow. You can't let just anyone land on them, and adding a selector to every unrelated Deployment isn't an option. That's what a taint on a node is for.

What taints are

node-1 · taint workload=stateful db · with toleration node-2 · no taint web api
A toleration only lifts the ban. It can't pull a Pod toward a node.

A taint is a mark on a node in the format key=value:effect. When choosing a node, the scheduler reads its taints and checks them against the Pod's tolerations. If even one taint isn't covered by a toleration, the node is dropped from the candidates, and the Pod won't land on it.

This is the inverse of nodeSelector. There, the Pod picks a node by labels; here, the node filters Pods. A taint means "don't let anyone in except those who explicitly said they're ready." That's how dedicated nodes work: a GPU pool, nodes for system Pods, isolated workloads.

An admin sets taints with the kubectl taint command. A developer writes tolerations in the Pod spec. The link runs both ways: the admin says "only workload=stateful goes here," and the developer adds the matching toleration to the workloads that need it.

Three effects

NoExecute is what evacuation from failed nodes runs on. When a node stops responding, the control plane puts the taint node.kubernetes.io/not-ready or node.kubernetes.io/unreachable on it with effect NoExecute. Every Pod automatically gets tolerations for these two taints at creation time, with tolerationSeconds: 300. That's why Pods leave a failed node after about five minutes, not instantly.

Minimal toleration YAML

spec:
  tolerations:
    - key: "workload"
      operator: "Equal"
      value: "stateful"
      effect: "NoSchedule"

Field by field:

A toleration covers a taint when the key and effect match, and the value matches too with Equal (with Exists, the value isn't compared).

Operator: Equal and Exists

Equal requires the value to match. Exists catches any taint with the right key. If you set a value with Exists, the API rejects the manifest. The "tolerate everything" template:

tolerations:
  - operator: "Exists"

No key, no effect. A toleration like this matches any taint. It's used on system DaemonSets (a controller that keeps one Pod per node): the network plugin, kube-proxy, node-exporter, a log collector. They need to run on every node, including the control plane, which usually carries the taint node-role.kubernetes.io/control-plane:NoSchedule.

Forget a toleration like this on your own DaemonSet, and node-exporter silently doesn't start on the control plane. There's no error, just one fewer node in DESIRED.

The admin sets and removes taints

kubectl taint nodes node-1 workload=stateful:NoSchedule
kubectl taint nodes node-1 workload-
kubectl describe node node-1 | grep -A2 Taints

The second command removes every taint with the key workload from the node, whatever its effect. To remove one specific taint: kubectl taint nodes node-1 workload=stateful:NoSchedule-. You can change the value of an existing taint only with the --overwrite flag.

In a single-node cluster, a taint is especially easy to see. Mark the node NoSchedule, and any new Pod without a toleration sits in Pending, because the scheduler has no other candidates. In the Pod's events it looks like this:

Warning  FailedScheduling  ...  0/1 nodes are available: 1 node(s) had untolerated taint(s). ...

A single-node kind cluster doesn't put a taint on the control plane, otherwise no ordinary Pod would land there at all. You can check this with kubectl describe node: the Taints line will show <none>.

Common mistakes

kubectl taint nodes <name> <key>=<value>:<effect>
kubectl taint nodes <name> <key>-
kubectl describe node <name> | grep -A3 Taints
kubectl get node <name> -o jsonpath='{.spec.taints}'
kubectl get pod <name> -o jsonpath='{.spec.tolerations}'
kubectl explain pod.spec.tolerations
kubectl explain node.spec.taints

What you'll need for the tasks

Tasks are in the labs/ folder: each lab brings up its own kind cluster on your computer, with instructions in its README.md.

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-05-taints-tolerations