Kind Node IP Mismatch After Crash Runbook¶
Symptoms¶
- One or more nodes stuck in
NotReadyafter Docker / Rancher Desktop crash and restart - Pods stuck in
TerminatingorPendingstate ingress-nginxcontroller unable to schedule a replacement pod — all*.idp.localURLs return 502 / unreachablekubectl get nodesshows a node asNotReadyeven though its Docker container is running- Kubelet logs inside the node container show repeated connection errors to the old IP:
1 | |
Root Cause¶
Kind runs each cluster node as a Docker container. When Docker or Rancher Desktop crashes and restarts, it may reassign container IPs — the control-plane node gets a different IP than what is baked into /etc/kubernetes/kubelet.conf. The kubelet then cannot reach the API server, so the node reports NodeStatusUnknown and stays NotReady.
This cascades to:
- Pods that were running on the affected node get evicted and enter Terminating
- Replacement pods that require a NotReady node (e.g. DaemonSets, ingress with node affinity) stay Pending
- inotify resource limits may also be exhausted after the restart, causing kube-proxy to crash
Diagnosis¶
1. Confirm the IP mismatch¶
1 2 3 4 5 6 7 | |
If the control-plane's Docker IP differs from its INTERNAL-IP in kubectl, you have an IP mismatch.
2. Confirm the API server cert covers the new IP¶
1 2 | |
The new IP must appear in the IP Address: list. If it does not, a cluster recreate is required (see Prevention below).
3. Check kubelet.conf target¶
1 | |
It should match the current Docker IP of the control-plane.
Fix¶
Replace 172.18.0.NEW with the current Docker IP of the control-plane container (from step 1 above).
Step 1 — Update kubelet.conf with the new IP¶
1 2 3 4 5 6 | |
Step 2 — Restart the kubelet¶
1 | |
Step 3 — Wait for the node to recover¶
1 2 | |
Step 4 — Fix inotify limits if kube-proxy is crash-looping¶
1 2 3 4 5 6 7 8 9 | |
Step 5 — Verify full cluster health¶
1 2 3 4 5 | |
ingress-nginx should reschedule and reach Running within ~60 seconds, restoring all *.idp.local URLs.
Step 6 — Fix kagent-controller if still CrashLoopBackOff¶
After DNS recovers the kagent-controller may still crash because the startup probe kills it before the pgvector DB migrations complete (default window is only ~60 s).
1 2 3 4 5 6 7 8 9 10 11 | |
Known Harmless Failure¶
catalog-exporter in the monitoring namespace will stay in CrashLoopBackOff whenever Backstage is not running via Docker Compose. It tries to reach backstage.default.svc.cluster.local:3000 (in-cluster DNS), which does not exist when Backstage is down. Start Backstage to resolve it:
1 | |
Prevention¶
-
Add a fixed IP subnet to Docker: configure Docker Desktop / Rancher Desktop to use a stable subnet so container IPs do not change across restarts. Set this in Docker Desktop → Settings → Docker Engine:
1 2 3 4 5
{ "default-address-pools": [ { "base": "172.18.0.0/16", "size": 24 } ] }Kind still assigns IPs dynamically within that pool, but stability is improved on clean restarts.
- Graceful shutdown: use
./scripts/bootstrap-local.sh --destroybefore shutting down Docker / Rancher Desktop, then re-bootstrap on next use. This avoids IP drift entirely.
-
Cluster recreate (last resort): if the API server cert does not cover the new IP, recreate the cluster:
1 2
./scripts/bootstrap-local.sh --destroy ./scripts/bootstrap-local.sh --full