Ordered by how often each one turns out to be the answer.
No node has enough free CPU or memory
CommonThe pod's *requests* — not its usage — exceed what any single node has unallocated. This catches people because a cluster can look busy at 40% actual utilisation while being fully committed on requests. The scheduler reasons about requests, not about what is really being used.
Confirm
kubectl describe pod <pod> | grep -A10 Events
"Insufficient cpu" or "Insufficient memory" in the FailedScheduling message
Fix
- Lower the request if it was set higher than the workload genuinely needs.
- Add capacity, or free it by right-sizing over-requested pods elsewhere.
- Check the request is not accidentally enormous — a units mistake such as 1000 instead of 1000m asks for a thousand CPUs.
kubectl describe nodes | grep -A5 'Allocated resources'
Shows committed requests per node, which is what the scheduler actually weighs.
Node selectors, affinity or taints exclude every node
CommonThe pod asks for nodes with a particular label, or the only suitable nodes carry a taint the pod does not tolerate. Capacity is irrelevant here — the scheduler has excluded those nodes from consideration entirely.
Confirm
kubectl describe pod <pod> | grep -i -E 'didn.t match|untolerated taint|node.selector|affinity'
"node(s) didn't match Pod's node affinity/selector" or "had untolerated taint"
Fix
- Compare the pod's nodeSelector and affinity against real node labels: kubectl get nodes --show-labels.
- Add the matching toleration, or remove the constraint if it was inherited from a copied manifest.
- Control-plane nodes are tainted deliberately — tolerating that taint is rarely the right answer.
A PersistentVolumeClaim is unbound
OccasionalThe pod mounts a PVC that has not been satisfied. No matching PersistentVolume exists, no StorageClass can provision one, or the volume is zone-bound to a zone with no suitable node. The pod waits on storage, not on compute.
Confirm
kubectl get pvc -n <namespace>
A PVC in Pending rather than Bound
Fix
- Check the StorageClass named by the PVC exists and can provision dynamically.
- For zonal volumes, confirm a node exists in the volume's zone.
- Read the PVC's own events — they usually name the provisioning failure directly.
A ResourceQuota is blocking creation
OccasionalThe namespace has a quota and this pod would exceed it. The failure is admission-level rather than scheduling, so the wording differs from the usual capacity messages — which makes it easy to misread as insufficient resources.
Confirm
kubectl describe quota -n <namespace>
Used values at or near Hard limits for the resource the pod requests
Fix
- Free quota by removing unused workloads in the namespace, or raise the quota.
- Confirm the pod sets the requests the quota requires — some quotas reject pods with no requests at all.
There are no schedulable nodes
RareEvery node is NotReady, cordoned, or the cluster genuinely has none. Obvious once seen, and worth ruling out early because it makes every other investigation pointless.
Confirm
kubectl get nodes
All nodes NotReady or SchedulingDisabled, or an empty list
Fix
- Recover or uncordon the nodes: kubectl uncordon <node>.
- If a cluster autoscaler should have added capacity, check its logs — it may be at its own maximum.