Home › Kubernetes

0/3 nodes are available: Insufficient cpu — how to fix

Warning  FailedScheduling  default-scheduler  0/3 nodes are available: 3 Insufficient cpu. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Insufficient memory.

Quick fix: Run kubectl describe pod POD and kubectl describe nodes | grep -A8 "Allocated resources". Lower resources.requests.cpu (e.g. 100m instead of 2), reduce replicas, remove stuck/unused pods, or add nodes / enable the cluster autoscaler.

What it means

The pod stays Pending. For each node, the scheduler checks whether allocatable CPU minus the sum of CPU requests of pods already on that node is at least this pod's CPU request. No node passed, so it reports N Insufficient cpu. A node can be at 5% real CPU usage and still be "full" if its pods request a lot.

The message lists a reason for every node, so read it all: 1 node(s) had untolerated taint, 2 Insufficient memory, didn't match Pod's node affinity/selector are separate problems.

Diagnose

kubectl describe pod my-pod             # Events: FailedScheduling
kubectl get pod my-pod -o jsonpath='{.spec.containers[*].resources}'
kubectl describe nodes | grep -A 8 "Allocated resources"
kubectl top nodes                        # real usage (needs metrics-server)

Allocated resources shows, per node, e.g. cpu 3800m (95%) requests out of allocatable. If that's near 100%, the node is full for scheduling.

Fix 1: lower the requests

CPU is measured in cores: 1 = one vCPU, 500m = half, 100m = a tenth. A request of 2 on 2-vCPU nodes can never fit, because system components reserve part of each node.

resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    memory: 512Mi

Set requests near the pod's typical usage (check kubectl top pod). Helm charts often default to large requests — override them in values.

Watch out for a LimitRange in the namespace that injects default requests:

kubectl get limitrange -n my-namespace -o yaml

Fix 2: free capacity

kubectl get pods -A --field-selector=status.phase!=Running
kubectl get pods -A -o wide | grep NODE_NAME

Fix 3: add capacity

Fix 4: the control-plane taint (single-node / small clusters)

had untolerated taint {node-role.kubernetes.io/control-plane: } means the scheduler won't use the control-plane node. On a lab cluster (kubeadm) you can allow workloads there:

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

Don't do that in production.

Fix 5: selectors and affinity narrow the choice

If the pod has a nodeSelector, node affinity or a toleration-only node pool, only those nodes are considered, and they may be the full ones. Check:

kubectl get pod my-pod -o yaml | grep -A5 -E "nodeSelector|affinity"
kubectl get nodes --show-labels

Fix 6: priority and preemption

No preemption victims found means no lower-priority pods could be evicted to make room. For critical workloads, give them a higher PriorityClass so the scheduler can preempt less important pods.

Last updated 2026-11-02.