KubernetesIX · Pod LifecyclePod lifecycle
Pod phases — Pending, Running, Succeeded, Failed, Unknown
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
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
| Phase | Meaning | Set when |
|---|---|---|
| Pending | Accepted by the cluster but not yet running | API server creates the Pod record; transitions out when bound |
| Running | Bound to a node; at least one container is running, starting, or restarting | kubelet reports the first container as running/started |
| Succeeded | All containers terminated successfully (exit 0) and will not be restarted | kubelet reports all containers exited 0 and restartPolicy != Always |
| Failed | At least one container terminated in failure and will not be restarted | kubelet reports at least one container exited non-zero and restartPolicy = Never |
| Unknown | State cannot be obtained; typically the kubelet cannot communicate with the API server | kubelet 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: OnFailureorNever. 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-Processescovers process states; Pod phases are the cluster-level equivalent of process lifecycle states. - The Ansible course part
XXXV-Ansible-Scriptingcovers service lifecycle; Pod phases are the cluster-level equivalent for containerised services. - The Docker course part
XXX-Docker-Lifecyclecovers container states (created, running, paused, exited, dead); Pod phases aggregate container states.
Quiz
Knowledge check · 4 questions
Q1. Which Pod phase signals that the kubelet has not been able to report the Pod's state?
Q2. A Pod in `Running` is healthy and ready to serve traffic.
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`.
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.phasefirst. It is the first triage signal: Pending and Unknown are actionable; Running needs deeper inspection; Succeeded and Failed are terminal. - Read
status.conditionsfor fine-grained state.conditions[].type=Readytells you if the Pod can serve traffic;conditions[].type=PodScheduledtells you if the scheduler has bound it. - Watch for ImagePullBackOff and CrashLoopBackOff.
These are status signals from
containerStatuses[*].statethat indicate specific failure modes. - Tune
pod-eviction-timeoutfor 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!=Runningexcluding terminal states. This is the production alerting query for actionable Pods.