🔴 CrashLoopBackOff

CrashLoopBackOff means the container started, crashed (non-zero exit), and Kubernetes is retrying with exponential backoff (10s → 20s → 40s → … → 5min cap). The pod is not stuck permanently — it just keeps failing.

Diagnostic workflow

# Step 1: confirm the status and restart count
kubectl get pod my-app-xyz
# NAME         READY   STATUS             RESTARTS   AGE
# my-app-xyz   0/1     CrashLoopBackOff   8          12m

# Step 2: read the PREVIOUS container's logs (current may be empty)
kubectl logs my-app-xyz --previous

# Step 3: check events for extra context
kubectl describe pod my-app-xyz | grep -A 20 Events

# Step 4: check exit code in lastState
kubectl get pod my-app-xyz -o jsonpath='{.status.containerStatuses[0].lastState.terminated}'
# exitCode: 1  → app error
# exitCode: 137 → OOMKilled (128 + SIGKILL)
# exitCode: 139 → Segfault (128 + SIGSEGV)
# exitCode: 143 → SIGTERM not handled gracefully

Common root causes by exit code

Exit CodeSignalLikely causeFix
1App startup error (bad config, missing file, DB unreachable)Read logs, fix config/env vars
2Shell/bash syntax error or misuse of commandFix entrypoint command
137SIGKILLOOMKilled — container exceeded memory limitIncrease memory limit or fix leak
139SIGSEGVSegmentation fault — application bugDebug core dump, update app
143SIGTERMPod evicted or SIGTERM not handled — exit took too long, got SIGKILLImplement graceful shutdown handler
255Generic unhandled errorCheck application logs carefully
💡 If logs are empty on CrashLoopBackOff An empty --previous log means the container crashed before writing anything — bad entrypoint, missing binary, or permission error. Use kubectl debug --copy-to=debug-pod -- sleep infinity to keep the pod alive and inspect its filesystem interactively.

🟡 ImagePullBackOff / ErrImagePull

ImagePullBackOff means the kubelet could not pull the container image. ErrImagePull is the immediate error; ImagePullBackOff is the same error after backoff kicks in.

Diagnostic workflow

# Check the exact error in events
kubectl describe pod my-app-xyz | grep -A 5 "Failed to pull"
# Failed to pull image "ghcr.io/myorg/myapp:v99": rpc error: code = NotFound

# Common error messages and meanings:
# "not found"        → wrong image name or tag doesn't exist
# "unauthorized"     → missing or wrong imagePullSecret
# "connection refused" → registry unreachable (network issue, private registry)
# "manifest unknown" → tag exists but wrong architecture (amd64 vs arm64)

Fix: missing imagePullSecret

# Create the secret from registry credentials
kubectl create secret docker-registry ghcr-secret \
  --docker-server=ghcr.io \
  --docker-username=myuser \
  --docker-password=ghp_token123 \
  --namespace=production

# Reference it in the pod spec
spec:
  imagePullSecrets:
  - name: ghcr-secret
  containers:
  - name: app
    image: ghcr.io/myorg/myapp:v1.2.3

# Or attach to a ServiceAccount so all pods in the namespace get it
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets": [{"name": "ghcr-secret"}]}'

🗑️ Eviction Diagnosis

Pods are evicted when the kubelet detects node pressure — low memory, low disk, or too many processes. Eviction is a normal protective mechanism but becomes a problem when it's continuous or targets critical workloads.

Identifying evictions

# Evicted pods stay in the namespace with STATUS=Evicted
kubectl get pods -A | grep Evicted

# See why a pod was evicted
kubectl describe pod evicted-pod-xyz | grep -A 5 "Status:"
# Status:   Failed
# Reason:   Evicted
# Message:  The node was low on resource: memory.
#           Threshold quantity: 100Mi, available: 47Mi

# Check current node conditions
kubectl describe node worker-1 | grep -A 15 Conditions
# MemoryPressure   True   → node actively evicting
# DiskPressure     True   → disk full, kubelet will evict
# PIDPressure      True   → too many processes

# Clean up completed/evicted pods (they accumulate)
kubectl delete pods -A --field-selector='status.phase=Failed'

