Kubernetes Pod Not Ready: Debugging Guide
Kubernetes Pod Not Ready: Debugging Guide
Step-by-step guide to diagnose and fix pods stuck in NotReady state — the most common Kubernetes issue after CrashLoopBackOff.
Table of Contents
- What "Not Ready" Means
- Diagnostic Workflow
- Common Causes and Fixes
- Readiness Probe Deep Dive
- Quick Checklist
What "Not Ready" Means
A pod shows 0/1 READY and Running status when containers are up but the readiness probe fails. Traffic will NOT be routed to these pods by Services.
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# my-app-7d4b8c9f-x2abc 0/1 Running 0 5m
Running but not Ready = the app started but can't serve traffic yet.
Diagnostic Workflow
Step 1: Describe the Pod
kubectl describe pod my-app-7d4b8c9f-x2abc
Check the Events section at the bottom:
Warning Unhealthy 5s (x10 over 2m) kubelet Readiness probe failed:
Get "http://10.244.1.5:8080/health": dial tcp 10.244.1.5:8080: connect: connection refused
This tells you the exact probe URL failing.
Step 2: Check the Logs
kubectl logs my-app-7d4b8c9f-x2abc
# If the app takes time to boot, follow logs
kubectl logs -f my-app-7d4b8c9f-x2abc
Step 3: Test the Endpoint Manually
# Exec into the pod and test the readiness URL
kubectl exec -it my-app-7d4b8c9f-x2abc -- curl -v localhost:8080/health
# Test with wget if curl isn't installed
kubectl exec -it my-app-7d4b8c9f-x2abc -- wget -qO- localhost:8080/health
Step 4: Check the Probe Configuration
kubectl get pod my-app-7d4b8c9f-x2abc -o yaml | grep -A 10 readinessProbe
Common Causes and Fixes
1. Probe Hits Wrong Port or Path
# WRONG — app listens on 3000, probe checks 8080
readinessProbe:
httpGet:
path: /health
port: 8080
# FIX — match the actual container port
readinessProbe:
httpGet:
path: /health
port: 3000
2. App Needs Longer Startup Time
Java apps, apps loading caches, or apps running migrations can take 60s+ to be ready.
# Give it more time
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # wait before first probe
periodSeconds: 10
failureThreshold: 6 # allow 6 failures (60 more seconds)
Better solution — use a startupProbe for slow starters:
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 × 5s = 150s max startup
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
3. Dependency Not Ready (Database, Redis, etc.)
The app can't become ready because a dependency is down.
# Check if the dependency pod is running
kubectl get pods -l app=postgres
# Test connectivity from the app pod
kubectl exec -it my-app-7d4b8c9f-x2abc -- nc -zv postgres-service 5432
Fix: ensure the app's /ready endpoint correctly reflects dependency status, or make the app start serving before dependencies connect.
4. Readiness Endpoint Returns Non-2xx
The endpoint must return HTTP 200–399. Redirects to auth pages (302 → /login) WILL pass since 302 is acceptable — but 500 fails.
# Check what the endpoint actually returns
kubectl exec -it my-app-7d4b8c9f-x2abc -- curl -i localhost:8080/health
5. Resource Starvation
Pod runs but responds too slowly:
# Check if CPU throttling
kubectl top pod my-app-7d4b8c9f-x2abc
kubectl describe pod my-app-7d4b8c9f-x2abc | grep -A3 "QoS"
# Increase limits
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
6. Missing Health Endpoint
If the app has no /health endpoint, either add one or use a TCP probe:
readinessProbe:
tcpSocket:
port: 8080
periodSeconds: 5
A TCP probe passes if the port accepts connections — simpler but less accurate than HTTP.
7. Init Container Still Running
kubectl get pods
# my-app-7d4b8c9f-x2abc 0/1 Init:0/1 0 3m
# Check init container logs
kubectl logs my-app-7d4b8c9f-x2abc -c init-container-name
Readiness Probe Deep Dive
Probe Types
# HTTP probe (most common)
readinessProbe:
httpGet:
path: /ready
port: 8080
httpHeaders:
- name: X-Custom-Header
value: ready
# TCP probe (port connectivity only)
readinessProbe:
tcpSocket:
port: 5432
# Exec probe (run a command)
readinessProbe:
exec:
command:
- sh
- -c
- "pg_isready -U postgres"
Timing Parameters Explained
| Parameter | Meaning | Default |
|---|---|---|
initialDelaySeconds | Wait before first probe | 0 |
periodSeconds | Time between probes | 10 |
timeoutSeconds | Probe timeout | 1 |
successThreshold | Successes needed to mark ready | 1 |
failureThreshold | Failures before not-ready | 3 |
Total max wait = initialDelaySeconds + (failureThreshold × periodSeconds)
Readiness vs Liveness — Critical Difference
| Readiness | Liveness | |
|---|---|---|
| Failure result | Removed from Service endpoints | Container restarted |
| Use for | "Can I serve traffic?" | "Am I deadlocked/crashed?" |
| Typical endpoint | /ready (checks deps) | /health (checks self) |
Never put dependency checks in liveness probes — a database blip would restart all your pods.
Quick Checklist
When a pod shows 0/1 Running:
# 1. Events — what is failing?
kubectl describe pod <name> | tail -20
# 2. Logs — is the app logging errors?
kubectl logs <name> --tail=50
# 3. Probe config — correct port/path?
kubectl get pod <name> -o yaml | grep -A8 readinessProbe
# 4. Manual test — does the endpoint respond?
kubectl exec -it <name> -- curl -i localhost:<port>/<path>
# 5. Resources — throttled?
kubectl top pod <name>
# 6. Dependencies — reachable?
kubectl exec -it <name> -- nc -zv <dependency> <port>
Related Articles
- Fix Kubernetes CrashLoopBackOff
- Kubernetes Resource Limits and Requests
- kubectl get pods all namespaces
Last Updated: October 2026
Author: CloudOpsGuide Team
Difficulty: Beginner
Estimated Reading Time: 10 minutes