kubernetes kubectl · ·

Debug Kubernetes CrashLoopBackOff in 30 Seconds

Quick command to instantly find why your pod is crash-looping.

Debug Kubernetes CrashLoopBackOff in 30 Seconds

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:

  1. The app itself errored on startup. Exit code 1, and the --previous logs 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.

  2. 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.

  3. 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 initialDelaySeconds or add a startupProbe so 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.

Get the next article in your inbox

Practical DevOps tips, tutorials, and guides. No spam, unsubscribe anytime.