🩺 Three Probes, Three Questions

Kubernetes uses three independent probes to decide how to handle a running container. Each answers a different question:

🔴 Liveness

"Is this container still alive?" — Failure triggers a container restart. Use for deadlock detection.

🟡 Readiness

"Is this container ready to serve traffic?" — Failure removes the pod from Service endpoints. Use for warm-up and dependency checks.

🟢 Startup

"Has this container finished starting up?" — Disables liveness/readiness until it passes. Use for slow-starting apps.

Startup Probe liveness+readiness gated Running — Liveness + Readiness active liveness fail → restart | readiness fail → remove from endpoints container starts startup passes steady state

🔌 Four Probe Mechanisms

Each of the three probe types can use any of these four mechanisms to check health:

MechanismHow it worksBest for
httpGet kubelet makes an HTTP GET to the container's IP; 2xx–3xx = success, anything else = failure HTTP/HTTPS services — most common
tcpSocket kubelet opens a TCP connection to the specified port; connection opens = success Non-HTTP services (databases, message queues)
exec kubelet runs a command inside the container; exit code 0 = success Custom health logic, file existence checks
grpc kubelet calls the gRPC Health Checking Protocol; SERVING = success (K8s 1.24+ GA) gRPC services — avoids an extra HTTP sidecar

All Four in YAML

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: X-Health-Check
      value: "true"

readinessProbe:
  tcpSocket:
    port: 5432   # e.g. postgres readiness

livenessProbe:
  exec:
    command:
    - sh
    - -c
    - "redis-cli ping | grep PONG"

livenessProbe:
  grpc:
    port: 50051
    service: ""   # empty string = overall server health

🟡 Readiness Probe — Traffic Gate

The readiness probe controls whether a pod receives traffic from a Service. When it fails, the pod's IP is removed from the Service's Endpoints/EndpointSlice — no new connections are routed to it. The pod is NOT restarted; it just stops receiving traffic until the probe passes again.

Key use cases

🔥 Warm-up

JVM apps, ML models, or caches that need time to load before serving. Keep pod out of rotation until warm.

🔗 Dependency check

App can't serve if the database is unreachable. Readiness (not liveness!) should check this — no restart, just traffic hold.

⚖️ Graceful overload

Pod signals it's overloaded by failing readiness. Load balancer stops sending new requests while in-flight ones finish.

🚀 Rolling deployment

New pods must pass readiness before old pods are removed. Prevents a broken deployment from taking down all replicas.

readinessProbe:
  httpGet:
    path: /readyz
    port: 8080
  initialDelaySeconds: 5
  periodSeconds:      10
  timeoutSeconds:      2
  successThreshold:    1   # 1 success to re-enter rotation
  failureThreshold:    3   # 3 failures to leave rotation
🔵 successThreshold can be > 1 for readiness Unlike liveness (must be 1), readiness can require multiple consecutive successes before re-entering the load balancer. Useful if your health endpoint flaps — require 2–3 successes to prevent flapping pods in and out of rotation.

🎛️ Tuning Probe Parameters

Misconfigured probe thresholds are one of the most common causes of production incidents. Here is a field-by-field guide:

FieldDefaultMeaningTuning guidance
initialDelaySeconds 0 Wait before first probe fires Use startup probe instead. If not using startup probe, set to your p95 startup time.
periodSeconds 10 How often to probe 10–15s for liveness. 5–10s for readiness (faster traffic recovery).
timeoutSeconds 1 Max wait for a probe response Increase to 3–5s. Default 1s is too tight under load — causes false failures.
successThreshold 1 Consecutive successes to become healthy Must be 1 for liveness/startup. Can be 2–3 for readiness to prevent flapping.
failureThreshold 3 Consecutive failures before action 3 is usually right for liveness. For readiness, consider 1 for fast egress from rotation.

Probe Anti-Patterns to Avoid

Anti-patternProblemFix
Liveness checks external deps (DB, Redis) Restarts app when dependency is down — cascading failure Liveness = internal only; readiness = dependencies
No startup probe on slow JVM/Python apps Liveness kills pod before it finishes starting → CrashLoopBackOff Add startup probe with generous failure budget
timeoutSeconds: 1 under load Slow responses under CPU pressure cause false liveness failures and restarts Set timeoutSeconds to 3–5s
Same endpoint for liveness and readiness Dependency failure triggers restart instead of traffic removal Separate /healthz (liveness) from /readyz (readiness)
exec probe running expensive script Probe consumes CPU on a tight periodSeconds — starves the app Use httpGet or reduce periodSeconds; keep exec probes lightweight

Observing Probe State

