First, Understand Why the Pod Is Stuck
You're running Azure Kubernetes Service (AKS) and a pod just sits there with Pending status. When you run kubectl describe pod, you see the event Insufficient cpu. This means the scheduler couldn't find a node with enough free CPU capacity to fit your pod's requests. But it's not always about raw capacity. Taints and tolerations can hide nodes from the scheduler, making them look unavailable even if they have spare CPU.
I've seen this a dozen times. Last month a client's payment API went down because a single pod couldn't schedule after they added a new deployment with a CPU request that was too high. The fix took 30 seconds, but they'd been looking at the wrong logs for an hour.
This troubleshooting flow starts with the fastest fix. Try each step. Stop when your pod schedules.
30-Second Fix: Scale Up the Node Pool
The most common reason you see Insufficient cpu is that your node pool is simply full. If you're using cluster autoscaler, it might be waiting for the scale-out to happen, but sometimes it's slow or disabled. The fastest way to confirm and fix is to manually scale the node pool.
- Open the Azure portal, find your AKS cluster.
- Go to Node pools.
- Select your system or user pool and click Scale.
- Increase the node count by 1 or 2, then save.
Alternatively, use the Azure CLI command:
az aks nodepool scale --resource-group myResourceGroup --cluster-name myAKSCluster --name nodepool1 --node-count 3Wait a couple of minutes for the new nodes to join. Then check if the pod schedules with kubectl get pods.
If that doesn't work, check whether autoscaler is enabled and configured correctly. If you don't have autoscaler and you're always hitting capacity, think about adding it later. But for now, scaling out is the immediate relief.
5-Minute Fix: Inspect Taints and Tolerations
If scaling didn't help, the problem might not be raw capacity. Nodes with taints won't accept pods unless the pod has a matching toleration. If your node has a taint like CriticalAddonsOnly=true:NoSchedule and your pod doesn't tolerate it, the scheduler skips that node entirely, and you get Insufficient cpu on other nodes that might actually be full.
Here's how to check:
- Run
kubectl describe nodes | grep -i taintsto list taints on all nodes. - Or get a cleaner view with
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINT:.spec.taints - Look for taints like
node.kubernetes.io/not-ready,node.kubernetes.io/unreachable, or custom ones.
If you find a taint, check your pod's spec for a matching toleration. If it's missing, add it. For example, if your node has the taint:
key=workload,value=critical,effect=NoScheduleYour pod needs this in its spec:
tolerations:
- key: "workload"
operator: "Equal"
value: "critical"
effect: "NoSchedule"Apply the updated deployment with kubectl apply -f deployment.yaml. If the pod schedules, you're done. If not, it's time for the deeper dive.
15+ Minute Fix: Check Resource Requests and Node Allocatable
If scaling and taints aren't the issue, you're dealing with a resource request problem. The scheduler looks at requests, not limits, to decide placement. If your pod requests 2 CPUs but every node only has 1.5 CPUs free, it won't fit. The fix is to lower the request or free up resources on nodes.
First, see what's actually available:
kubectl describe nodes | grep -A 5 "Allocatable"That shows CPU and memory the node can allocate to pods. Also check current usage:
kubectl top nodesThis tells you if a node is nearly full. If one node has 500m CPU free and your pod requests 1 CPU, it won't go there.
Now, look at your pod's resource requests. Run kubectl get deploy my-deployment -o yaml and find the resources.requests.cpu field. If it's set to something like 2 but your workloads don't need that much, lower it. For many apps, 0.5 CPU is plenty. Here's an example:
resources:
requests:
cpu: 0.5
memory: 128MiIf you can't lower the request, you need to reduce other pods' requests on the cluster. That's a bigger architectural change. But often you'll find that a test namespace has pods requesting huge amounts of CPU that they never use. Clean those up.
Another angle: check if you're running into the node allocatable vs capacity difference. AKS reserves some resources for system components. You might see capacity of 4 CPUs but allocatable of only 3.2. That's normal. The scheduler only sees allocatable.
If you've done all this and it still fails, run kubectl describe pod <pod-name> again and look for events after your changes. Sometimes the scheduler gives more specific messages like 0/3 nodes are available: 3 Insufficient cpu. That tells you all nodes are short.
One more thing: if you're using node pools with different VM sizes, the big pools might all be full, but a smaller pool has room. Check if your deployment has a node selector that pins it to a specific pool. If the selector matches a pool that's full, you'll get this error even if other pools have space.
When to Walk Away from the Defaults
Most times, the 30-second scale-out fixes it. If not, the taint check takes five minutes and solves a lot of mystery. The deep resource dive is for when you're dealing with a recurring problem, not a one-off.
I've seen teams spend an hour troubleshooting when a simple taint was the culprit. Don't skip that step. Also, set proper resource requests from day one. Understating requests causes overcommitment; overstating causes this exact error. Start with cpu: 0.5 and adjust based on real usage via kubectl top pods.
If you need to, check the AKS documentation for taints, but honestly, kubectl describe will tell you everything.