Why Your Kubernetes Pod Is Crashing And How to Fix It
There’s a specific kind of frustration that comes with Kubernetes. You write your Dockerfile, build the image, apply your manifest, and…
Why Your Kubernetes Pod Is Crashing And How to Fix It

There’s a specific kind of frustration that comes with Kubernetes. You write your Dockerfile, build the image, apply your manifest, and then watch as your pod cycles through Running, Error, CrashLoopBackOffover and over, with logs that sometimes tell you nothing useful at all.
I’ve been there more times than I’d like to admit. And after years of debugging production clusters, I can tell you that most pod crashes fall into a surprisingly small set of categories. Once you know what to look for, diagnosis goes from a 45-minute mystery to a 5-minute checklist.
This is that checklist.
First, Let’s Talk About CrashLoopBackOff
Before diving into causes, it helps to understand what Kubernetes is actually doing when a pod enters CrashLoopBackOff.
Your container started, ran for a moment, and exited with a non-zero code. Kubernetes tried to restart it. It crashed again. Kubernetes tried again, but this time waited a bit longer before restarting — that exponential backoff is what gives the state its name. The pod isn’t stuck. It’s being politely throttled while Kubernetes waits for something to change.
The key insight: Kubernetes didn’t break anything. Your container exited. The question is always why.
1. Your Application Is Crashing on Startup
This is the most common cause, and it’s almost never Kubernetes’ fault.
Your application is likely:
- Failing to connect to a database that isn’t ready yet
- Missing an environment variable it expects to exist
- Throwing an unhandled exception before it binds to a port
- Running a migration on startup that fails and exits
How to confirm it:
kubectl logs <pod-name> — previous
The — previous flag is important. It shows logs from the last crashed container, not the current one that might be in a brief Running state before crashing again. This is where most engineers lose time — they run kubectl logs and see nothing because they’re looking at the wrong container lifecycle.
If your app exits immediately, the error is almost always in those logs. Read them carefully. A missing DATABASE_URL, a connection refused on port 5432, a Python ModuleNotFoundError — these all show up clearly here.
The fix:Solve the application-level error. Add proper startup error handling. Use init containers or readiness gates to delay startup until dependencies are available.
2. OOMKilled — You’re Running Out of Memory
OOMKilled is a different beast from CrashLoopBackOff, and it deserves its own section.
When your pod’s memory usage exceeds the limit defined in its resource spec, the Linux kernel’s OOM killer steps in and terminates the process. Kubernetes reports this as exit code 137.
kubectl describe pod <pod-name>
Look for:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
This happens for a few reasons:
- Your memory limit is set too low for the actual workload
- Your application has a memory leak
- A one-time spike (like loading a large file into memory) exceeds the limit
- JVM-based applications configured without container-aware heap settings
The fix:
First, check what the application actually needs. Use kubectl top pod if metrics-server is installed, or hook into Prometheus if you have it. Then either raise the limit or fix the underlying memory issue.
For JVM apps specifically — if you’re running Java or Scala — make sure you’re setting -XX:MaxRAMPercentage instead of -Xmx. The old -Xmx flag doesn’t know about cgroup limits, so the JVM may allocate heap beyond what Kubernetes allows.
3. Your Liveness Probe Is Too Aggressive
This one is subtle and catches a lot of engineers off guard.
You configured a liveness probe to hit /health every 10 seconds. Your app takes 20 seconds to initialize. Kubernetes starts checking before the app is ready, gets timeouts, decides the container is unhealthy, and kills it. Then restarts it. Then kills it again.
Your application is fine. Kubernetes is the one pulling the trigger — because you told it to.
How to confirm it:
kubectl describe pod <pod-name>
Look for events like:
Liveness probe failed: HTTP probe failed with statuscode: 000
Killing container with id …: pod “…” container “…” is unhealthy
The fix:
Set initialDelaySeconds to something generous — at least 30–60 seconds for apps with non-trivial startup time. Also tune failureThreshold and periodSeconds to avoid killing containers over transient slowness.
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 45
periodSeconds: 15
failureThreshold: 3
Separate your liveness and readiness probes if you haven’t already. Readiness controls traffic routing; liveness controls restarts. They’re not the same thing.
— -
4. The Image Can’t Be Pulled
Sometimes the pod never even starts. It just sits in ImagePullBackOff or ErrImagePull.
Common causes:
- The image tag doesn’t exist in the registry (
latestwas overwritten, or you have a typo in the tag) - The registry requires authentication and there’s no
imagePullSecretconfigured - The node doesn’t have network access to the registry (common in air-gapped or private cluster setups)
- You’re pulling from Docker Hub and hitting rate limits
How to confirm it:
kubectl describe pod <pod-name>
The events section will tell you exactly which registry it tried to reach and what failed.
The fix:
For private registries, create a secret and reference it:
kubectl create secret docker-registry regcred \
— docker-server=<your-registry> \
— docker-username=<username> \
— docker-password=<password>
Then in your pod spec:
imagePullSecrets:
— name: regcred
For Docker Hub rate limits, authenticate your pulls or use a mirror like AWS ECR Public or GitHub Container Registry.
5. Resource Requests Are Causing Scheduling or Throttling Issues
Here’s one that often looks like a crash but is actually a scheduling problem.
If your pod requests more CPU or memory than any node can provide, it will sit in Pending forever — never scheduled, never crashing. But if requests are set too low and limits too high, the pod might get scheduled, run fine for a while, and then get CPU-throttled under load, causing timeouts that look like crashes.
The difference between requests and limits matters:
requests: what Kubernetes uses for scheduling decisionslimits: the hard ceiling the container can’t exceed
Setting limits much higher than requests creates a pod that looks cheap to schedule but is actually expensive at runtime — a classic cause of noisy-neighbor problems on shared clusters.
How to confirm throttling:
If you have Prometheus, look for container_cpu_cfs_throttled_periods_total. High throttling means your limit is too tight for the workload, even if the pod isn’t crashing.
6. ConfigMap or Secret Isn’t Mounted Correctly
Your app is looking for a config file or environment variable, and it simply isn’t there.
This often happens after a rename, a typo in the manifest, or a missing kubectl apply of the ConfigMap before deploying the pod.
How to confirm it:
kubectl exec -it <pod-name> — env | grep MY_VAR
kubectl exec -it <pod-name> — ls /etc/config/
Or check the describe output for mount errors:
MountVolume.SetUp failed for volume “config”: configmap “app-config” not found
The fix:
Make sure the ConfigMap or Secret exists in the same namespace as the pod:
kubectl get configmap -n <namespace>
kubectl get secret -n <namespace>
Also verify the key names match exactly. Kubernetes is case-sensitive and will silently not mount a key if the name doesn’t match.
7. Permissions and Security Context
In hardened clusters — or when working with certain storage backends — containers might fail because they’re trying to write to paths they don’t own, or because security policies block certain syscalls.
Symptoms include Permission denied errors in logs, or pods that start but immediately fail when trying to write to a volume.
Common fixes
Set runAsUser and fsGroup in your security context to match what the application expects:
securityContext:
runAsUser: 1000
fsGroup: 2000
If you’re using a PVC and the data directory has root ownership, the container running as a non-root user won’t be able to write to it. Setting fsGroup tells Kubernetes to chown the mounted volume to that group on startup.
The Debugging Workflow, Summarized
When a pod is crashing, this is the order I run through:
# Step 1: What state is it in?
kubectl get pod <pod-name>
# Step 2: What happened?
kubectl describe pod <pod-name>
# Step 3: What did the app say before it died?
kubectl logs <pod-name> — previous
# Step 4: If it’s running, get inside
kubectl exec -it <pod-name> — /bin/sh
# Step 5: Check events at the namespace level
kubectl get events — sort-by=’.lastTimestamp’
Most crashes reveal themselves by step 3. Describe tells you about probe failures, image pull errors, and scheduling issues. Logs tell you about application-level panics, missing config, and connection failures.
One Last Thing
Kubernetes is a runtime, not a debugger. It will faithfully restart a broken container all day long without ever explaining why. The information you need is almost always there — in the logs, in the events, in the exit code — but you have to know where to look.
The engineers I’ve seen struggle the most with pod crashes are the ones who reach for the Kubernetes documentation first. Most of the time, the real answer is in the application logs, and it has nothing to do with Kubernetes at all.
Start there.
Before you go
- Please take a moment to like the post and follow the writer!
- Did you know that over 400,000 developers share what they’re building, learning, and discovering across our platforms every month? Learn how you can contribute here
메타데이터
- post_id
- 5c71f8d2779a
- slug
- why-your-kubernetes-pod-is-crashing-and-how-to-fix-it-5c71f8d2779a
- url
- https://aws.plainenglish.io/why-your-kubernetes-pod-is-crashing-and-how-to-fix-it-5c71f8d2779a
- canonical_url
- https://aws.plainenglish.io/why-your-kubernetes-pod-is-crashing-and-how-to-fix-it-5c71f8d2779a
- author_url
- https://medium.com/@lucas.siqueira.chagas
- status
- ok
- fetched_at
- 2026-07-14 12:43:20