Skip to main content
How to Fix CrashLoopBackOff in Kubernetes

How to Fix CrashLoopBackOff in Kubernetes

KU
• 6 min read•Updated •57 views

CrashLoopBackOff means your container keeps dying on startup and Kubernetes keeps restarting it slower each time. Here's the diagnosis flow we actually use.

A pod goes CrashLoopBackOff and half the team starts guessing. It's almost never a mystery once you read the right output, and the order you read it in matters more than any single command.

Here's what the status actually means. Your container starts, runs for a bit (sometimes milliseconds), and exits. Kubernetes restarts it. It exits again. After a few rounds the kubelet stops restarting immediately and starts waiting: 10s, 20s, 40s, doubling up to a 5-minute cap. That growing delay is the "backoff." The "crashloop" is the part where your process refuses to stay up. So CrashLoopBackOff is not an error in itself, it's Kubernetes telling you it's tired of restarting something that won't stay alive.

The container is crashing. Your job is to find out why it exited, not why Kubernetes backed off.

The diagnosis flow

Start here, every time. First look at the pod:

code
kubectl get pods
NAME                        READY   STATUS             RESTARTS   AGE
api-7d9f8c6b4-2xqzk         0/1     CrashLoopBackOff   6          4m12s

Six restarts in four minutes. Now describe it:

code
kubectl describe pod api-7d9f8c6b4-2xqzk

Scroll to the container state and the events at the bottom. This is the single most useful block:

code
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1
      Started:      Wed, 08 Jul 2026 10:22:14 +0000
      Finished:     Wed, 08 Jul 2026 10:22:14 +0000
    Restart Count:  6

Started and Finished at the same second means the process died on boot. The exit code is your first real clue (more on codes below).

Now the opinionated part: read the previous container's logs before anything else.

code
kubectl logs api-7d9f8c6b4-2xqzk --previous

The --previous flag is the whole game. Without it, kubectl logs shows the current container, which may be freshly restarted and empty, or already dead again by the time you run it. --previous shows the instance that actually crashed. Nine times out of ten the stack trace or panic is sitting right there, and you can skip the guessing entirely.

If logs are empty, check events across the namespace:

code
kubectl get events --sort-by=.lastTimestamp

That surfaces the things logs won't: failed mounts, image pull problems, OOM kills, probe failures.

Common causes and how to fix them

Application error or unhandled exception (exit 1). The most common case. Your app throws on startup: a nil pointer, an unhandled promise rejection, a bad migration. --previous logs show the trace. Fix the code or roll back the image. Exit code 1 is a generic application failure, so treat the logs as truth, not the code.

Bad or missing config / env var. The app starts, can't find DATABASE_URL or a required flag, and bails. Logs usually say something like panic: required env DATABASE_URL not set. Check what the pod actually received:

code
kubectl exec api-7d9f8c6b4-2xqzk -- env | grep DATABASE

If the pod won't stay up long enough to exec, inspect the ConfigMap and Secret refs in the deployment spec directly. A typo'd configMapKeyRef name silently gives you nothing.

Failed dependency (DB not reachable). The app boots, tries to connect to Postgres or Redis, times out, and exits instead of retrying. Logs show connection refused or a DNS failure. Two fixes: make the app retry with backoff instead of dying, and confirm the dependency is actually up and its Service resolves:

code
kubectl exec api-7d9f8c6b4-2xqzk -- nslookup postgres.default.svc.cluster.local

A hard crash on a transient dependency blip is a bug in your app, not in Kubernetes. Startup dependencies should be patient.

Failing liveness probe killing a healthy app. This one fools people. The app is fine but slow to warm up, the liveness probe hits /healthz before the app is ready, fails, and the kubelet kills the container. Restart, repeat, CrashLoopBackOff. You'll see it in describe:

code
  Warning  Unhealthy  kubelet  Liveness probe failed: HTTP probe failed with statuscode: 500
  Normal   Killing    kubelet  Container failed liveness probe, will be restarted

The fix is a startupProbe, or a longer initialDelaySeconds / failureThreshold on the liveness probe. Liveness should catch a wedged app, not punish a slow boot.

OOMKilled (exit 137). The container hit its memory limit and the kernel killed it. Describe shows Reason: OOMKilled and Exit Code: 137 (128 + signal 9). Either the limit is too low or the app is leaking. Raise resources.limits.memory if the workload legitimately needs it, or fix the leak. Don't just keep bumping the limit forever.

Missing file or permission denied. Often after switching to a non-root user or a read-only filesystem. Logs show permission denied or no such file or directory. Check your securityContext, volume mounts, and whether the image expects to write somewhere it no longer can.

Wrong command or entrypoint (exit 127 / 126). Exit 127 is "command not found," 126 is "found but not executable." Usually a bad command: / args: override in the manifest, or a typo in the binary path. The container never really runs your app at all.

Exit codes worth memorizing

  • 0 — clean exit. If it's crashlooping on 0, your process finishes and has nothing to keep it running.
  • 1 — generic app error. Read the logs.
  • 126 / 127 — entrypoint not executable / not found.
  • 137 — SIGKILL, almost always OOMKilled or a probe kill.
  • 139 — SIGSEGV, a segfault.
  • 143 — SIGTERM, graceful shutdown that exited too fast.

The checklist

Run these in order and you'll rarely get stuck:

  1. kubectl get pods — confirm the status and restart count.
  2. kubectl describe pod <pod> — read Last State, Reason, and Exit Code.
  3. kubectl logs <pod> --previous — read the crash from the dead container first.
  4. kubectl get events --sort-by=.lastTimestamp — catch mounts, probes, OOM, image pulls.
  5. Map the exit code (137 = memory, 127 = entrypoint, 1 = app error).
  6. Reproduce locally with the same image and env if the logs aren't enough.
  7. Check probes before assuming the app is broken.

If you want a broader reference for the surrounding failure modes, our Kubernetes troubleshooting guide covers the pod lifecycle states around this one.

The call we'd make

Read --previous logs first, always. Everything else is confirmation. The exit code narrows it (137 sends you to memory and probes, 127 to your entrypoint, 1 to the application), but the crashed container's own log line is what tells you the truth. Resist the urge to bump memory limits or loosen probes before you've actually read why the thing died, because the fastest fix is the one aimed at the real cause.

Explore topics:KubernetesDevOps
React
Share this post
KU

Kiril Urbonas

AI Engineer

553 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.