Liveness/Readiness Probe Failed, Connection Refused · Context Deadline: A 30-Second Decision Tree
K8s_Troubleshooting_Guide, Part 21
The Pod is clearly Running, but the RESTARTS count keeps climbing, or traffic never reaches the Service. Run kubectl describe pod and you’ll see a single probe failed line at the bottom of Events. This runbook is built so you can Ctrl+F that exact error string and jump straight to the fix. Minimal theory — just follow: console string → root cause → copy-paste YAML.
Step 1: Which probe is the culprit? (symptom decision table)
Identify the probe type first. The symptom almost always tells you which one it is.
| Symptom | Culprit probe | Pod behavior | Key takeaway |
|---|---|---|---|
| RESTARTS keep climbing; container dies and is recreated | Liveness | Container is force-killed and restarted | Killing a still-alive process |
Pod stays up but traffic never arrives (no IP in kubectl get endpoints) | Readiness | Removed from Endpoints; no restart | Not ready to receive traffic |
| Slow-starting app is killed by Liveness during boot → infinite restart loop | Missing Startup | Liveness fires before boot completes | Protect with startupProbe |
Key rule: Liveness failure = restart, Readiness failure = removed from Endpoints (traffic blocked). Restart loop → suspect Liveness first. 502 / connection failures → suspect Readiness first.
Step 2: Where to read the exact error string
# — describe Events
kubectl describe pod <pod-name> | tail -30
# when
kubectl get events --sort-by=.lastTimestamp -n <ns>The probe message is printed verbatim on the Warning Unhealthy line in the Events section. Find that one line in the table below.
Step 3: Exact-match table for error strings
| String printed in the console | Cause | Immediate action |
|---|---|---|
Liveness probe failed: HTTP probe failed with statuscode: 500 | The app health endpoint itself returns 500 (DB/cache dependency failure) | Check app /healthz logic and DB/Redis connectivity |
Readiness probe failed: connection refused | App is not listening on that port yet / still starting | Boot delay → raise initialDelay or add startupProbe |
Liveness probe failed: Get "http://...": context deadline exceeded | No response within timeoutSeconds | Raise timeoutSeconds + check app response time |
Readiness probe failed: dial tcp 10.x.x.x:8080: connect: connection refused | containerPort ≠ probe.port mismatch | Make the port numbers match |
Step 4: Decision tree
probe failed found
├─ connection refused / dial tcp refused
│ ├─ Port numbers differ → fix so containerPort == probe.port
│ └─ Ports match (still booting) → raise initialDelaySeconds or add startupProbe
├─ context deadline exceeded → raise timeoutSeconds (1→3) + check app latency
├─ statuscode: 500 → check health-endpoint dependencies (DB/cache) (NOT a probe-config issue!)
└─ Restart loop but the app is healthy → Liveness is too aggressive; loosen thresholdsStep 5: Four copy-paste YAML snippets
① Slow-booting Spring/JVM apps — startupProbe + relaxed Liveness
For apps with a long cold start, protect them with startupProbe so Liveness only runs after startup completes.
startupProbe:
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30 # 30 × 10s = up to 5 min for startup
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3Liveness/Readiness wait until startupProbe succeeds. The app will no longer be killed by Liveness during boot.
② connection refused — fix httpGet path/port
containerPort and the probe port must be identical.
ports:
- containerPort: 8080 # port the app actually listens on
readinessProbe:
httpGet:
path: /healthz
port: 8080 # must match containerPort above!③ context deadline exceeded — adjust the timeout
The default timeoutSeconds: 1 is too tight for JVM apps that pause briefly for GC.
livenessProbe:
httpGet:
path: /healthz
port: 8080
timeoutSeconds: 3 # 1 → 3
periodSeconds: 10
failureThreshold: 3④ Choosing tcpSocket vs httpGet
For DBs, message queues, and anything without an HTTP endpoint, tcpSocket (just check that the port is open) is the right probe.
readinessProbe:
tcpSocket:
port: 5432 # check that the port is open
periodSeconds: 5
# grpc field is GA in 1.29+ for gRPC apps
livenessProbe:
grpc:
port: 50051
periodSeconds: 10Practitioner note: Don’t set Liveness too aggressively
The most common self-inflicted outage in production is setting Liveness so tight that it kills a healthy app. A brief slowdown under a traffic spike makes Liveness fail → restart → traffic piles onto a cold new Pod → slowdown again → another restart. You amplify the incident yourself. Keep Liveness loose so it only catches true unrecoverable deadlocks; leave transient slowness to Readiness. Also, in service-mesh environments like Istio, readiness false positives are common because of sidecar startup order (the app is up but Envoy isn’t yet). Guarantee order with startupProbe + sidecar holdApplicationUntilProxyStarts.
Recommended thresholds to prevent recurrence
| Probe | failureThreshold | periodSeconds | timeoutSeconds | Notes |
|---|---|---|---|---|
| Liveness | 3 | 10 | 1 → raise to 3 | Don’t be aggressive; catch deadlocks only |
| Readiness | 3 | 5 | 1–2 | For traffic control; frequent checks are OK |
| Startup | 30 | 10 | 1 | 30×10s = up to 5 minutes for startup |
References: official docs
The primary source for the behavior, settings, and errors covered here is the official documentation below. Check it for version-specific options and exact semantics.
FAQ
Q. The Pod is Running but traffic never arrives. Why?
A. Most likely a Readiness probe failure. Check whether the Pod IP is missing with kubectl get endpoints <svc>. A Readiness failure removes the Pod from Endpoints without restarting it.
Q. RESTARTS keep climbing. There are no errors in the app logs.
A. Liveness is too aggressive. Raise timeoutSeconds from 1 to 3. For slow-booting apps, add a startupProbe (failureThreshold: 30) so Liveness does not run until startup completes.
Q. Can I fix statuscode: 500 by changing the probe config? A. No. 500 means the health endpoint actually returned 500 — it is not a probe-config problem. Check connectivity to dependencies such as DB and cache.
Part 22 covers a runbook that matches CrashLoopBackOff to causes by exit code.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.