
Kubernetes CrashLoopBackOff is the status a pod shows when its container keeps starting, crashing, and being restarted, with Kubernetes waiting a little longer before each new attempt. It is not an error inside Kubernetes. It is Kubernetes reporting that your app keeps exiting.
It usually appears right after a deploy you were confident about, often for an app that ran perfectly on your laptop. The good news is that it is one of the most fixable problems you will meet. This guide explains what the status means, why the restarts slow down, the four causes behind most cases, and a calm three-command method that finds the real error. It finishes with how to prevent crash loops and the mistakes that keep them going.
- CrashLoopBackOff is a symptom: the container keeps exiting and Kubernetes keeps restarting it.
- Waits between restarts double from about 10 seconds up to a 5-minute ceiling.
- Most cases come from a wrong start command, missing settings, bad config, or exceeding the memory limit.
- Diagnose in order: kubectl get pods, kubectl describe pod, then kubectl logs --previous.
- Fix one thing, reapply the manifest, and watch until the pod is Running and the restart count stops climbing.
- 0:00Intro
- 0:31It worked locally. Now it won't start
- 1:29Kubernetes runs containers inside pods
- 2:31Start, crash, wait, repeat
- 3:26Why the waits get longer
- 4:24Four usual suspects
- 5:22Three commands find the cause
- 6:18Read events and exit codes
- 7:19Logs from the previous container
- 8:14Fix, reapply, then watch
- 9:08One crashing API, fixed
- 10:06Why local isn't the cluster
- 10:54Stop crash loops before they start
- 11:50Mistakes that keep the loop going
- 12:43Everything you need, in brief
- 13:20Read events and logs, not the status
What does CrashLoopBackOff mean in Kubernetes?
In Kubernetes you don't start containers by hand. You write a manifest describing what should run, and Kubernetes keeps working to make reality match it. The control plane reads the manifest and picks a worker node. On that node, an agent called the kubelet starts a pod, a small wrapper around your container, and keeps watching it.
When a container stops with an error, Kubernetes restarts it automatically by default. That habit is useful for temporary glitches, like a brief network blip. It cannot fix broken code or broken settings, though. If the same failure happens on every start, the container starts, begins initialising, hits the problem, exits, and is restarted, over and over.
The name describes exactly that. "Crash loop" means the container keeps crashing in a loop. "Back off" means Kubernetes waits a little longer between each attempt. The important point is that the status tells you something is failing, not what is failing. To learn the cause, you have to look at events and logs.
- Status: tells you a container keeps exiting
- Events: tell you what Kubernetes noticed
- Logs: tell you what your app said before it died
Why do the waits between restarts get longer?
Kubernetes doesn't restart a crashing container instantly forever. By default it starts with a short wait and doubles it after each failure, up to a maximum of about five minutes. This stops a broken app from hammering the machine with endless instant restarts, while still never giving up.
Follow one broken pod: after the first crash it waits 10 seconds, then 20, then 40, and keeps doubling until it hits the cap. From then on it retries roughly every five minutes. This catches people out. During a long wait nothing seems to happen, so the pod can look settled. It isn't. It is simply waiting for its next attempt to fail.
Once you fix the cause and the container runs steadily for a while, the back-off timer resets. So slow restarts are a clue, not a fix.
What causes CrashLoopBackOff? The four usual suspects
Containers that crash on startup almost always fail for one of four reasons. None are mysterious once you know where to look, and keeping them in mind tells you what to search for in the events and logs.
A wrong start command fails instantly, every time: the manifest overrides the command with a typo, or points to a script that isn't in the image. Missing settings are just as common. On your laptop, a value like a database address lives in a local file. In the cluster nobody passed it in, so the app refuses to start.
Bad config covers a typo in a config file, a file mounted at the wrong path, or a setting that points to a service that isn't there. The app reads it, chokes, and exits. Finally, if you set a memory limit and the app goes past it, the container is killed straight away, often with the reason listed as OOMKilled, meaning out of memory.
- Wrong start command or entrypoint
- Missing environment variable or secret
- Malformed or wrongly mounted config
- Memory limit exceeded (OOMKilled)
How to diagnose CrashLoopBackOff with kubectl
Don't guess. Run three commands in order, moving from the big picture to the details: status, then events, then logs. Each one narrows the problem down. First, kubectl get pods lists your pods with their status and restart counts, so you know which pod is crash looping. In this example, the api pod shows 0/1 containers ready, status CrashLoopBackOff, and six restarts.
Next, kubectl describe pod with the pod's name shows Kubernetes' own record. Think of it like a doctor's visit: the status is the patient saying they feel unwell, and the describe output is the chart. Scroll to the Events section at the bottom. It is a timeline of pulls, starts, kills and back-off messages. Image pull errors show up here too.
Then find Last State, which shows why the previous container ended and its exit code. Exit code 1 points to an error in your app. Exit code 137 often means the container was OOMKilled. Together, these two commands usually tell you what kind of problem you have before you open a single log.
# 1. Which pod is failing?
kubectl get pods
# api-7d9f 0/1 CrashLoopBackOff 6
# 2. What did Kubernetes see?
kubectl describe pod api-7d9f
Why kubectl logs --previous shows the real error
Logs tell you why the container died, in your application's own words. There is a trap, though. Because the pod keeps restarting, the container you are looking at may be brand new, with an empty log. The useful messages belong to the one that just crashed.
Adding the --previous flag asks Kubernetes for the logs of the last container that ended. In this example, the final line reads that DATABASE_URL is not set. That is the real cause, in plain English. Focus on the last few lines: apps usually print an error or stack trace right before they quit, and everything above is context.
If there are no logs at all, that is a clue too. It often means the app never really started, perhaps because of a wrong start command. In that case, go back to the describe events.
# Logs from the container that crashed
kubectl logs api-7d9f --previous
# Error: DATABASE_URL is not set
# Several containers? Name one:
kubectl logs api-7d9f -c api --previous
Fixing CrashLoopBackOff: change one thing, then watch
Fixing the cause is usually the easy part, but the order matters, because it lets you confirm the fix instead of hoping. Change one thing at a time. If you change three things and the pod recovers, you won't know which change mattered. Fix the source first: add the missing variable, correct the command, fix the config, or rebuild and push a corrected image with a new tag.
Then run kubectl apply -f with your updated manifest. Kubernetes compares it with what is running and replaces the broken pods. Finally, run kubectl get pods -w and watch until you see Running, ready, and a restart count that stays put. If it crashes again, start the diagnosis loop over.
Here is how it looks in practice. A team moves a small orders API into Kubernetes and the first deploy crash loops. Get pods shows CrashLoopBackOff. Describe shows exit code 1, so it is an app error, not memory. Logs with --previous say DB_URL is not set. They add the variable, apply, and watch the pod reach Running, 1/1, within minutes. They also put the setting in the manifest, ideally through a ConfigMap or Secret, so every environment gets it.
Why does it work locally but crash in the cluster?
Most crash loops come from hidden assumptions about the environment. Your laptop and the cluster are different worlds, and your app often relied on things only your machine had. In the orders API example, the database address came from a local settings file that was never copied into the image.
On a laptop, settings sit in local files, memory is plentiful, services run on localhost, and the app runs as your user with your paths. In the cluster, the container only gets the settings you pass in, memory is capped, services are reached by name, and permissions are more locked down. Whenever a pod crash loops, ask one question: what did my app get for free locally that the cluster isn't giving it?
- Local settings files → only what you pass in
- Plenty of memory → strict limits
- localhost → services reached by name
How to prevent crash loops before they start
Three habits catch most of these problems early. First, test the actual image, not just your code. Run the container locally, passing settings the same way the cluster will. If it crashes there, it will crash in Kubernetes too. Second, set memory limits based on what your app really uses, so it isn't killed for normal behaviour. The number in this example is only illustrative.
Third, add probes, which are small health checks Kubernetes calls on your app. A readiness probe asks whether the app is ready for traffic, so half-started apps don't receive and fail requests. A liveness probe asks whether the app is still healthy, and restarts it if it freezes. Give it enough time to start, though, or the probe itself can cause a crash loop.
resources:
limits: { memory: 512Mi }
readinessProbe:
httpGet: { path: /ready, port: 8080 }
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
Common mistakes that keep the loop going
Most mistakes treat the status as the problem instead of asking why the container exits. The classic one is deleting the pod and hoping. Kubernetes simply creates a new pod with the same broken setup. Another is reading only the current logs, which may be empty, and missing the real error that --previous would have shown.
A sneakier one is a liveness probe that checks before the app is ready. Kubernetes decides the app is frozen and restarts it, forever. Give slow apps an initial delay or a startup probe. And when you see OOMKilled, don't just double the memory limit. Check real usage first. Sometimes the limit is too low, but sometimes there is a real memory leak that bigger limits would only hide.
Key takeaways
- CrashLoopBackOff is a symptom; events and logs reveal the cause.
- Back-off delays double up to about five minutes, so a quiet pod isn't necessarily fixed.
- Check the four usual suspects: start command, settings, config, memory.
- Diagnose with get pods, describe pod, then logs --previous.
- Fix one thing, reapply, and watch until restarts stop.
- Prevent repeats with local image tests, realistic limits, and well-tuned probes.
Frequently asked questions
Is CrashLoopBackOff a Kubernetes bug?
No. It is Kubernetes reporting that your container keeps starting and exiting. Kubernetes is doing its job by restarting it and slowing down between attempts.
How long does Kubernetes wait between restarts in CrashLoopBackOff?
By default it starts with a short wait, around 10 seconds, and doubles it after each failure. The delay is capped at about five minutes, and it resets once the container runs steadily for a while.
Why are my pod logs empty during CrashLoopBackOff?
You may be looking at a brand-new container that just restarted. Use kubectl logs --previous to see what the crashed container printed. If that is empty too, the app may never have started, so check the describe events.
What do exit codes 1 and 137 mean for a crashing pod?
Exit code 1 usually points to an error inside your application. Exit code 137 often means the container was OOMKilled for exceeding its memory limit.
Will deleting the pod fix CrashLoopBackOff?
Usually not. Kubernetes recreates the pod from the same manifest, so the same problem returns. Find and fix the cause, then reapply.
Can a liveness probe cause CrashLoopBackOff?
Yes. If it starts checking before a slow app is ready, Kubernetes treats the app as frozen and keeps restarting it. An initial delay or a startup probe prevents this.