A pod is restarting for the fifth time. You type kubectl logs my-pod and get one line — or nothing at all. You redeploy. It restarts again. Somewhere between the third and fourth restart the panic sets in, because the cluster is telling you something and you can't hear it. This article is about learning to hear it: a concrete triage order, the exit codes that tell you where to look, and the handful of causes behind most CrashLoopBackOff events — each with the exact kubectl command and the one-line manifest fix.
Why kubectl logs shows nothing (and what to run instead)
Here is the trap almost everyone falls into. A container that crashes on startup writes its error and dies. The new container starts with an empty log buffer. kubectl logs shows you the current container, which may have written nothing yet. The error you need is in the previous one.
kubectl logs my-pod --previous
That single flag resolves a large share of "the logs are empty" cases. If it still shows nothing, the container probably never got as far as starting a process — which means the problem is not inside your app at all.
So the triage order is:
kubectl describe pod my-pod— read Events at the bottom and the container State/Last State at the top.kubectl logs my-pod --previous— the crash output from the dead container.- Check the exit code in Last State. This narrows the field faster than any log.
The nine causes you will actually meet
Once you know the exit code, the causes collapse quickly. Here is the map — each row is a real pattern, not a hypothetical. More on the topic: “CI/CD as an Art”.
Symptom in describe |
Likely cause | Fix |
|---|---|---|
CreateContainerConfigError |
Missing ConfigMap or Secret key | Create the key or fix the configMapKeyRef / secretKeyRef name |
| Exit code 1, instant crash | Wrong command or entrypoint |
Run kubectl exec into a working image and check the binary path |
| Exit 137, no OOMKilled message | Liveness probe killing a slow-starting app | Add initialDelaySeconds / raise failureThreshold |
OOMKilled: true |
Container hit its memory limit | Raise resources.limits.memory or fix the leak |
Read-only file system |
readOnlyRootFilesystem: true with no writable volume |
Mount an emptyDir where the app writes |
Pod stuck in Init:0/1 |
Init container waiting on a dependency | Check init container logs — kubectl logs my-pod -c init-name |
ImagePullBackOff |
Private registry, missing imagePullSecrets |
Create the secret and reference it in the pod spec |
| Crash with a type error | Bad environment variable (e.g. PORT=abc) |
Fix the value in the ConfigMap or env block |
| Crash after startup | Database or service not ready yet | Add a readiness probe, or a retry loop in the app |
A few of these deserve a closer look, because the fix is less obvious than the row suggests.
The liveness probe that kills a healthy app
A liveness probe answers one question: is this container alive? If it fails failureThreshold times, kubelet kills the container and restarts it. That is correct behaviour for a hung process — and disastrous for an app that simply takes 40 seconds to warm up while the probe starts checking at 10. The container gets killed, restarts, warms up again, gets killed. You see a restart loop and healthy logs. The fix is initialDelaySeconds (or a startupProbe), not more memory.
The OOMKill from a missing limit
A container with no memory limit can consume the node until the kernel's OOM killer steps in — and it does not always pick your container. Setting resources.requests.memory and resources.limits.memory makes the failure local and readable: the pod is killed, describe shows OOMKilled, and you know exactly which service to look at. Without limits, the symptom is a node-wide incident with no clear owner.
The dependency that isn't ready
This one fools people because the app's own logs look clean. The container starts, tries to connect to a database or an upstream service, fails, and exits. kubectl describe shows exit code 1 and nothing else. The fix is not in the Deployment — it is in how the app handles a slow dependency: a retry loop, or a readiness probe so traffic waits until the connection is up. Related breakdown: Microservices Architecture in Practice covers how this plays out across services. More on the topic: “Docker and Kubernetes”.
Debugging by hand vs. building a cluster that doesn't crash
There are two ways to learn this material, and they produce different skills.
Reading about it vs. breaking it. You can memorise the exit-code table in twenty minutes. You will still freeze the first time a real pod loops, because the skill is not the table — it is the habit of running describe before logs, and reading Events before guessing. That habit comes from repetition on a cluster you can destroy. A local kind or minikube cluster costs nothing but time; the trade-off is that nobody tells you whether your diagnosis was right.
Self-study vs. structured practice. Self-study is cheap and flexible, and for an experienced engineer it is enough. The cost appears in the gaps: you learn the causes you happen to hit, and never the ones your workload doesn't produce. A structured path with labs and timed troubleshooting sessions exposes you to the nine causes above in a controlled setting — including the ones your own projects would never surface. If you want a sense of how much of this work is really about reading failure output, DevOps and Cloud Infrastructure is a good companion read. More on the topic: “From CrashLoopBackOff to a Working Helm Chart”.
Individual debugging vs. the exam format. In daily work you have an hour and a teammate to ask. Under CKA and CKAD conditions you have minutes and a terminal. That difference is worth practising explicitly: a 24-task, two-hour mock exam is not about knowledge you lack, it is about the speed of your triage order. More on the topic: “From Dockerfile to Debugging Pods”.
Who this is for
If you already run workloads in Kubernetes and want the failure modes to stop being mysterious, this material pays off immediately — the triage order alone will save you hours in the first month. If you are preparing for CKA or CKAD, it is the part of the syllabus that actually decides your result, because troubleshooting tasks reward a fixed sequence over improvisation. More on the topic: “Autoscaling in Kubernetes”.
It is less useful if you have never deployed a container. CrashLoopBackOff is a second-week problem; you need the first week — pods, Deployments, Services — before the exit codes mean anything. And no amount of reading substitutes for a cluster where you can run kubectl delete pod and watch what happens next.
The next time a pod loops, don't redeploy. Run kubectl describe, read the exit code, then --previous. Nine times out of ten, the answer is already on your screen. The course walks through this triage order in hands-on labs on a real cluster, with troubleshooting sessions and timed mock exams that mirror the CKA and CKAD format — the details are on the CKA + CKAD — Kubernetes Administrator & Developer page. More on the topic: “DevOps & Cloud”.