Skip to main content

Command Palette

Search for a command to run...

K8S-Schedule: Taints and Tolerations

Updated
4 min read

Intro

In Kubernetes, Taints and Tolerations provide a mechanism to control Pod placement by restricting which Pods can be scheduled on specific Nodes. Unlike Node Affinity, which expresses preferences, Taints enforce restrictions by preventing unwanted Pods from running on designated Nodes unless they have the appropriate Tolerations.

What Are Taints?

A Taint is applied to a Node and acts as a repelling force that prevents certain Pods from being scheduled onto it. Each Taint consists of:

  • A key (e.g., dedicated)

  • A value (e.g., gpu)

  • An effect, which determines how the Taint behaves:

    • NoSchedule – Prevents scheduling unless the Pod has a matching Toleration.

    • PreferNoSchedule – Tries to avoid scheduling Pods on the Node, but does not enforce it strictly.

    • NoExecute – Evicts running Pods that do not have a matching Toleration.

What Are Tolerations?

A Toleration allows a Pod to "tolerate" a Node’s Taint, permitting it to be scheduled there despite the restriction. However, a Toleration alone does not force scheduling onto a tainted Node—it simply allows the Pod to bypass the restriction if necessary.

Why Use Taints and Tolerations?

Taints and Tolerations are commonly used in scenarios such as:

  • Dedicated Nodes – Preventing general workloads from running on Nodes reserved for specific tasks (e.g., GPU workloads).

  • Node Maintenance – Temporarily evicting Pods from a Node for updates or repairs.

  • High-Priority Workloads – Ensuring that only critical applications run on specific Nodes while blocking lower-priority workloads.

By effectively using Taints and Tolerations, Kubernetes administrators can fine-tune scheduling, improve resource management, and ensure workloads are deployed in the right locations within a cluster. In the next section, we will explore how to apply Taints and Tolerations with practical examples.

Demo

In this demo, we will explore Taints and Tolerations by:

  1. Applying a Taint to a Node to restrict scheduling.

  2. Creating a Pod without a Toleration (which should not get scheduled).

  3. Creating a Pod with a Toleration (which should get scheduled on the tainted Node).


Apply a Taint to a Node

Now, let's taint all nodes so that only Pods with a matching Toleration can be scheduled on it.

$ kubectl taint nodes kind-control-plane dedicated=high-priority:NoSchedule
node/kind-control-plane tainted

$ kubectl taint nodes kind-worker dedicated=high-priority:NoSchedule
node/kind-worker tainted

$ kubectl taint nodes kind-worker2 dedicated=high-priority:NoSchedule
node/kind-worker2 tainted

Explanation of the Taint:

  • dedicated=high-priority: The key-value pair for the Taint.

  • NoSchedule: Prevents scheduling unless the Pod has a Toleration.

Verify that the Taint was applied:

$ kubectl describe node kind-control-plane | grep Taint
Taints:             dedicated=high-priority:NoSchedule

$ kubectl describe node kind-worker | grep Taint
Taints:             dedicated=high-priority:NoSchedule

$ kubectl describe node kind-worker2 | grep Taint
Taints:             dedicated=high-priority:NoSchedule

Deploy a Pod Without a Toleration (Expected to Fail)

Let's create a Pod without a Toleration and see if it gets scheduled.

apiVersion: v1
kind: Pod
metadata:
  name: no-toleration-pod
spec:
  containers:
    - name: nginx-container
      image: nginx-k8s:latest
      imagePullPolicy: Never

Apply the Pod and check the status of the pod:

# Check pod status
$ kubectl get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
no-toleration-pod   0/1     Pending   0          5s    <none>   <none>   <none>           <none>

# Describe pod
$ kubectl describe pod no-toleration-pod
Name:             no-toleration-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             <none>
Labels:           <none>
Annotations:      <none>
Status:           Pending
IP:
IPs:              <none>
Containers:
  nginx-container:
    Image:        nginx-k8s:latest
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-65sds (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  kube-api-access-65sds:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  19s   default-scheduler  0/3 nodes are available: 3 node(s) had untolerated taint {dedicated: high-priority}. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.

From the describe message we can see that: the Pod remains in Pending state because it is not allowed to run on any pods.

Deploy a Pod With a Matching Toleration

Now, let's create a Pod with a Toleration that allows it to be scheduled on any nodes in my cluster, because I tainted them all with the same taint.

apiVersion: v1
kind: Pod
metadata:
  name: toleration-pod
spec:
  tolerations:
    - key: "dedicated"
      operator: "Equal"
      value: "high-priority"
      effect: "NoSchedule"
  containers:
    - name: nginx-container
      image: nginx-k8s:latest
      imagePullPolicy: Never

Apply the file and check if the pod is scheduled:

$ kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE    IP            NODE           NOMINATED NODE   READINESS GATES
toleration-pod   1/1     Running   0          3m8s   10.244.1.10   kind-worker2   <none>           <none>

Remove the Taint

If you want to remove the Taint from the Node:

$ kubectl taint nodes kind-control-plane dedicated=high-priority:NoSchedule-
node/kind-control-plane untainted

$ kubectl taint nodes kind-worker dedicated=high-priority:NoSchedule-
node/kind-worker untainted

$ kubectl taint nodes kind-worker2 dedicated=high-priority:NoSchedule-
node/kind-worker2 untainted

This removes the Taint, allowing all Pods to be scheduled normally.

Conclusion

In this article, we learned how to:

  • Apply a Taint to restrict Pod scheduling.

  • Create a Pod without a Toleration, which remains pending.

  • Create a Pod with a Toleration, which gets scheduled on the tainted Node.

  • Remove the Taint to restore normal scheduling.

11 views

More from this blog

Clarence's Blog

56 posts

I share insights on programming, web development, cloud computing, computer networks, and AI, alongside financial knowledge, reading notes, and reflections on business and entrepreneurship.