# How to Fix CrashLoopBackOff in Kubernetes

URL: https://www.devopsness.com/blog/fix-crashloopbackoff
Author: Kiril Urbonas
Published: 2026-07-08
Updated: 2026-08-30
Categories: DevOps
Tags: Kubernetes, Containers, DevOps, Debugging, Operations

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

---
# How to Fix CrashLoopBackOff in Kubernetes

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:

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

Six restarts in four minutes. Now describe it:

```
kubectl describe pod api-7d9f8c6b4-2xqzk
```

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

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

```
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:

```
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:

```
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:

```
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:

```
  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](/blog/kubernetes-error-troubleshooting-guide) 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.

