Kubernetes Troubleshooting with kubectl: A Practical Debugging Guide

August 18, 2026 · 8 min read

Your pod is in CrashLoopBackOff. Or ImagePullBackOff. Or it's stuck in Pending for an hour. The deployment was working yesterday — nothing changed. Sound familiar?

Kubernetes failures all look the same at first glance: a red status, a cryptic message, and a ticket in your queue. But almost every problem falls into a small set of categories, and each category has a fixed set of diagnostic commands. Learn those, and you stop guessing and start fixing.

Here's the troubleshooting workflow I use in production, ordered from cheapest to most expensive. Run these in order and you'll find the root cause in minutes, not hours.

1. The First Three Commands: Always

Before touching anything, get the lay of the land:

# Everything in the namespace, with status
kubectl get all -n myapp

# Pods with restarts and age
kubectl get pods -n myapp -o wide

# The full story on a single pod
kubectl describe pod my-api-7d4f8b9c6c-xk9j2 -n myapp

kubectl describe is the single most valuable command in Kubernetes debugging. It shows Events at the bottom — the actual reason things went wrong, written by the kubelet and the controller manager. "Failed to pull image", "Back-off restarting failed container", "0/3 nodes are available: 1 Insufficient memory". The event section is the answer key. Read it first.

Rule of thumb: if you can't tell what's wrong after running these three commands, you haven't read the Events section carefully enough.

2. CrashLoopBackOff: The Container Won't Stay Up

The pod starts, crashes, restarts, crashes. The most common cause is an application error, not a Kubernetes problem. Three things to check, in order:

# Current logs
kubectl logs my-api-7d4f8b9c6c-xk9j2 -n myapp

# Logs from the previous, crashed instance
kubectl logs my-api-7d4f8b9c6c-xk9j2 -n myapp --previous

# If there are multiple containers in the pod
kubectl logs my-api-7d4f8b9c6c-xk9j2 -n myapp -c sidecar

--previous is the move. When a container crashes and restarts, the current logs are empty or show only the new attempt. The crash output — the stack trace, the panic, the FATAL line — is in the previous instance's logs.

Common crash causes I see weekly:

3. ImagePullBackOff: The Image Isn't There

Kubernetes can't pull your container image. The reasons are usually boring:

# See the exact pull error
kubectl describe pod my-api-7d4f8b9c6c-xk9j2 -n myapp | grep -A 5 Events

Then check the obvious suspects:

4. Pending: The Pod Can't Be Scheduled

A pod stuck in Pending means the scheduler hasn't found a node for it. Again, describe tells you why:

kubectl describe pod my-api-7d4f8b9c6c-xk9j2 -n myapp
# Look for lines like:
#   0/3 nodes are available: 1 node(s) had untolerated taint,
#   2 Insufficient cpu. preemption: 0/3 nodes available.

The classic causes:

For the "which node can run this" question, the scheduler's own tool is the fastest answer:

kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu,MEM:.status.allocatable.memory

5. Running But Broken: Connection and Config Issues

The pod is Running, but the app is failing. This is where you move from the pod layer to the app layer:

# Get an interactive shell inside the container
kubectl exec -it my-api-7d4f8b9c6c-xk9j2 -n myapp -- /bin/sh

# Port-forward to reach a service from your laptop
kubectl port-forward svc/my-api 8080:80 -n myapp

# Copy a file out for inspection
kubectl cp myapp/my-api-7d4f8b9c6c-xk9j2:/var/log/app.log ./app.log

# Stream all logs from a deployment
kubectl logs deploy/my-api -n myapp --tail=200 -f

When the app is up but can't reach its dependencies, work the connectivity chain:

  1. Is DNS resolving? Inside the pod: nslookup my-db.default.svc.cluster.local or getent hosts my-db. If this fails, check CoreDNS: kubectl get pods -n kube-system -l k8s-app=kube-dns and kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50.
  2. Is the Service pointing at the right pods? kubectl get endpoints my-api — empty endpoints mean the selector doesn't match your pod labels. This is the #1 cause of "the service exists but nothing responds".
  3. Is the NetworkPolicy blocking it? Check for NetworkPolicies in the namespace. A policy that allows only certain ingress can silently kill cross-namespace calls.

6. A Debugging Checklist for Incidents

When something breaks and you're under pressure, run this sequence top to bottom:

kubectl get events --sort-by=.lastTimestamp | tail -30
kubectl get pods -A | grep -v Running
kubectl describe pod <pod> | sed -n '/Events:/,$p'
kubectl logs <pod> --previous
kubectl get endpoints <service>
kubectl get nodes

That's it. Six commands cover 90% of Kubernetes incidents: events, non-running pods, the pod's story, crash logs, service endpoints, and node health. Everything else is application-specific investigation.

7. Quick Wins to Prevent Future Debugging

A few habits that turn these hour-long firefights into five-minute confirmations:

Summary

Kubernetes debugging is pattern matching. CrashLoopBackOff → logs. ImagePullBackOff → describe + registry. Pending → describe + node capacity. "Running but broken" → exec, port-forward, endpoints, DNS. Start with kubectl describe, read the Events, and work outward from the pod.

The next time a pod misbehaves, resist the urge to delete and recreate it. Recreating hides the evidence. Diagnose first — the Events section has already told you what's wrong, you just haven't read it yet.