Lesson 6 of 6

Common Gotchas

Kubernetes fails in recognizable patterns with recognizable names. Learn the five below and kubectl get pods output stops being cryptic — each status points at a specific cause and a specific next command.

💡 The Bottom Line

The debugging path is always the same: kubectl get pods for the status, kubectl describe pod for the Events, kubectl logs (with --previous for crashed attempts) for the app's own words. The status tells you which of the three will hold the answer.

1. CrashLoopBackOff

The container starts, crashes, gets restarted, crashes again — and Kubernetes backs off, waiting longer between attempts. The problem is inside your app or its config: bad env var, unreachable database, missing file, immediate exception on boot. kubectl logs <pod> --previous shows the dying attempt's output — the answer is almost always right there.

2. ImagePullBackOff / ErrImagePull

The node can't download your image. Three causes cover nearly every case: a typo in the image name or tag, the tag doesn't exist in the registry (did CI actually push it?), or the registry is private and the pod lacks an imagePullSecrets credential. kubectl describe pod's Events section quotes the exact registry error.

3. Pending forever

The pod is accepted but no node can host it. describe tells you why the scheduler is stuck — most commonly "Insufficient cpu/memory" (the pod's requests ask for more than any node has free), or a volume/affinity constraint that can't be satisfied. Locally: your minikube VM is just too small.

4. No resource limits — the quiet one

Pods without resources.requests and limits run fine — until one leaks memory and starves everything else on the node, or the scheduler packs a node past its real capacity because nothing declared what it needed. Every production pod template should declare both:

          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              memory: 256Mi

A pod exceeding its memory limit is killed with status OOMKilled — worth recognizing on sight: it means "raise the limit or fix the leak," not "Kubernetes is flaky."

5. Service selector mismatch

From Lesson 4, but it earns its spot in the gotcha list: the Service exists, DNS resolves, and every request times out — because the selector matches zero pods. kubectl get endpoints <service>: empty means label mismatch. One typo, total outage, no error message anywhere.

🙋 There Are No Dumb Questions

Q: My pod is Running with 0 restarts, but the app still doesn't work. Now what?
A: "Running" only means the process is alive. Work the chain: kubectl logs for app errors, kubectl exec -it <pod> -- sh to poke around inside (can it reach its database?), kubectl get endpoints to check the Service wiring, kubectl port-forward to test the pod directly and bypass the Service. Each step isolates one layer — pod, dependencies, Service — until the broken one is obvious.

⚠️ Watch Out

Resist the reflex to fix things by deleting pods until the error disappears. A deleted pod's replacement often hits the same problem — you've destroyed the evidence and gained nothing. describe and logs --previous exist precisely so you can diagnose before anything is deleted. Delete-as-debugging is the Kubernetes equivalent of turning it off and on again — sometimes it works, but you learn nothing.

🔧 Try It Yourself

Cause two of the gotchas on purpose, so you recognize them relaxed instead of at 3 a.m.:

kubectl run broken1 --image=nginx:no-such-tag
kubectl run broken2 --image=busybox -- sh -c "exit 1"
kubectl get pods -w

Watch broken1 reach ImagePullBackOff and broken2 reach CrashLoopBackOff, then read kubectl describe pod broken1 and kubectl logs broken2 --previous. Clean up with kubectl delete pod broken1 broken2.

✅ Quick Check

A pod shows CrashLoopBackOff. Where does the problem almost certainly live?


That's the track. The recap gathers the whole model — desired state, pods, Deployments, Services, config — into one page, plus where to go next.

← Back to
Next →