Runbooks / kubernetes-node-notready
Kubernetes Node NotReady
A node reports NotReady and its pods stop rescheduling — decide between reviving the kubelet and replacing the node before workloads starve.
12 canonical sections · 5 command blocks · last reviewed · updated 2026-09-21
Related focus areas:CloudOpsPlatform EngineeringSRE
Fix the underlying gap:AWS Security ImprovementCloud Cost OptimizationIncident Management MaturityObservability MaturityPlatform Engineering AdoptionProduction-Ready KubernetesResilience and DR ImprovementSRE Adoption
On this page
Symptoms
kubectl get nodesshowsNotReady(orUnknown) for a node;Readycondition'slastHeartbeatTimeis minutes old.- The node gains the
node.kubernetes.io/not-readytaint; pods without a toleration get evicted after theirtolerationSeconds(default 300s for the system default). Pendingpods accumulate if the remaining nodes lack capacity; node-level dashboards (allocatable pods, kubelet uptime) flatline.
Severity / impact
- Sev-3 for a worker in a large cluster with headroom — the scheduler absorbs it, nobody notices but the on-call.
- Sev-1 when the node runs a singleton or stateful pod without a toleration window, when remaining capacity cannot absorb the evictions, or — worst case — the NotReady node is a control-plane member and etcd quorum is at risk.
Immediate safety actions
- Do NOT cordon or drain MORE nodes "to be safe" — draining reduces capacity exactly when you need it. Touch only the broken node.
- Check etcd health FIRST if the node is control-plane: a not-ready worker is an availability problem; lost etcd quorum is a cluster.
- If the node hosts singleton/stateful workloads, raise their
tolerationSecondsor apply a temporary toleration so eviction does not outrun your diagnosis.
Fast checks
- One node or several? Several at once points at the network, the control plane, or a bad node-image rollout — different playbook.
- Does the node respond to SSH? Reachable-but-NotReady is usually kubelet/container-runtime; unreachable is a host/cloud problem.
kubectl describe node: theReadycondition's reason (KubeletNotReady, ContainerRuntimeNotReady) names the failing component before you log in.
Diagnostic commands
# Node conditions + the kubelet's last heartbeat + taints, in one view:
kubectl describe node <node> | sed -n '/Conditions:/,/Taints:/p'# How stale is the heartbeat? (compare to now):
kubectl get node <node> -o jsonpath='{.status.conditions[?(@.type=="Ready")].lastHeartbeatTime}'# On the node: is the kubelet running, and what is it complaining about:
systemctl status kubelet --no-pager
journalctl -u kubelet --since "30 min ago" --no-pager | tail -50# On the node: kubelet refuses to start when its disk is full (logs/images):
df -h /var/lib/kubelet /var/lib/containerd# From the control plane: recent events naming the node's failure mode:
kubectl get events --field-selector involvedObject.name=<node> --sort-by=.lastTimestamp | tail -20Likely causes
1. Kubelet stopped or wedged (OOM, failed upgrade, cgroup stall) — most common on long-lived manually-managed nodes. 2. Container runtime (containerd) dead or unresponsive — kubelet reports NotReady when it cannot ping the runtime. 3. Node under disk/memory pressure evicting the kubelet's own overhead, or the node image filled /var with logs. 4. Network partition: the node works locally but cannot reach the API server (security group, route, DNS). 5. Host/cloud failure: instance stopped, hardware replacement, snapshot hung — the node is simply gone.
Decision tree
- Node SSH-able, kubelet inactive → restart kubelet (a); if it loops, read journalctl for the refusing reason.
- Node SSH-able, kubelet active, runtime errors in logs → restart containerd before anything heavier (b).
- Node SSH-able, disk >90% → free space / expand volume (c); kubelet usually self-recovers once it can write.
- Node unreachable from both SSH and kubelet → cloud-level failure (d): replace the node, do not wait it out beyond your declared threshold.
- Several nodes NotReady at once → stop: suspect network/control plane/upgrade, escalate to the platform on-call with the timeline.
Remediation
- (a) Kubelet:
systemctl restart kubeleton the node; watchkubectl get nodeflip Ready within ~1 minute. A kubelet that needs regular restarts is a bug — capture journalctl for the follow-up, do not script around it. - (b) Runtime:
systemctl restart containerdthen the kubelet; expect pods on the node to restart — announce it in the incident channel BEFORE running it. - (c) Disk: clear what is safe (rotated logs, dangling images via the runtime's prune — read what it lists before confirming), then grow the volume through the cloud.
- (d) Replace: for autoscaled nodes, delete the instance and let the ASG replace it; for static nodes, cordon + drain the NotReady node (
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data— it will mostly time out on unreachable pods; use --force on a confirmed-dead node only), then remove it from the cluster and provision a successor. - Never "fix" repeated NotReady by raising tolerationSeconds everywhere — that trades eviction latency for silent downtime.
Verification
kubectl get node <node>Ready continuously for 15+ minutes; heartbeat current.- Pod count on the node back to baseline; no pods Pending cluster-wide; DaemonSets re-scheduled.
- If the node was replaced: the new node Ready, the dead object gone (
kubectl get nodeshas no ghost entry).
Escalation
- Platform/cluster team for kubelet, runtime, or node-image problems; cloud on-call for instance-level failures; service owners of anything still Pending after the node recovered. For control-plane NotReady, escalate immediately — that is a cluster-risk incident, not a node one.
- Hand-off state: node name, condition reason, SSH-ability, kubelet journal excerpt, what was restarted/replaced, current pod placement.
Prevention
- Node auto-repair / health remediation where the platform offers it (GKE auto-repair, ELC autoreplacement, ASG replace-on-health-check).
- Alerts on
kube_node_status_readyand on heartbeat AGE (not just current state) — the age alert catches wedged nodes the state alert misses. - Multiple nodes for every workload class + PodDisruptionBudgets so one NotReady node is a non-event, not an outage.
- Keep node images small and /var on a dedicated volume with a disk-pressure alert at 80%.
Sources
Know which failure modes you actually have?
A free OpsRoadmaps assessment scores your controls with explainable findings and builds a prioritized roadmap — runbook coverage is one of the things it measures.
Assess my stackRunbooks document self-reported practice. Commands and thresholds were last reviewed 2026-09-19 — verify against your own environment before acting.