When Kubernetes restarts your pod – And when it doesn't

trikelef1 pts0 comments

When Kubernetes restarts your pod — And when it doesn’t | CNCF

Skip to content<br>Accessibility<br>help

Posted on March 17, 2026<br>by Shamsher Khan, Project Maintainer

CNCF projects highlighted in this post

A production internals guide verified against Kubernetes 1.35 GA<br>Companion repository: github.com/opscart/k8s-pod-restart-mechanics

The terminology problem

Engineers say "the pod restarted" when they mean four different things. Getting this wrong leads to flawed runbooks and bad on-call decisions.

Term Pod UID Changes? Pod IP Changes? Restart Count Container restart (process restart inside the same pod) NoNo+1Pod recreation (rolling update, drain) YesYesResets to 0In-place resize (1.35 GA) — CPU NoNo0In-place resize (1.35 GA) — memory (RestartContainer policy) NoNo+1

The practical test: Did the pod UID change? If yes — that is recreation, not a container restart. Restart count resets to zero. If no — same pod object, container process restarted inside it.

The core insight: What kubelet actually watches

kubelet watches the pod spec — not ConfigMaps, not Secrets, not Istio CRDs. If the pod spec didn’t change, kubelet never fires. This single fact explains the majority of "why didn’t my config update?" investigations in production.

Mutating admission webhooks can change the pod spec at creation time, but never after admission — they cannot trigger container restarts post-creation.

Decision matrix

Change Container Restart? Pod Recreated? Automatic? Container image YesYesYes — Deployment controllerEnv var (any source) YesNoManual rolloutConfigMap — volume mount App decidesNoPartial — app must watch inotifyConfigMap — envFrom YesNoManual rolloutSecret — volume mount App decidesNoPartial — app must watch inotifySecret — envFrom YesNoManual rolloutProjected ServiceAccount token NeverNoYes — kubelet auto-rotatesCPU resize (K8s 1.35+) NeverNoManual patchMemory resize (K8s 1.35+) Per resizePolicyNoManual patchIstio VirtualService / DestinationRule NeverNoYes — xDS pushNetworkPolicy NeverNoYes — CNI agentService ports NeverNoYes — kube-proxyRBAC NeverNoYes — API serverNode drain / eviction YesYesYes — automatic

The flowchart below translates the same matrix into a decision path you can walk at 2am during an incident.

Diagram 1: Complete decision flowchart — does this change require a pod restart?

Scenario 1: ConfigMap — Why the same change has two behaviors

[Diagram 1: ConfigMap env var vs volume mount — env var pod frozen, volume pod auto-synced via kubelet symlink swap]

Env var mode (envFrom / valueFrom): The kernel copies env vars into /proc//environ at execve(). That memory is owned by the process — no external system can modify it. Update the ConfigMap and kubelet sees no pod spec change, does nothing. The process keeps old values indefinitely.

Volume mount mode : kubelet syncs via an atomic symlink swap, not a file write:

/etc/config/<br>├── ..2025_12_19_11_30_00/ ← NEW data dir (kubelet creates this)<br>│ └── APP_COLOR ← "red"<br>├── ..data ─────────────────▶ ..2025_12_19_11_30_00/ ← symlink SWAPPED atomically<br>└── APP_COLOR ──────────────▶ ..data/APP_COLOR

The symlink swap generates IN_CREATE on ..data — NOT IN_MODIFY on the file. Applications watching IN_MODIFY on an open file descriptor miss this entirely. This is why nginx does not auto-reload on ConfigMap changes without explicit inotify handling.

Lab Evidence (01-configmap/ in companion repo)

ConfigMap updated: APP_COLOR blue → red

Pod A (env var): APP_COLOR=blue ← frozen, restart count: 0<br>Pod B (volume mount): APP_COLOR=red ← auto-synced, restart count: 0

Correct inotify pattern — watch the directory, not the file

watcher.Add(filepath.Dir(configPath)) // watches /etc/config/ — catches IN_CREATE<br>// watcher.Add(configPath) // misses symlink swap entirely

for event := range watcher.Events {<br>if event.Op&fsnotify.Create == fsnotify.Create {<br>reloadConfig()

Scenario 2: Image updates — Recreation vs container restart vs CrashLoop

These three scenarios look similar but are fundamentally different:

Successful image update — pod is recreated

BEFORE: Pod UID aaa-bbb, IP 10.244.1.5, nginx:1.25, restarts: 0<br>AFTER: Pod UID xxx-yyy, IP 10.244.1.6, nginx:1.27, restarts: 0<br>↑ UID changed — RECREATION, not container restart

Diagram 3: Rolling update flow showing new ReplicaSet creation, pod recreation, and old RS retained for rollback.

ImagePullBackOff — old pod stays protected

Old pod: Running ← Kubernetes keeps it alive<br>New pod: ImagePullBackOff ← stuck, old pod never killed until new one is healthy

CrashLoopBackOff — same pod, restart count climbs

Pod UID: aaa-bbb ← UNCHANGED<br>Restart count: 0 → 1 → 2 → 3 ← same pod object, container crashing

Diagnostic rule: Climbing restart count with unchanged UID = crash loop. Zero restart count with new UID = rolling update.

StatefulSet note: StatefulSet pods are also recreated on image change, but ordinal identity (pod-0, pod-1) and PVC bindings are preserved. Container restart semantics are identical to...

restart container count change kubelet update

Related Articles