DevOps

Kubernetes Pods stuck in Pending — 8 causes and exact fixes

Every reason a Pod stays in Pending status and the kubectl commands to diagnose each one. From resource limits to node selectors to PVC issues.

A Pod stuck in Pending means Kubernetes cannot schedule it onto any node. The Pod is created but no container is running. Here are the 8 real causes ranked by frequency, with exact diagnostic commands.

First — always run this

kubectl describe pod <pod-name>

Scroll to the Events: section at the bottom. The scheduler explains WHY it can’t place the Pod. Every fix below starts from this event message.

Cause 1 — Insufficient CPU or memory on nodes

Event message:

0/3 nodes are available: 3 Insufficient cpu.

The Pod requests more resources than any node has free.

Diagnose:

kubectl top nodes
kubectl describe nodes | grep -A 5 "Allocated resources"

Fix:

  • Reduce Pod’s resources.requests.cpu / memory if over-requested
  • Scale cluster (add nodes)
  • Free capacity by removing unused Deployments

Cause 2 — Node selector or affinity doesn’t match

Event message:

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

Pod requires nodes with specific labels but none have them.

Diagnose:

kubectl get nodes --show-labels
kubectl get pod <pod-name> -o yaml | grep -A 5 nodeSelector

Fix:

  • Add the required label to a node: kubectl label node <node> disktype=ssd
  • Or remove the nodeSelector from Pod spec if not needed

Cause 3 — Taints without matching tolerations

Event message:

0/3 nodes are available: 3 node(s) had untolerated taint.

Nodes have taints (e.g. NoSchedule) that Pod doesn’t tolerate.

Diagnose:

kubectl describe nodes | grep -A 2 Taints

Fix:

  • Add matching toleration to Pod spec:
tolerations:
- key: "special"
  operator: "Equal"
  value: "true"
  effect: "NoSchedule"
  • Or remove the taint from node: kubectl taint node <node> special-

Cause 4 — PersistentVolumeClaim not bound

Event message:

persistentvolumeclaim "data-pvc" not found

or

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

Pod references a PVC that doesn’t exist or has no matching PV.

Diagnose:

kubectl get pvc
kubectl describe pvc <pvc-name>

Fix:

  • Create the missing PVC
  • Check StorageClass exists: kubectl get storageclass
  • Verify dynamic provisioning is working (CSI driver installed)

Cause 5 — Image pull secret missing (private registry)

Event message:

Failed to pull image: unauthorized

Pod’s image is in a private registry, no credentials configured.

Diagnose:

kubectl get pod <pod-name> -o yaml | grep imagePullSecrets
kubectl get secrets | grep regcred

Fix:

kubectl create secret docker-registry regcred \
  --docker-server=<registry-url> \
  --docker-username=<user> \
  --docker-password=<pass>

Add to Pod spec:

spec:
  imagePullSecrets:
  - name: regcred

Cause 6 — Pod anti-affinity conflicts

Event message:

0/3 nodes are available: 3 node(s) didn't match pod anti-affinity rules.

Pod has anti-affinity requiring separation from other Pods, but no eligible node.

Diagnose:

kubectl get pod <pod-name> -o yaml | grep -A 20 affinity
kubectl get pods -o wide

Fix:

  • Add more nodes (so anti-affinity has room to spread)
  • Relax anti-affinity from requiredDuringScheduling to preferredDuringScheduling

Cause 7 — Namespace resource quota exceeded

Event message:

forbidden: exceeded quota

Namespace has ResourceQuota and adding this Pod would exceed limits.

Diagnose:

kubectl describe quota -n <namespace>

Fix:

  • Delete unused Pods in namespace
  • Increase quota: kubectl edit quota <quota-name>
  • Deploy to different namespace

Cause 8 — Scheduler broken or overloaded

Rare, but happens on stressed clusters.

Diagnose:

kubectl get pods -n kube-system | grep scheduler
kubectl logs -n kube-system <scheduler-pod-name>

Fix:

  • Restart scheduler pod
  • Check for scheduler configuration issues
  • On managed K8s (EKS/GKE), this usually resolves itself

The universal debug checklist

Copy this and run through it every time:

# Basic status
kubectl get pod <pod-name>

# Detailed events (THE most important)
kubectl describe pod <pod-name>

# Node status
kubectl get nodes
kubectl describe nodes

# Recent events across cluster
kubectl get events --sort-by='.lastTimestamp' | tail -20

# Related resources
kubectl get pvc
kubectl get storageclass
kubectl get secrets

Reproduce this yourself (browser sandbox)

Free Kubernetes playground: https://killercoda.com/playgrounds/scenario/kubernetes

Create a pod that will be Pending:

kubectl run stuck-pod --image=nginx --overrides='{"apiVersion":"v1","spec":{"nodeSelector":{"nonexistent":"label"}}}'

Then:

kubectl describe pod stuck-pod

Screenshot the Events section. Note the “didn’t match node selector” message.

Clean up:

kubectl delete pod stuck-pod

Prevention

Set sensible defaults from the start:

  • Resource requests — not too high, not too low. Start with 100m CPU, 128Mi memory. Adjust based on actual usage.
  • No nodeSelector unless truly needed. Default schedulers work well.
  • Test PVCs before deploying real workloads — verify storage class works.
  • Use kubectl top regularly to see node capacity trends.

Bottom line

Every Pending pod has a specific reason. The scheduler always tells you what it is via kubectl describe. Match the event message to one of the 8 causes above. Apply the corresponding fix. Done in 5 minutes.

Recommended

DevOps YAML Pack

36 production-ready configs — Kubernetes, Docker Compose, GitHub Actions, Terraform, Helm, Ansible. Every file heavily commented. Copy, paste, ship.

Get the pack — ₹499 →
Never miss an article