Eviction prevention strategies

Set memory requests accurately

BestEffort pods (no requests) are evicted first. Guaranteed QoS pods (requests=limits) are evicted last.

Use PodDisruptionBudgets

PDBs limit voluntary disruptions but don't block kubelet evictions under pressure. They do protect against scheduler evictions during upgrades.

Watch imagefs + nodefs

Unused images accumulate. kubectl debug node + crictl rmi --prune frees disk. Or increase imageGCHighThresholdPercent in kubelet config.

Tune eviction thresholds

Kubelet eviction thresholds: memory.available<100Mi, nodefs.available<10%. Increase node size or adjust in kubelet config.

💥 OOMKilled

OOMKilled (exit code 137) means the Linux kernel's OOM killer terminated the container because it exceeded its memory limit. Unlike eviction (node-level), OOMKill is container-level — enforced by cgroups.

# Identify OOMKilled containers
kubectl get pods -A -o json | jq '.items[] | select(
  .status.containerStatuses[]?.lastState.terminated.reason == "OOMKilled"
) | {name: .metadata.name, ns: .metadata.namespace}'

# Check memory usage vs limit for a running pod
kubectl top pod my-app-xyz --containers

# See the limit that was exceeded
kubectl get pod my-app-xyz -o jsonpath='{.spec.containers[0].resources.limits.memory}'

# Check container memory usage in the describe output
kubectl describe pod my-app-xyz | grep -A 10 "Last State"
# Last State: Terminated
#   Reason:    OOMKilled
#   Exit Code: 137

🧭 Universal Troubleshooting Methodology

Every Kubernetes problem follows the same diagnostic ladder. Work top-down, stop when you find the cause:

  1. kubectl get — confirm the resource exists and note its phase/status/age/restarts
  2. kubectl describe — read the Events section at the bottom; this is the most useful signal
  3. kubectl logs [--previous] — read application output; always check previous on restarts
  4. kubectl get endpoints / pvc / configmap — verify dependencies exist and are populated
  5. kubectl exec — test connectivity — nslookup, curl, nc from inside the affected pod
  6. kubectl debug / node — ephemeral container or node-level inspection for deep issues
  7. Metrics + logs — check Prometheus for resource saturation, Loki for correlated log spikes
💡 The Events section is your best friend kubectl describe pod always ends with an Events section. Kubernetes components (scheduler, kubelet, image puller) write human-readable messages here. In 80% of cases, the Events section contains the exact error message you need. Always read it before diving deeper.

Quick-reference: status → most likely cause

StatusREADYMost likely causeFirst command
Pending0/1Scheduler can't place podkubectl describe pod → Events
ImagePullBackOff0/1Can't pull imagekubectl describe pod → Failed to pull
CrashLoopBackOff0/1Container crashes on startkubectl logs --previous
Running0/1Readiness probe failingkubectl describe pod → Unhealthy events
Running1/1App error (check with logs)kubectl logs -f
OOMKilled0/1Memory limit exceededkubectl describe pod → lastState + kubectl top
Evicted0/1Node pressure (disk/mem)kubectl describe node → Conditions

🔵 Pending Pods

Pending means the scheduler could not find a suitable node. The pod exists in etcd but no node has been assigned. Always check the Events section of kubectl describe pod first — the scheduler emits a clear reason.

Common Pending causes and fixes

Scheduler event messageRoot causeFix
Insufficient cpu / Insufficient memory No node has enough allocatable CPU/memory to satisfy requests Scale cluster, reduce requests, or check for resource hogging pods
0/3 nodes are available: 3 node(s) had untolerated taint All nodes have taints the pod doesn't tolerate Add toleration to pod spec or remove taint from a node
didn't match Pod's node affinity/selector nodeSelector or nodeAffinity rules exclude all available nodes Fix affinity rules or label the target nodes correctly
pod has unbound immediate PersistentVolumeClaims PVC is Pending — no PV matched or StorageClass provisioner failed Check kubectl describe pvc — wrong StorageClass? Provisioner running?
Unschedulable: exceeds max pod count Node has hit its maxPods limit (default 110) Add more nodes or increase maxPods in kubelet config
PodTopologySpread constraints not satisfiable TopologySpreadConstraint with whenUnsatisfiable: DoNotSchedule Add nodes in missing zones or relax maxSkew / change to ScheduleAnyway
# Full diagnostic sequence for a Pending pod

