<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Probe · XB Field Notes</title><link>https://www.xbcatm.com/tags/probe/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.xbcatm.com/tags/probe/index.xml" rel="self" type="application/rss+xml"/><item><title>Liveness and readiness tell Kubernetes how the Pod feels</title><link>https://www.xbcatm.com/posts/kubernetes-probes-liveness-readiness/</link><pubDate>Wed, 14 Jul 2027 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/posts/kubernetes-probes-liveness-readiness/</guid><description>Readiness decides whether the Pod gets traffic; liveness decides whether it should be restarted, splitting two concerns cleanly.</description><content:encoded>&lt;p&gt;A slow start is not a dead process: readiness shields traffic while liveness only cares about keeping the process alive. Teams wire a single HTTP endpoint into both probes, and then every slow dependency becomes a restart loop — the app needs time, liveness refuses to grant it. Two probes exist because &amp;ldquo;not ready&amp;rdquo; and &amp;ldquo;dead&amp;rdquo; demand opposite responses: one asks for time, the other for a restart.&lt;/p&gt;
&lt;p&gt;The mental model that prevents the confusion is a hospital triage. Readiness is the waiting-room nurse deciding who sees a doctor right now; liveness is the crash cart, rolled out only when the patient codes. You would never defibrillate someone for sitting in the waiting room. Keep that image and the probe configuration almost writes itself.&lt;/p&gt;
&lt;h2 id="readiness-gates-traffic"&gt;Readiness gates traffic&lt;/h2&gt;
&lt;p&gt;A readiness probe failure removes the Pod from the Service&amp;rsquo;s endpoints but leaves it running, so the process can finish warming up or recover on its own. Traffic flows again as soon as the probe passes. This makes readiness the safe probe to fail: caches can warm, migrations can finish, downstream dependencies can come back, all without losing the Pod&amp;rsquo;s in-memory state or paying a restart penalty.&lt;/p&gt;
&lt;p&gt;Design the readiness endpoint around genuine servability. Check what makes this Pod able to answer correctly right now: migrations applied, required caches populated, critical downstream reachable. A probe that only checks &amp;ldquo;the HTTP server responds&amp;rdquo; admits traffic to Pods that will fail every request. But resist stuffing every dependency into it: if a non-critical recommendation service is down, failing readiness sheds real traffic over a decoration.&lt;/p&gt;
&lt;h2 id="liveness-guards-the-process"&gt;Liveness guards the process&lt;/h2&gt;
&lt;p&gt;A failing liveness probe triggers a restart, a last resort that ends a hopeless wedge. Use it for what it is a liveness issue—not for containers that are merely slow to become ready. The legitimate targets are narrow: deadlocks where no request will ever succeed again, runaway goroutine or thread leaks that only a fresh process clears, corrupted in-memory state that no retry can heal. In each case the defining property is irreversibility — without a restart, recovery is impossible, not merely slow.&lt;/p&gt;
&lt;p&gt;That bar means many services should ship a minimal liveness probe or none at all. A liveness check against an external database turns every database blip into a fleet-wide restart storm. Prefer a cheap local check — a watchdog timestamp refreshed by the main loop — that detects &amp;ldquo;this process is wedged&amp;rdquo; without consulting anything outside the process. Kubernetes already restarts crashed containers; liveness exists only for the living dead.&lt;/p&gt;
&lt;h2 id="give-the-probes-grace"&gt;Give the probes grace&lt;/h2&gt;
&lt;p&gt;Set the initialDelaySeconds high enough that a cold start is not counted as a crash, and keep timeoutSeconds consistent with real work. Separate the two probes so a slow pod is protected by readiness, not mistakenly recycled by liveness. Better still, add a startup probe for slow-booting containers: while it runs, liveness stays disabled, so version upgrades that warm large caches get minutes instead of seconds without weakening steady-state liveness at all.&lt;/p&gt;
&lt;p&gt;Tune thresholds from observed behavior, not round numbers copied between services. periodSeconds times failureThreshold is the real reaction time — 10 seconds times 3 means 30 seconds of badness before action. Measure cold-start p99, dependency recovery time, and worst-case pauses, then set each number to react faster than the alert but slower than the noise.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;The signature failure is the restart loop during dependency outages: the database slows, readiness would have sufficed, but liveness probes share the same failing endpoint and the whole fleet restarts in waves. Each restart wipes warm caches, making the next wave slower to recover, until the system oscillates indefinitely. Diagnose by correlating restart timestamps with the dependency incident; the fix is splitting the endpoints — readiness checks the dependency, liveness checks only the process — so a sick downstream sheds traffic instead of killing Pods.&lt;/p&gt;
&lt;p&gt;The mirror failure is the zombie Pod: liveness passes because the HTTP server responds, but every request hangs behind a deadlocked worker pool. Traffic keeps flowing to a Pod that answers nothing. The fix is a liveness endpoint that exercises the real path — acquire a worker slot with a short timeout, verify the last successful request timestamp is fresh — so wedged state fails the probe while mere slowness does not.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;Inspect probe wiring and recent kills:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl get pod web-0 -o jsonpath=&amp;#39;{.spec.containers[*].livenessProbe}{&amp;#34;\n&amp;#34;}{.spec.containers[*].readinessProbe}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get events --field-selector reason=Unhealthy --sort-by=.lastTimestamp | tail -n 10
kubectl describe pod web-0 | grep -A3 -i &amp;#39;liveness\|readiness&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Shared endpoints across both probes, or Unhealthy events coinciding with downstream incidents, mark the split that still needs doing.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/configuration/liveness-readiness-startup-probes/"&gt;Kubernetes probes&lt;/a&gt;, Official docs on liveness and readiness, checked 2026-06-01.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/"&gt;Configure liveness and readiness probes&lt;/a&gt;, task guide for probe configuration, checked 2026-09-05.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>