Skip to main content
RunBook Academy

KubernetesIX · Pod LifecyclePod lifecycle

Pod phases — Pending, Running, Succeeded, Failed, Unknown

Intermediate⏱ ~16 minkubectl

What you'll learn

  • Identify the five Pod phases and the transitions between them
  • Diagnose why a Pod is Pending
  • Distinguish Succeeded, Failed, and the role of restartPolicy
  • Recognise Unknown as a signal of kubelet unreachability

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

A Pod’s status.phase is the most basic state field — a single string that summarises where the Pod is in its lifecycle. This lesson walks through the five phases, explains when the API server sets each, and shows the diagnostic patterns that follow from each phase.

The five phases

status:
  phase: Running
PhaseMeaningSet when
PendingAccepted by the cluster but not yet runningAPI server creates the Pod record; transitions out when bound
RunningBound to a node; at least one container is running, starting, or restartingkubelet reports the first container as running/started
SucceededAll containers terminated successfully (exit 0) and will not be restartedkubelet reports all containers exited 0 and restartPolicy != Always
FailedAt least one container terminated in failure and will not be restartedkubelet reports at least one container exited non-zero and restartPolicy = Never
UnknownState cannot be obtained; typically the kubelet cannot communicate with the API serverkubelet has not reported in a long time
stateDiagram-v2
    [*] --> Pending
    Pending --> Running: bound, at least one container started
    Running --> Running: container restarts (Always / OnFailure)
    Running --> Succeeded: all containers exit 0, restartPolicy != Always
    Running --> Failed: container exits non-zero, restartPolicy = Never
    Running --> Unknown: kubelet stops reporting
    Unknown --> Running: kubelet recovers
    Unknown --> Failed: deletion timeout exceeded

The phase field is intentionally coarse. For finer-grained state, look at status.conditions (Part IX-03) and status.containerStatuses[*].state (Part IX-02).

Pending — waiting for something

A Pod is Pending from creation until it is bound to a node and at least one container has started. Common reasons for a long Pending state:

  • Scheduling failures: no node fits the Pod (insufficient resources, taints without tolerations, nodeSelector mismatch, affinity/anti-affinity rules impossible).
  • Image pull failures: the kubelet cannot pull the image (network, registry auth, tag does not exist).
  • Volume mount failures: PVC unbound, CSI driver error, ConfigMap/Secret does not exist.
  • Sandbox creation failures: the runtime cannot create the pause sandbox (CNI error, OOM on the node).
kubectl describe pod web-7c8
# shows the events that explain why the Pod is Pending

The output includes Events with reasons like FailedScheduling, FailedMount, FailedCreatePodSandbox, ErrImagePull, ImagePullBackOff. The event reason is the fastest path to diagnosis.

Running — bound and active

A Pod is Running once it is bound to a node and at least one container has started. The Pod can have:

  • All containers Running: healthy state.
  • Some containers Waiting: e.g., init containers running or a sidecar waiting on a dependency.
  • Some containers Terminated: a Job container that ran successfully and exited; restartPolicy kept the Pod running because the main container is still running.

The Running phase is not a health indicator. A Pod in CrashLoopBackOff is technically Running — the kubelet is actively trying to restart the container. The status.containerStatuses[*].state field gives the real state.

kubectl get pod web-7c8 -o jsonpath='{.status.containerStatuses[*].state}' | jq
# shows each container's state: waiting, running, terminated

Succeeded — successful completion

A Pod reaches Succeeded when all containers have exited with code 0 and restartPolicy != Always. The Pod will not restart; the controller (if any) decides what to do next.

Succeeded is the normal terminal state for:

  • Jobs: restartPolicy: OnFailure or Never. A Job’s Pod terminates Succeeded when the work is done.
  • Init-only Pods: Pods that run init containers and exit without main containers.

For Deployments, a Pod does not reach Succeeded unless the application is configured to exit cleanly after one run — which is the wrong shape for a long-running workload.

Failed — failed termination

A Pod reaches Failed when at least one container has exited with a non-zero code and restartPolicy: Never. The Pod is terminated and will not restart.

Failed is the terminal state for:

  • Jobs that failed: the work could not be completed; the Job’s backoff mechanism (if any) decides what to do next.
  • CrashLoopBackOff with restartPolicy: Never: rare; the container crashed and there is no restart policy.

For Deployments, a Pod does not reach Failed because restartPolicy: Always keeps the Pod in Running even when containers exit non-zero (with backoff).

Unknown — kubelet is silent

A Pod is Unknown when the kubelet has not reported the Pod’s state in a while. The default timeout is node-monitor-grace-period (50 seconds in 1.34.x; configurable on the kube-controller- manager).

Unknown is a strong signal of kubelet unreachability:

  • Network partition between the kubelet and the API server.
  • Kubelet crash or hang.
  • Node-level failure that takes the kubelet with it.

The Pod’s controller (Deployment, ReplicaSet) does not act on Unknown Pods by default — they are not considered Failed. The node controller is the one that decides what to do: after the node-monitor-grace-period expires, the node is marked NotReady, and after pod-eviction-timeout (5 minutes by default), the Pods on the node are evicted (deleted) and recreated elsewhere.

Reading the phase field

kubectl get pods -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,PHASE:.status.phase,NODE:.spec.nodeName

Output:

NAMESPACE    NAME           PHASE      NODE
team-a-prod  web-7c8        Running    node-3
team-a-prod  web-9d2        Pending    <none>
team-a-prod  api-4f1        Running    node-1
batch        job-runner-1   Succeeded  node-2
batch        job-runner-2   Failed     node-4

The phase is the first triage signal. Pending and Unknown are the actionable phases for operators; Running requires deeper inspection of containerStatuses and conditions; Succeeded and Failed are terminal.

Cross-course references

  • The Linux course part VI-Linux-Processes covers process states; Pod phases are the cluster-level equivalent of process lifecycle states.
  • The Ansible course part XXXV-Ansible-Scripting covers service lifecycle; Pod phases are the cluster-level equivalent for containerised services.
  • The Docker course part XXX-Docker-Lifecycle covers container states (created, running, paused, exited, dead); Pod phases aggregate container states.

Quiz

Knowledge check · 4 questions

  1. Q1. Which Pod phase signals that the kubelet has not been able to report the Pod's state?

  2. Q2. A Pod in `Running` is healthy and ready to serve traffic.

  3. Q3. A Pod is stuck in `Pending` for 5 minutes. Walk through the diagnosis to find why.

    Pod `web-7c8` is part of a Deployment with 3 replicas. The other 2 replicas are Running on nodes 1 and 2. This Pod is Pending. The cluster has 5 worker nodes. The Pod requests `cpu: 4`, `memory: 8Gi`. The Pod has `nodeSelector: workload=high-memory`.

  4. Q4. What is the difference between `status.phase` and `status.conditions` on a Pod? When is each the right field to read?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Read status.phase first. It is the first triage signal: Pending and Unknown are actionable; Running needs deeper inspection; Succeeded and Failed are terminal.
  • Read status.conditions for fine-grained state. conditions[].type=Ready tells you if the Pod can serve traffic; conditions[].type=PodScheduled tells you if the scheduler has bound it.
  • Watch for ImagePullBackOff and CrashLoopBackOff. These are status signals from containerStatuses[*].state that indicate specific failure modes.
  • Tune pod-eviction-timeout for your environment. Unknown Pods linger until this timeout. A 5-minute default may be too long for high-traffic services.
  • Monitor kubectl get pods --field-selector=status.phase!=Running excluding terminal states. This is the production alerting query for actionable Pods.