skip to content
‹ All posts

CrashLoopBackOff vs ImagePullBackOff vs Error: a pod status taxonomy

A field map of every Kubernetes pod status - CrashLoopBackOff, ImagePullBackOff, Pending, Error - and the first command to run for each one.

#kubernetes

kubectl get pods gives you one word per pod, and that word is doing a lot of work. Every Kubernetes pod status is a compressed diagnosis: it tells you which subsystem gave up, which means it tells you where to look first. Engineers who resolve incidents fast aren't faster at typing - they've internalized the map from status to entry point, so they skip the ten minutes of flailing.

This post is that map. For each status: what it actually means, the one command that opens the right door, and a link to the full field guide where one exists.

One clarification before we start: the STATUS column is not a single field. It's a summary computed from the pod's phase, container states, and waiting reasons. CrashLoopBackOff isn't a phase - it's a container waiting reason surfaced to save you a describe. That's why the same underlying failure can show different statuses at different moments.

The taxonomy: every Kubernetes pod status, decoded

Pending

The pod object exists, but no containers have started. Either the scheduler can't place it, or the node accepted it and is stuck preparing (pulling images, attaching volumes).

kubectl describe pod <pod> | sed -n '/Events:/,$p'

If you see FailedScheduling, read the scheduler's arithmetic - it lists exactly how many nodes failed and why (Insufficient cpu, node(s) had untolerated taint). The complete decision tree is in Pod stuck in Pending: the complete diagnostic tree. If events show FailedMount or FailedAttachVolume, it's a storage problem, not a scheduling one.

ContainerCreating

Scheduled, but the kubelet is still assembling the sandbox. Normal for a few seconds. Stuck for minutes means volumes, secrets, or the CNI plugin. Same command: describe, read events. A pod stuck here with no events at all often means the kubelet itself is unwell - check journalctl -u kubelet on the node.

ImagePullBackOff / ErrImagePull

The runtime can't fetch the image. ErrImagePull is the attempt failing; ImagePullBackOff is Kubernetes waiting longer between retries. The event text contains the actual registry error:

Failed to pull image "myapp:v1.2.4": rpc error: code = NotFound

NotFound is a typo'd tag; unauthorized is a credentials problem; toomanyrequests is a rate limit. Ordered checklist, fastest first, in Fixing ImagePullBackOff: every cause, fastest check first.

CreateContainerConfigError

The image is fine - the configuration the container needs doesn't exist. Almost always a ConfigMap or Secret referenced by name (or key) that isn't there:

Error: configmap "app-config" not found

Full decoding of every variant in CreateContainerConfigError: the ConfigMap/Secret mistakes behind it.

CrashLoopBackOff

The container starts, dies, and Kubernetes is backing off between restarts (10s doubling to a cap of 5 minutes). The status tells you nothing about why it dies - that's in the previous container's logs:

kubectl logs <pod> --previous

The eight real causes and their fastest paths are in Debugging CrashLoopBackOff: a field guide.

Error

A one-shot container (usually a Job) exited non-zero and won't be restarted, or a pod-level failure occurred. Same first move as CrashLoopBackOff: read the logs, then read the exit code in Last State.

OOMKilled

Technically a termination reason, not a status, but you'll see it in describe constantly: the kernel killed the container for exceeding its memory limit. Exit code 137. Diagnosis and the JVM gotchas live in OOMKilled and exit code 137: finding the real memory hog.

Completed

Exit code 0. For a Job, this is success. For a Deployment, it's a bug - your container ran to completion instead of serving, and Kubernetes will restart it, eventually producing CrashLoopBackOff with exit code 0. Usually a wrong entrypoint or a foregrounding mistake (nginx without daemon off;).

Terminating

Deletion requested, but the pod won't go. Either the app is ignoring SIGTERM (wait out terminationGracePeriodSeconds), or a finalizer is blocking the delete. If it's been stuck for hours, someone force-deleted the wrong thing or a controller is down.

Evicted / Unknown

Evicted: the node ran out of a resource and the kubelet sacrificed this pod - the message in describe names the resource. Unknown: the node stopped reporting; the pod may be running fine, but the control plane can't see it. Both are node problems wearing a pod costume.

The three questions that classify any pod failure

If you forget the table, you can reconstruct it. Every pod status answers three questions:

  1. Did it schedule? No → Pending. Scheduler or resource problem.
  2. Did the container start? No → ImagePullBackOff, CreateContainerConfigError, ContainerCreating. Image, config, or node problem.
  3. Did it stay up? No → CrashLoopBackOff, Error, OOMKilled. Application or resource problem - and now the exit code is your best witness. 137 is SIGKILL (usually OOM), 143 is SIGTERM, 1 is the app's own decision, 126/127 are entrypoint mistakes. The full table is in Kubernetes exit codes explained.

The order matters. A CrashLoopBackOff investigation that starts with the scheduler is wasted motion; a Pending investigation that starts with application logs will find no logs to read. In a graded incident, that wasted motion is exactly what separates transcripts - this taxonomy is the difference between a straight line and a random walk.

Statuses that lie (or at least mislead)

  • Running doesn't mean working. A pod can be Running and failing its readiness probe, receiving no traffic. kubectl get pods shows READY 0/1 - read both columns.
  • CrashLoopBackOff with exit code 0 means the app is exiting successfully - Kubernetes restarts it because a Deployment expects a long-running process.
  • Init: prefixes (Init:0/2, Init:CrashLoopBackOff) mean the main container is innocent; debug the init container with kubectl logs <pod> -c <init-container>.
  • A healthy status right after apply proves nothing. Watch it: kubectl get pods -w for two backoff cycles before declaring victory.

FAQ

What's the difference between pod phase and pod status? Phase is the coarse five-value field on the pod object (Pending, Running, Succeeded, Failed, Unknown). The STATUS column in kubectl get pods is a friendlier summary that also surfaces container waiting reasons like CrashLoopBackOff - which is why you see values there that aren't phases.

Why does my pod alternate between Error and CrashLoopBackOff? Same failure, two moments. Error is the instant the container exits non-zero; CrashLoopBackOff is the backoff wait before the next restart. Diagnose it as one problem: kubectl logs --previous.

How do I see why a pod has a given status? kubectl describe pod <pod> and read from the bottom: Events, then the container's State/Last State blocks, which carry the reason and exit code.

Is there a single command to triage everything at once? kubectl get pods -o wide for the overview, then kubectl get events --sort-by=.lastTimestamp -n <namespace> - the sorted event stream is the closest thing to a cluster-wide narration of what went wrong, in order.