Kubernetes
6 min readMay 25, 2026Updated August 19, 2026

Fix Kubernetes Pending Pods: Pod Stuck and Never Scheduled

AJ
Ajeet Yadav
Platform & Cloud Engineer
Fix Kubernetes Pending Pods: Pod Stuck and Never Scheduled

Quick answer

A pod stuck in Pending means the scheduler can't find a node to place it on. The reason is always in the pod events — insufficient resources, affinity rules, taints, or an unbound PVC.

6 min read · Kubernetes

Fix Kubernetes Pending Pods: Pod Stuck and Never Scheduled

NAME        READY   STATUS    RESTARTS   AGE
api-7d9f8   0/1     Pending   0          10m

A pod in Pending state hasn't been assigned to a node yet. The container hasn't started — the scheduler is looking for a node that satisfies the pod's requirements and can't find one.

Unlike CrashLoopBackOff or ImagePullBackOff, Pending is a scheduler problem, not a runtime problem. The cause is always in the pod events.


Step 1: Read the scheduling failure reason

bash
kubectl describe pod <pod-name>

Scroll to the Events section:

Events:
  Warning  FailedScheduling  30s  default-scheduler  0/3 nodes are available: 3 Insufficient cpu.

The scheduler's message tells you exactly why it can't place the pod. Match it below.


Cause 1: Insufficient CPU or memory — Insufficient cpu / Insufficient memory

0/3 nodes are available: 3 Insufficient cpu.
0/3 nodes are available: 2 Insufficient memory, 1 node(s) had untolerated taint.

The sum of requests for all pods already scheduled on the nodes exceeds available capacity. The scheduler uses requests, not limits, for placement decisions.

bash
# See how much is allocatable on each node
kubectl describe nodes | grep -A5 "Allocatable:"

# See how much is already requested (allocated)
kubectl describe nodes | grep -A10 "Allocated resources:"

Fix options:

  1. Lower the pod's resource requests if they're over-specified:
yaml
resources:
  requests:
    cpu: "100m"      # Lower this if the app doesn't need 1 full CPU
    memory: "128Mi"
  1. Add more nodes — if the cluster is genuinely full, scale up (or let Cluster Autoscaler do it if configured)

  2. Free up space — delete unused pods, evict non-critical workloads, or resize existing Deployments


Cause 2: Node selector or required affinity has no match

0/3 nodes are available: 3 node(s) didn't match Pod's node affinity/selector.

The pod requires a node with specific labels, but no node has them.

bash
1# Check the pod's nodeSelector or affinity
2kubectl get pod <pod-name> -o jsonpath='{.spec.nodeSelector}'
3kubectl get pod <pod-name> -o yaml | grep -A20 affinity
4
5# Check what labels your nodes actually have
6kubectl get nodes --show-labels
7kubectl describe node <node-name> | grep Labels

Fix: Either add the required label to a node, or correct the nodeSelector/affinity in the pod spec.

bash
# Add a label to a node
kubectl label node <node-name> disktype=ssd
yaml
1# Or relax the affinity from required to preferred
2affinity:
3  nodeAffinity:
4    preferredDuringSchedulingIgnoredDuringExecution:   # preferred, not required
5      - weight: 1
6        preference:
7          matchExpressions:
8            - key: disktype
9              operator: In
10              values: ["ssd"]

Cause 3: Taint not tolerated

0/3 nodes are available: 3 node(s) had untolerated taint {key: value: effect: NoSchedule}.

Nodes have taints that the pod doesn't tolerate. Common taints: node.kubernetes.io/not-ready, node-role.kubernetes.io/control-plane, or custom taints for dedicated node groups.

bash
# See node taints
kubectl describe node <node-name> | grep Taint
# Taints: dedicated=gpu:NoSchedule

Fix: Add a matching toleration to the pod:

yaml
spec:
  tolerations:
    - key: "dedicated"
      operator: "Equal"
      value: "gpu"
      effect: "NoSchedule"

Or remove the taint from the node if it was applied by mistake:

