Debug Kubernetes CrashLoopBackOff in 30 Seconds
Quick command to instantly find why your pod is crash-looping.
The Problem
A pod is stuck in CrashLoopBackOff. The container starts, dies, and Kubernetes restarts it with an exponential backoff delay (10s, 20s, 40s, capped at 5 minutes). On Kubernetes 1.30 and later the behavior is unchanged, so this approach holds across current clusters. You need the reason, not a tour of every possible cause.
The one command that matters
kubectl logs <pod-name> --previous
The --previous flag (short form -p) prints stdout/stderr from the container instance that just died, not the one currently spinning up. That dead instance is where the stack trace, config error, or panic lives. Without --previous you often get an empty result, because the new container has not produced output yet before crashing again.
If the pod has more than one container, name it explicitly:
kubectl logs <pod-name> -c <container-name> --previous
Read the exit code from describe
Logs tell you what the application said. The exit code tells you how it died. Pull it from the pod’s last state:
kubectl describe pod <pod-name> | grep -A 5 "Last State"
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
The exit code points you straight at a category:
| Code | Meaning | Quick check |
|---|---|---|
| 1 | Application error | Read --previous logs for the stack trace |
| 137 | OOMKilled (out of memory) | Compare usage against resources.limits.memory |
| 139 | Segfault | Wrong CPU arch or a bad native binary |
| 143 | SIGTERM not handled | App ignored graceful shutdown |
The three most common culprits
Most crash loops fall into one of three buckets. Check them in this order:
-
The app itself errored on startup. Exit code 1, and the
--previouslogs show a stack trace or a thrown exception. Usually a missing env var, an unreachable database, or a bad config file. Fix: read the actual error, do not guess. -
OOMKilled. Exit code 137 with
Reason: OOMKilled. The container hit its memory limit and the kernel killed it. Confirm the limit and current usage:kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources.limits}' kubectl top pod <pod-name>Fix: raise
resources.limits.memory, or fix the leak. -
Liveness probe killing a healthy-but-slow container. Here the logs look fine and the exit code is often 137 (the kubelet sent SIGKILL after the probe failed). The app is just slow to start. Check the probe timing:
kubectl describe pod <pod-name> | grep -A 3 "Liveness"Fix: increase
initialDelaySecondsor add astartupProbeso a slow boot is not mistaken for a hang.
Watch the restart count live
To see whether your fix actually stopped the loop, watch the restart counter instead of re-running the command by hand:
$ kubectl get pod <pod-name> -w
The RESTARTS column stays flat once the pod is stable. If it keeps climbing, the container is still dying and you have the wrong fix. For a continuous view across a namespace, see monitor pods live with kubectl get -w.
Why –previous works
The kubelet retains the log stream and termination state of the last exited container even after it restarts the pod. That retained record is exactly what you need: the final words of the process that failed. Reach for --previous first, read the exit code second, and you will have the cause in well under a minute.
When the exit code points at memory, the debug OOMKilled pods step by step walkthrough covers limits and requests in depth. For the full failure matrix beyond these three culprits, see why did my pod die. The official reference for probe fields and timing is the Kubernetes liveness and readiness docs.
Related articles
Get the next article in your inbox
Practical DevOps tips, tutorials, and guides. No spam, unsubscribe anytime.