How do you troubleshoot a pod stuck in CrashLoopBackOff, ImagePullBackOff, or Pending?
Quick Answer
CrashLoopBackOff means the container starts but keeps crashing — check logs, events, and resource limits. ImagePullBackOff means Kubernetes cannot pull the container image — verify image name, tag, registry access, and pull secrets. Pending means no node can accept the Pod — check resource requests, node capacity, taints, and affinity rules.
Detailed Answer
Think of these three states like three different reasons your car won't get you to work. CrashLoopBackOff is like the engine starting but immediately stalling — the car turns on but something internal keeps failing. ImagePullBackOff is like the car not having fuel — you can't even get the engine started because a required input is missing. Pending is like being stuck in the driveway because all the roads are blocked — your car works fine but there's nowhere for it to go.
CrashLoopBackOff means the container starts, runs for some time (could be milliseconds), then exits with a non-zero exit code. Kubernetes restarts it, it crashes again, restarts again, crashes again — and the backoff delay grows exponentially (10s, 20s, 40s, up to 5 minutes). Your debugging workflow: First, kubectl logs <pod> to see stdout/stderr output — 90% of the time the application prints the error (missing database connection, invalid config, null pointer exception). If the container crashes too fast for logs, try kubectl logs <pod> --previous to see the last container's output before it was replaced. Second, kubectl describe pod <pod> and read the Events section — it shows the restart count, exit codes (137 = OOMKilled, 1 = application error, 139 = segfault), and timestamps. Third, check if resource limits are too tight — a 64Mi memory limit on a Java app guarantees OOMKill.
ImagePullBackOff means the kubelet tried to pull the container image and failed. Common causes: typo in image name or tag (payments-api:latests instead of :latest), the image doesn't exist in the registry, the registry requires authentication and no imagePullSecret is configured, or network policies/firewall rules block outbound access to the registry. Debug by running kubectl describe pod <pod> and reading the exact error in Events: 'repository not found', 'unauthorized', 'manifest unknown' each point to different root causes. Test image accessibility by running crictl pull <image> directly on the node.
Pending means the Pod has been created in etcd but no node can accept it. The Scheduler cannot find a node that passes all filters. Common causes: insufficient CPU or memory on all nodes (the Pod requests 4Gi but all nodes only have 2Gi available), no node matches the Pod's nodeSelector or nodeAffinity (requesting a GPU node when none exist), all matching nodes have taints that the Pod doesn't tolerate (master nodes tainted with NoSchedule), or a PersistentVolumeClaim referenced by the Pod can't be bound (no matching PV exists). Debug by running kubectl describe pod <pod> and checking the Events section for messages like 'Insufficient cpu', '0/3 nodes are available: 3 node(s) had taint'. Also check kubectl describe nodes to compare Allocatable resources versus current allocations.
A production gotcha that catches teams repeatedly: CrashLoopBackOff with exit code 137 often looks like an application bug but is actually an OOMKill — the kernel killed the process for exceeding its memory limit. The application logs may show nothing because the process was killed externally. Check kubectl describe pod for 'OOMKilled' in the Last State section, and look at the container's memory limit versus actual memory usage from kubectl top pod. Java applications are notorious for this because JVM heap + metaspace + thread stacks + native memory often exceeds the container memory limit.