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.
- Step 1: Read the scheduling failure reason
- Cause 1: Insufficient CPU or memory — Insufficient cpu / Insufficient memory
- Cause 2: Node selector or required affinity has no match
- Cause 3: Taint not tolerated
- Cause 4: PersistentVolumeClaim not bound
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
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.
# 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:
- Lower the pod's resource requests if they're over-specified:
resources:
requests:
cpu: "100m" # Lower this if the app doesn't need 1 full CPU
memory: "128Mi"-
Add more nodes — if the cluster is genuinely full, scale up (or let Cluster Autoscaler do it if configured)
-
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.
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 LabelsFix: Either add the required label to a node, or correct the nodeSelector/affinity in the pod spec.
# Add a label to a node
kubectl label node <node-name> disktype=ssd1# 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.
# See node taints
kubectl describe node <node-name> | grep Taint
# Taints: dedicated=gpu:NoScheduleFix: Add a matching toleration to the pod:
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"Or remove the taint from the node if it was applied by mistake:
kubectl taint node <node-name> dedicated- # The trailing - removes the taintStuck on this in production?
We debug exactly this kind of issue for platform teams — usually in a single working session.
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.
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-storageCommon reasons:
- No StorageClass with the requested name exists
- No PV with matching capacity and access mode (for static provisioning)
- Volume provisioner isn't running
# List available StorageClasses
kubectl get storageclassesFix: 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.
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 UnschedulableFix: Uncordon the node when maintenance is done:
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 inkubectl describe replicaset <name>events instead.
Quick diagnostic flow
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
- Pod Topology Spread Constraints for High Availability
- Fix: Kubernetes CrashLoopBackOff — pod is scheduled but keeps crashing
- Kubernetes Storage, ConfigMaps & Secrets — PVCs and StorageClasses explained
- Kubernetes Core Concepts — how the scheduler and resource requests work
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
- Debug Pods — reading pod status, events and container states
- kubectl reference — command syntax, output formats and selectors
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


