Shared Kubernetes clusters often need reserved nodes for workloads that ordinary Pods should not use. A taint marks a node as something the scheduler should repel, while a toleration in a Pod spec names the exception that is allowed through.
A NoSchedule taint blocks new scheduler placements that do not tolerate the taint. It does not evict Pods that were already running, and it does not attract matching Pods by itself. Pair a toleration with nodeSelector or node affinity when a workload must land on the reserved nodes.
In a real cluster, worker-2 is any node chosen for reserved workloads, nodepool=reserved is any scheduling label, and dedicated=reserved:NoSchedule is any taint key, value, and effect used consistently by the workload. Run from a kubectl context that can update nodes and create test Pods.
$ kubectl get nodes NAME STATUS ROLES AGE VERSION control-plane Ready control-plane 6d v1.36.1 worker-1 Ready <none> 6d v1.36.1 worker-2 Ready <none> 6d v1.36.1
$ kubectl label node worker-2 nodepool=reserved --overwrite node/worker-2 labeled
The label selects the reserved node. The toleration only permits scheduling onto a tainted node; it does not force the scheduler to choose that node.
$ kubectl taint node worker-2 dedicated=reserved:NoSchedule node/worker-2 tainted
Remove the reservation later with kubectl taint node worker-2 dedicated:NoSchedule- only after ordinary workloads may schedule there again.
$ kubectl describe node worker-2
Name: worker-2
Roles: <none>
Labels: kubernetes.io/hostname=worker-2
kubernetes.io/os=linux
nodepool=reserved
Taints: dedicated=reserved:NoSchedule
Unschedulable: false
Conditions:
Type Status Reason
Ready True KubeletReady
##### snipped #####
$ kubectl create namespace taint-demo namespace/taint-demo created
Use a temporary namespace for proof Pods so cleanup does not affect application workloads.
Related: How to create a Kubernetes namespace
apiVersion: v1 kind: Pod metadata: name: untolerated namespace: taint-demo spec: nodeSelector: nodepool: reserved containers: - name: pause image: registry.k8s.io/pause:3.10
The nodeSelector makes the scheduler consider the reserved node, and the missing tolerations field should keep the Pod unscheduled.
$ kubectl apply -f without-toleration.yaml pod/untolerated created
$ kubectl get pod untolerated --namespace taint-demo -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES untolerated 0/1 Pending 0 8s <none> <none> <none> <none>
$ kubectl events --namespace taint-demo --for pod/untolerated --types=Warning LAST SEEN TYPE REASON OBJECT MESSAGE 8s Warning FailedScheduling Pod/untolerated 0/3 nodes are available: 1 node(s) had untolerated taint(s), 2 node(s) didn't match Pod's node affinity/selector. no new claims to deallocate, preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
The warning should mention the custom taint or a broader untolerated-taint message for the reserved node. Other scheduler blockers can appear in the same row when the Pod also uses node selection.
Related: How to check Kubernetes events
Tool: Kubernetes Events Timeline Analyzer
apiVersion: v1 kind: Pod metadata: name: tolerated namespace: taint-demo spec: nodeSelector: nodepool: reserved tolerations: - key: dedicated operator: Equal value: reserved effect: NoSchedule containers: - name: pause image: registry.k8s.io/pause:3.10
operator: Equal requires the key, value, and effect to match the node taint. Use operator: Exists when any value for the same key and effect should be accepted.
$ kubectl apply -f with-toleration.yaml pod/tolerated created
$ kubectl wait pod/tolerated --namespace taint-demo --for=condition=Ready --timeout=180s pod/tolerated condition met
$ kubectl get pods --namespace taint-demo -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES tolerated 1/1 Running 0 1s 10.244.1.2 worker-2 <none> <none> untolerated 0/1 Pending 0 9s <none> <none> <none> <none>
$ kubectl delete namespace taint-demo namespace "taint-demo" deleted
Delete only the temporary namespace used for proof Pods. Keep the node label and taint when the reserved-node policy should remain active.