Skip to content
SRE & DevOpsFix Published Updated 4 min readViews unavailable

Fixing a Kubernetes Node Stuck NotReady

Diagnose a Kubernetes NotReady node through conditions, leases, kubelet, runtime, storage, networking, and safe workload evacuation with evidence intact.

A node showing NotReady means the control plane hasn’t received a recent healthy status report from that node’s kubelet — the fix depends on identifying which specific layer (kubelet itself, the container runtime, or networking) actually stopped reporting healthy.

Step 1: check node conditions for the specific reported reason

kubectl describe node <node-name>

The Conditions section frequently states the actual problem directly — MemoryPressure, DiskPressure, or a generic heartbeat timeout each point at a different next step.

Step 2: check kubelet’s own status on the affected node

ssh <node>
systemctl status kubelet
journalctl -u kubelet -n 100

If kubelet itself has crashed or isn’t running, nothing else matters until it’s restarted — check its logs for the specific reason it stopped.

Step 3: check container runtime health

systemctl status containerd    # or crio, depending on your runtime
crictl info

kubelet depends entirely on a healthy container runtime underneath it — a crashed or unresponsive containerd/CRI-O produces a NotReady node even with kubelet itself technically running.

Step 4: check disk pressure specifically

df -h

A node that’s run out of disk space (frequently from accumulated container images or logs — see fixing Docker disk space issues for the underlying cause) reports DiskPressure, which kubelet treats as reason enough to stop accepting new pods and mark itself unhealthy.

Step 5: check CNI network plugin health

kubectl logs -n kube-system -l k8s-app=calico-node --field-selector spec.nodeName=<node-name>

(Substitute your actual CNI plugin’s namespace/label selector.) A broken network plugin can prevent kubelet from reporting healthy even when the node’s basic compute resources are fine.

Step 6: check node resource pressure generally

kubectl top node <node-name>

Confirm actual CPU/memory pressure isn’t simply overwhelming the node — a genuinely overloaded node reporting NotReady may need workload rebalancing (or a Pod Disruption Budget-aware drain) rather than a service restart.

Step 7: restart kubelet after resolving the underlying cause

systemctl restart kubelet

Restarting kubelet without first fixing the actual root cause (a full disk, a crashed runtime) just produces the same NotReady state again shortly afterward.

Before restarting, save recent kubelet and runtime logs, node conditions, events, and the last Lease renewal. A restart can erase timing clues. On managed nodes, use the provider-supported diagnostic and replacement workflow; manual service changes may be overwritten or place the instance outside support.

Step 8: cordon and drain the node if it needs deeper investigation or replacement

kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets

Cordoning prevents new pods from scheduling onto a node you’re actively troubleshooting; draining safely evicts existing pods elsewhere first, if the node needs to come out of service for extended repair or replacement.

Distinguish loss of contact from resource pressure

The node controller uses status updates and Lease heartbeats to determine reachability. Ready: Unknown with stale heartbeats suggests the control plane cannot hear from kubelet, which may be host failure, routing, firewall, certificate, clock, or API connectivity. Ready: False with current reports indicates kubelet is communicating but declares itself unhealthy. That distinction changes where to investigate.

Check filesystem bytes and inodes, runtime sockets, DNS, routes, MTU, and CNI pods specifically on the affected node. kubectl top depends on the metrics pipeline and may be unavailable precisely during a node failure, so corroborate with host metrics. Do not assume a Calico label when another CNI is installed.

Drain only after confirming replacement capacity and PodDisruptionBudgets. Local data, unmanaged pods, DaemonSets, and unhealthy workloads require explicit decisions; avoid --force until the ownership and data-loss consequences are understood. If the node is unreachable, eviction may proceed only after controller timeouts, and storage attachments can delay rescheduling.

After repair or replacement, uncordon only when Ready is stable, runtime and CNI health are clean, test pods can start and reach dependencies, and alerts have cleared. Repeated NotReady transitions usually warrant replacement and a root-cause investigation rather than another restart.

Why checking conditions before touching anything saves the most time

kubectl describe node’s Conditions section names the actual reported problem in most cases — disk pressure, memory pressure, or a specific heartbeat failure — which tells you immediately whether to look at storage, the container runtime, or networking first, rather than checking all three exhaustively before finding the one that’s actually broken.

Related:

Sources:

Comments