bash
kubectl taint node <node-name> dedicated-   # The trailing - removes the taint

Stuck on this in production?

We debug exactly this kind of issue for platform teams — usually in a single working session.

Talk to us

Cause 4: PersistentVolumeClaim not bound

0/3 nodes are available: pod has unbound immediate PersistentVolumeClaims.

The pod references a PVC that isn't in Bound state — either the PVC doesn't exist or no PersistentVolume satisfies the claim.

bash
1# Check PVC status
2kubectl get pvc
3# NAME         STATUS    VOLUME   CAPACITY   ACCESS MODES
4# db-storage   Pending             ← stuck
5
6# See why the PVC is pending
7kubectl describe pvc db-storage

Common reasons:

  • No StorageClass with the requested name exists
  • No PV with matching capacity and access mode (for static provisioning)
  • Volume provisioner isn't running
bash
# List available StorageClasses
kubectl get storageclasses

Fix: Ensure a StorageClass exists that can dynamically provision the volume, or create a matching PV manually for static provisioning.


Cause 5: All nodes are cordoned or unschedulable

0/3 nodes are available: 3 node(s) were unschedulable.

A cordoned node is marked as unschedulable — no new pods will be placed on it. Existing pods are unaffected. If an admin cordoned nodes for maintenance and didn't uncordon them, new pods pile up as Pending.

bash
1# Check node status — SchedulingDisabled means cordoned
2kubectl get nodes
3# NAME        STATUS                     ROLES
4# node-1      Ready,SchedulingDisabled   worker   ← cordoned
5# node-2      Ready                      worker
6
7# See why a node is unschedulable
8kubectl describe node <node-name> | grep -A3 Unschedulable

Fix: Uncordon the node when maintenance is done:

bash
kubectl uncordon <node-name>

ResourceQuota note: If pods aren't showing up as Pending at all (the Deployment replica count is right but fewer pods exist than expected), check kubectl describe resourcequota -n <namespace>. ResourceQuota rejections happen at the API level — pods are never created, so they don't appear as Pending. The evidence shows up in kubectl describe replicaset <name> events instead.


Quick diagnostic flow

bash
1# 1. Get the scheduling failure reason
2kubectl describe pod <pod-name> | grep -A5 Events
3
4# 2. Check node capacity and what's already allocated
5kubectl describe nodes | grep -E "(Allocatable|Allocated)" -A6
6
7# 3. If node selector / affinity issue — check node labels
8kubectl get nodes --show-labels
9
10# 4. If taint issue — check node taints
11kubectl describe nodes | grep Taint
12
13# 5. If PVC issue — check PVC status
14kubectl get pvc -n <namespace>

See also

Related scheduling and startup failures: FailedScheduling with no nodes available, readiness probe failed, and CreateContainerConfigError.

Frequently Asked Questions

Where does the scheduler say why it failed?

In the pod's events, which name the constraint per node — insufficient cpu, node affinity mismatch, taints not tolerated, or volume node affinity conflict. That message is precise and is the fastest route to the answer, but it is only in the events, not the pod status.

Why is my pod pending when nodes look empty?

Requests are what the scheduler reserves, not current usage. A node running workloads that request more than they use appears idle in metrics while being fully committed. Compare requests against allocatable capacity, not utilisation graphs.

Can a volume keep a pod pending?

Yes. A PersistentVolume in one zone constrains the pod to that zone, and if no node there has room the pod stays pending with a volume node affinity conflict. With wait-for-first-consumer binding the PVC is pending too, which is normal until a schedulable node exists.

What if the cluster autoscaler should have added a node?

It only adds nodes when a pending pod would fit on one it can create. A pod pending for affinity, taints or a volume constraint does not trigger a scale-up, because a new node would not help. The autoscaler's logs state which pods it considered and why it declined.

Official References

Was this article helpful?

Be the first to rate this article

Related Topics

Kubernetes
Troubleshooting
Scheduling
Pending
Debugging
DevOps

Found this useful? Share it.

Practice this

Related tools

Read Next