# See probe configuration and recent events
kubectl describe pod my-app-xyz

# Events section shows probe failures:
# Warning  Unhealthy  Liveness probe failed: Get "http://10.0.0.5:8080/healthz": context deadline exceeded
# Warning  Killing    Container my-app failed liveness probe, will be restarted

# Check restart count — tells you if liveness has been firing
kubectl get pod my-app-xyz -o jsonpath='{.status.containerStatuses[0].restartCount}'

# Watch readiness — pod is Ready only when readiness passes
kubectl get pod my-app-xyz -w
# NAME         READY   STATUS    RESTARTS
# my-app-xyz   0/1     Running   0        ← readiness failing
# my-app-xyz   1/1     Running   0        ← readiness passed

🔴 Liveness Probe — Restart on Deadlock

The liveness probe detects containers that are running but stuck — a goroutine leak causing deadlock, a thread pool exhausted, or a process that stopped responding but hasn't exited. Without it, the container sits in Running state forever while serving errors.

When liveness fails

  1. kubelet increments the failure counter.
  2. Once failureThreshold consecutive failures are reached, kubelet kills the container.
  3. The container restarts according to the pod's restartPolicy (almost always Always for long-running services).
  4. The pod's restartCount increments — visible in kubectl describe pod.

Complete liveness example with all fields

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 10   # wait before first probe
  periodSeconds:       15   # probe every 15s
  timeoutSeconds:       3   # fail if no response within 3s
  successThreshold:     1   # 1 success to go healthy (must be 1 for liveness)
  failureThreshold:     3   # 3 consecutive failures → restart
🔴 Don't make liveness probe too aggressive A liveness probe that checks external dependencies (DB, cache) will restart your pod when the dependency is down — not your app. Under load spikes, timeoutSeconds: 1 will cause cascading restarts. Liveness should only check the application's own internal health (e.g. can it handle requests at all).

What a good /healthz endpoint looks like

// Go example — liveness checks ONLY internal state
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    if isDeadlocked() || threadPoolExhausted() {
        w.WriteHeader(http.StatusServiceUnavailable)
        return
    }
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("ok"))
})

// Readiness checks dependencies too
http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
    if err := db.Ping(); err != nil {
        w.WriteHeader(http.StatusServiceUnavailable)
        return
    }
    w.WriteHeader(http.StatusOK)
})

🟢 Startup Probe — Protect Slow Starters

Before startup probes existed (added in 1.18, GA in 1.20), the only way to handle slow-starting apps was setting a large initialDelaySeconds on liveness — which meant waiting that long for every restart, slowing recovery. The startup probe solves this elegantly.

How startup probe works

  • While the startup probe is pending or failing, liveness and readiness probes are disabled.
  • Once the startup probe succeeds once, it hands off to liveness and readiness.
  • If the startup probe fails failureThreshold × periodSeconds total time, the container is killed (same as liveness).
# Pattern: give a slow app up to 5 minutes to start
# then switch to a tight 15s liveness cycle
startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30    # 30 × 10s = 300s max startup window
  periodSeconds:    10

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  periodSeconds:   15
  failureThreshold: 3   # tight once running
💡 CrashLoopBackOff is often a probe problem If you see CrashLoopBackOff on a slow-starting app, the liveness probe is likely killing it before it finishes starting. Add a startup probe with a generous failureThreshold × periodSeconds budget matching your worst-case startup time.

🧠 Knowledge Check

Q1. A pod shows READY 0/1 but STATUS Running. What is happening?

A) The container has crashed and is waiting to restart
B) The readiness probe is failing — pod is running but removed from Service endpoints
C) The liveness probe failed and the container will be restarted
D) The startup probe is still running

Q2. Why should a liveness probe NOT check external dependencies like a database connection?

A) The database endpoint is outside the cluster network and unreachable from the kubelet
B) External checks are not supported by the httpGet mechanism
C) A dependency outage would restart all pods needlessly, worsening the cascade — use readiness for dependency checks
D) Liveness probes only support the exec mechanism for custom checks

Q3. A Java app takes up to 3 minutes to start. What is the correct probe configuration?

A) Set initialDelaySeconds: 180 on the liveness probe
B) Add a startup probe with failureThreshold: 18 and periodSeconds: 10 (180s budget)
C) Remove the liveness probe entirely for slow-starting apps
D) Set restartPolicy: Never to prevent premature restarts

Q4. What does successThreshold: 2 on a readiness probe do?

A) The container must fail twice before being removed from endpoints
B) The probe runs twice per period for increased accuracy
C) The pod requires 2 consecutive successes before being re-added to Service endpoints — prevents flapping
D) This is invalid — successThreshold must always be 1