Debugging CrashLoopBackOff: Reading Pod Logs and Events Like a Pro
CrashLoopBackOff is the status every Kubernetes operator learns to dread, and the one most often misread. It is not an error in itself — it is Kubernetes telling you a container keeps starting and exiting, so it is backing off before trying again. The real cause is somewhere else. This is a systematic way to find it.
What the status actually means
A Pod enters CrashLoopBackOff when its container exits (cleanly or by crashing) and the restart policy brings it back, repeatedly. Each failure increases the back-off delay (10s, 20s, 40s, up to 5 minutes), which is why a crashing Pod looks "stuck" — Kubernetes is deliberately waiting between attempts.
Step 1: read the events
Start with describe. The Events section at the bottom is where the kubelet records why it killed or failed to start the container.
kubectl describe pod web-7d9c-abc12
Look for clues: Back-off restarting failed container, OOMKilled, Liveness probe failed, Error: ImagePullBackOff. The reason here often ends the investigation immediately.
Step 2: read the logs — including the previous container
Current logs show the latest (possibly still-starting) attempt. The crash happened in the previous container, so reach for --previous:
kubectl logs web-7d9c-abc12 --previous
For multi-container Pods, target the container explicitly:
kubectl logs web-7d9c-abc12 -c app --previous
This is where you usually find the stack trace, the missing env var, or the "connection refused" that explains everything.
Step 3: check the exit code
The container status carries an exit code that narrows things fast:
kubectl get pod web-7d9c-abc12 -o jsonpath='{.status.containerStatuses[0].lastState.terminated}'
- Exit 0 — the process completed and exited. Common when a long-running service is actually a one-shot script. Use a Job, or keep the process in the foreground.
- Exit 1 / 2 — application error. Read the logs.
- Exit 137 — SIGKILL, almost always OOMKilled. Raise the memory limit or fix the leak.
- Exit 143 — SIGTERM; the container did not shut down within the grace period.
Common root causes
Misconfiguration
A missing or malformed ConfigMap/Secret value, a bad connection string, or a required env var that is not set. The app starts, fails to read config, and exits.
Failing health probes
An overly aggressive liveness probe kills a container that is simply slow to start. Add a startupProbe or raise initialDelaySeconds:
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 5
Out of memory
Exit 137 with an OOMKilled event means the container exceeded its memory limit. Profile real usage, then set a realistic limit.
Dependency not ready
The app crashes because the database or a downstream service is not reachable yet. Add retries with backoff in the app, or an init container that waits for the dependency.
Step 4: get inside, if needed
If logs are not enough, debug a running or ephemeral container:
kubectl exec -it web-7d9c-abc12 -- sh
kubectl debug -it web-7d9c-abc12 --image=busybox --target=app
Doing this faster
The friction in CrashLoopBackOff debugging is context-switching: describe in one terminal, previous-logs in another, watching restart counts in a third. A tool like Kubexer collapses that loop — restart spikes surface as alerts, the events and previous-container logs are one click away, and you can search across containers for the one line that matters instead of scrolling. The methodology is the same; you just stop juggling terminals.
Wrapping up
Treat CrashLoopBackOff as a pointer, not a verdict. Events tell you the category, previous-container logs tell you the detail, and the exit code confirms the theory. Work them in that order and most crash loops resolve in minutes.