What's happening
Your pod has been sitting in Pending state for a while. When you check with kubectl describe, you see something like this:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 30s default-scheduler 0/3 nodes are available: 3 Insufficient cpu.
The Kubernetes scheduler checked all 3 nodes. None had enough free CPU to satisfy your pod's request. So it gave up and left the pod waiting.
This is different from CrashLoopBackOff (pod starts but crashes) or ImagePullBackOff (can't pull the image). Here the pod never even starts β it's stuck at scheduling, before any container runs.
Reproduce and diagnose
Step 1: Confirm the error
kubectl describe pod <pod-name> -n <namespace>
Scroll to the Events section at the bottom. You'll see the FailedScheduling warning:
0/3 nodes are available: 3 Insufficient cpu.
Step 2: Check what CPU your pod is requesting
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].resources}'
Or look directly at your Deployment YAML:
resources:
requests:
cpu: "2000m" # β this is what the scheduler uses to find a node
limits:
cpu: "4000m"
The scheduler uses requests, not limits, to make placement decisions. A request of 2000m means each node must have 2 full cores free β that's a lot to ask.
Step 3: Check actual node capacity and allocatable CPU
kubectl describe nodes | grep -A 5 'Allocatable\|Allocated resources'
For a cleaner view:
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu,MEMORY:.status.allocatable.memory
Then drill into each node to see what's already allocated:
kubectl describe node <node-name> | grep -A 10 'Allocated resources'
Example output:
Allocated resources:
(Total limits may be over 100 percent, i.e., no guarantee)
Resource Requests Limits
-------- -------- ------
cpu 1850m (92%) 3200m (160%)
memory 1Gi (55%) 2Gi (110%)
At 92% allocated, a new pod requesting even 200m won't fit. There's simply no room left.
Solutions
Option 1: Reduce your pod's CPU request (quick fix)
Most of the time, the request is just set too high. Lower it to something realistic for what the app actually uses at startup.
resources:
requests:
cpu: "100m" # reduced from 2000m
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
A common mistake: setting requests.cpu: "2" (2 full cores) when the app peaks at 200m. The limit can stay high. The request just needs to be an honest estimate of typical usage β not worst-case, not aspirational.
Apply the change:
kubectl apply -f your-deployment.yaml
Option 2: Scale up the cluster (add nodes)
Sometimes the request really is correct and you just need more capacity. Adding nodes is the direct fix.
GKE:
gcloud container clusters resize <cluster-name> \
--node-pool <pool-name> \
--num-nodes 5 \
--region <region>
EKS (managed node group):
aws eks update-nodegroup-config \
--cluster-name <cluster-name> \
--nodegroup-name <nodegroup-name> \
--scaling-config minSize=2,maxSize=10,desiredSize=5
On-premise: provision a new VM and join it to the cluster with kubeadm join.
Option 3: Enable Cluster Autoscaler
Cloud users have a smarter option: Cluster Autoscaler. It automatically provisions new nodes whenever the scheduler can't place a pod β no manual intervention required.
# Check if autoscaler is already running
kubectl get pods -n kube-system | grep cluster-autoscaler
Not installed? Deploy it (example for GKE with Workload Identity):
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/gce/examples/cluster-autoscaler-autodiscover.yaml
Then annotate your node group so the autoscaler knows it's eligible to scale.
Option 4: Evict or scale down other workloads
Find out what's eating CPU across the cluster:
kubectl top nodes
kubectl top pods --all-namespaces --sort-by=cpu | head -20
Idle or over-replicated deployments are often the culprit. Scale them down to free up space:
kubectl scale deployment <other-deployment> --replicas=1 -n <namespace>
Option 5: Check for a LimitRange applying large defaults
A pod with no resources block gets a zero request from Kubernetes β that won't cause this error on its own. But a LimitRange in the namespace can inject a large default automatically.
kubectl get limitrange -n <namespace>
kubectl describe limitrange <name> -n <namespace>
Set explicit requests in your pod spec to override whatever the LimitRange is injecting.
Verify the fix
Once you've applied the change, watch the pod live:
kubectl get pods -n <namespace> -w
You should see: Pending β ContainerCreating β Running β usually within a minute or two.
Confirm no more scheduling failures:
kubectl describe pod <pod-name> -n <namespace> | grep -A 10 Events
No FailedScheduling events means you're done.
Lessons learned
- Set realistic requests, not worst-case ones. The scheduler makes real placement decisions based on
requests. Requesting 4 cores when you use 200m wastes reserved capacity on every single node in the cluster. - Requests and limits do different jobs.
requests= what the scheduler reserves at placement time.limits= the hard ceiling enforced at runtime. Keep requests low and realistic; set limits at the actual max you'd tolerate. kubectl top nodesshows usage, not allocation. A node can show 30% CPU usage but have 95% CPU requested. The scheduler works off requests, not real-time usage. Always checkkubectl describe nodeand look at the Allocated resources section β that's the number that matters.- Cluster Autoscaler pays for itself fast. If you're on cloud infrastructure and hit this error more than once, autoscaler eliminates the manual bottleneck entirely. Set it up before you need it.

