Platforms 4 min read

Fixing CrashLoopBackOff Without Guessing

CrashLoopBackOff is not an error, it is a symptom. Here is the order to check things in so you stop guessing and start narrowing.

By BashPilot Team

CrashLoopBackOff tells you a container keeps exiting and Kubernetes keeps restarting it with growing backoff. It does not tell you why. The why is always in one of three places, and checking them in the wrong order is what turns a five minute fix into an afternoon.

Start with the exit code

The exit code is the cheapest signal available and most people skip it. Exit 1 is an application error, 137 is a SIGKILL (usually the OOM killer), 143 is a SIGTERM, and 127 means the command in your entrypoint does not exist.

read the exit code and reason
kubectl get pod my-pod -o jsonpath='{.status.containerStatuses[0].lastState.terminated}'

Gives you exitCode, reason and the timestamps in one shot.

Then the previous container's logs

This is the single most common mistake. kubectl logs my-pod shows the current container, which just started and has not failed yet. You want the one that died:

logs from the crashed instance
kubectl logs my-pod --previous --tail=100

Then the events

Some failures never reach application logs at all, because the application never started. Failed image pulls, failed readiness probes, and volume mount errors all show up here and nowhere else.

events for one pod, newest last
kubectl describe pod my-pod | sed -n '/Events:/,$p'

The usual suspects, in order of frequency

SymptomLikely causeCheck
Exit 137, OOMKilledMemory limit too lowresources.limits.memory
Exit 127Entrypoint binary missingImage contents and command
Exit 1 immediatelyMissing config or env varConfigMap and Secret refs
Restarts after ~30sReadiness or liveness probeProbe path, port and delay
Never startsImage pull failureEvents and registry credentials

Working through that table by hand across a cluster is exactly the kind of repetitive triage worth handing off. BashPilot runs the same sequence, reports the conclusion rather than the raw output, and asks before it changes anything. See AI Kubernetes management for how the approval gate works.

Confirm the fix actually worked

Whatever you changed, the way to know it worked is a clean rollout, not the absence of an error in your terminal. Watch the pod through a full restart cycle rather than checking once and moving on:

watch the rollout to completion
kubectl rollout status deployment/my-app --timeout=120s
kubectl get pods -l app=my-app -w

A pod that reaches Running and then immediately restarts again a few seconds later has not actually been fixed, it has just moved the failure slightly later. Give it at least two full restart cycles before calling it resolved, and check the fresh logs, not the ones from before your change:

confirm the new logs are clean
kubectl logs deployment/my-app --tail=50

Stop it coming back

  • OOM kills: do not just raise the memory limit and move on. Check what actually changed, a traffic spike, a leak, a new dependency, or the limit was never based on a real measurement in the first place.
  • Probe failures: if the app genuinely needs more than the default time to become ready, set initialDelaySeconds to match reality instead of shortening the probe's patience.
  • Missing config on deploy: a ConfigMap or Secret that has not been created yet is a sequencing problem, not a one-off. Add it to whatever applies your manifests, so it can never again be applied out of order.
  • Image pull failures: confirm the registry credentials are attached to the correct namespace, not just present somewhere in the cluster.
In BashPilot

See how BashPilot handles this

app.bashpilot.com
can you fix the CrashLoopBackOff on haproxy
haproxy deployment fixed

The haproxy pod had no config file. The image expects one at /usr/local/etc/haproxy/haproxy.cfg and exits immediately without it.

  • Created a ConfigMap with a working frontend and backend, ports 80 and 443 routed to the app backend.
  • Updated the deployment to mount it at /usr/local/etc/haproxy/haproxy.cfg.
  • New pod is Running, 1/1 ready, logs show Loading success.
bash
kubectl rollout status deployment/haproxy
Example output, not a screenshot of a specific customer's server.

Try this on one of your own servers.

Start free trial
Share LinkedIn X
Questions

Frequently asked questions

What does CrashLoopBackOff actually mean?

It means a container in the pod started, exited, and Kubernetes has restarted it repeatedly with an increasing delay between attempts. The backoff grows from 10 seconds up to 5 minutes. It is a status describing the restart pattern, not a diagnosis, so the real cause is always found in the exit code, the previous container's logs, or the pod events.

Why do kubectl logs show nothing useful?

Because by default it reads the container that is currently running, which has just started and has not failed yet. Add the --previous flag to read logs from the instance that actually crashed. If that is still empty, the container probably never reached your application code, so check the pod events for image pull or mount failures instead.

How do I tell an OOM kill from an application crash?

Check the termination reason. An out of memory kill reports exit code 137 with reason OOMKilled, because the kernel sent SIGKILL when the container exceeded its memory limit. An application crash typically exits 1 or 2 with reason Error, and will usually have written something to its logs before dying.

How do I know CrashLoopBackOff is actually fixed and not just delayed?

Do not stop at the first clean status you see. Watch the pod through at least two full restart cycles with kubectl rollout status or kubectl get pods -w, and check the logs from after your change, not the ones still open in your terminal from before it. A pod that reaches Running and crashes again a few seconds later has had its failure moved later, not removed. Confirming the fix is a separate step from applying it, and skipping it is how the same incident reopens an hour later.

Put your servers on autopilot.

Connect a server in about a minute. The first week is on us.