CloudOpsGuide
kubernetes

Kubernetes Pod Not Ready: Debugging Guide

Beginner
10 minutes
October 2026
CloudOpsGuide Team

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

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

ParameterMeaningDefault
initialDelaySecondsWait before first probe0
periodSecondsTime between probes10
timeoutSecondsProbe timeout1
successThresholdSuccesses needed to mark ready1
failureThresholdFailures before not-ready3

Total max wait = initialDelaySeconds + (failureThreshold × periodSeconds)

Readiness vs Liveness — Critical Difference

ReadinessLiveness
Failure resultRemoved from Service endpointsContainer 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


Last Updated: October 2026
Author: CloudOpsGuide Team
Difficulty: Beginner
Estimated Reading Time: 10 minutes