# 1. Read the scheduler reason
kubectl describe pod my-app-xyz | grep -A 10 Events

# 2. Check allocatable resources on all nodes
kubectl describe nodes | grep -A 5 "Allocated resources"

# 3. Check if the PVC is bound (if storage-related)
kubectl get pvc -n production
kubectl describe pvc my-pvc -n production

# 4. Test scheduling manually with a dry-run
kubectl run test-sched --image=nginx --dry-run=server -o yaml \
  --overrides='{"spec":{"nodeSelector":{"disktype":"ssd"}}}' 2>&1

🌐 DNS Failures

DNS failures are subtle — they appear as connection timeouts or "host not found" errors in application logs, often misdiagnosed as a service being down when the service is perfectly healthy.

DNS resolution hierarchy in Kubernetes

# A pod's /etc/resolv.conf looks like:
nameserver 10.96.0.10      # CoreDNS ClusterIP
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

# Resolution order for "payments-svc":
# 1. payments-svc.default.svc.cluster.local   ← found! stops here
# 2. payments-svc.svc.cluster.local
# 3. payments-svc.cluster.local
# 4. payments-svc. (external DNS)

Diagnostic workflow

# Step 1: test DNS from inside a pod
kubectl exec -it my-app-xyz -- nslookup kubernetes.default.svc.cluster.local
# If this fails → CoreDNS is the problem
# If this works but service DNS fails → check the service/endpoints

# Step 2: check CoreDNS is healthy
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

# Step 3: verify the Service and its Endpoints exist
kubectl get svc payments-svc -n production
kubectl get endpoints payments-svc -n production
# Empty Endpoints → no pods matching the Service selector are Ready

# Step 4: check ndots — external hostnames with few dots get search suffixes appended
# "api.stripe.com" has 2 dots < ndots:5, so 5 failed local lookups happen first!
# Fix: use trailing dot "api.stripe.com." OR set dnsConfig.options.ndots: 2
spec:
  dnsConfig:
    options:
    - name: ndots
      value: "2"   # reduces unnecessary local DNS queries
⚠️ Empty Endpoints is the #1 DNS confusion The Service exists and resolves fine via DNS, but connections time out. Run kubectl get endpoints <svc>. If it shows <none>, no pods match the selector OR no pods have passed their readiness probe. Fix the selector mismatch or the readiness issue — DNS itself is not the problem.

🧠 Knowledge Check

Q1. A pod shows exit code 137. What happened and what should you investigate first?

A) The application exited normally with code 137
B) The container was evicted due to node DiskPressure
C) OOMKilled — container exceeded its memory limit; check kubectl top pod and memory limit in spec
D) A liveness probe failed and Kubernetes sent SIGKILL

Q2. kubectl get endpoints my-svc shows <none>. The Service exists and DNS resolves it. Why do connections still time out?

A) CoreDNS is returning a stale cached IP address
B) The Service ClusterIP has not been assigned yet
C) No pods match the Service selector or all matching pods are failing readiness — no backends for kube-proxy to route to
D) A NetworkPolicy is blocking traffic to the Service port

Q3. A pod is Pending and kubectl describe pod shows: "0/3 nodes are available: 3 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: NoSchedule}". What is wrong?

A) The pod has too high resource requests for any control-plane node
B) All nodes are control-plane nodes with NoSchedule taint; the pod needs a matching toleration or worker nodes must be added
C) The pod's nodeSelector doesn't match any node labels
D) The pod is requesting a GPU that no node has

Q4. A pod's DNS lookup for api.stripe.com takes 5+ seconds before resolving. The /etc/resolv.conf shows options ndots:5. Why is it slow?

A) CoreDNS is overloaded and throttling external queries
B) The Stripe API server is slow to respond to DNS queries
C) A NetworkPolicy is blocking UDP port 53 to CoreDNS
D) ndots:5 causes 3 failed local search-domain lookups before trying the external name — set ndots:2 or use a trailing dot