CKA — Troubleshoot Pods

8 minute read

CKA Exam Domain

Domain Weight
Troubleshooting 30%

Pod troubleshooting is the bread and butter of the CKA exam. Almost every troubleshooting question starts with a Pod that isn’t behaving correctly. You need to identify the status, find the root cause, and fix it — fast.

Pod Troubleshooting Flow

Every Pod issue follows the same debugging flow. Memorize this:

kubectl get pods → Check STATUS → kubectl describe pod → kubectl logs → kubectl exec
  1. See the problem — kubectl get pods shows the current status
  2. Get details — kubectl describe pod <pod> shows events and configuration
  3. Read logs — kubectl logs <pod> shows application output
  4. Get inside — kubectl exec -it <pod> -- /bin/sh for interactive debugging

Exam Tip: Start with kubectl get pods -o wide to get the Pod status, node placement, and IP address all at once. This saves time in the exam.

CrashLoopBackOff

What It Means

CrashLoopBackOff means your container starts, crashes, and Kubernetes restarts it with an exponentially increasing backoff delay (10s, 20s, 40s, up to 5 minutes).

NAME         READY   STATUS             RESTARTS      AGE
my-app       0/1     CrashLoopBackOff   5 (2m ago)    8m

Common Causes

  • Wrong command or arguments — The container’s command or args field references a binary or script that doesn’t exist
  • Missing configuration — The app expects environment variables, ConfigMaps, or Secrets that aren’t present
  • Application error — The code itself crashes on startup (null pointer, missing dependency, port conflict)
  • Liveness probe failure — The liveness probe keeps killing the container because it can’t respond in time

Debugging Steps

# Step 1: Check the logs of the current crash
kubectl logs my-app

# Step 2: Check logs from the PREVIOUS crash (crucial for CrashLoopBackOff!)
kubectl logs my-app --previous

# Step 3: Describe the Pod to see events and config
kubectl describe pod my-app

# Step 4: For multi-container Pods, specify the container
kubectl logs my-app -c sidecar-container

Example Fix

# BROKEN — wrong command
spec:
  containers:
  - name: app
    image: nginx
    command: ["/bin/start-app"]  # doesn't exist in nginx image

# FIXED — remove the wrong command, let the image's default CMD run
spec:
  containers:
  - name: app
    image: nginx
    # command removed — uses default nginx entrypoint

ImagePullBackOff & ErrImagePull

What It Means

Kubernetes can’t pull the container image. ErrImagePull is the initial failure; ImagePullBackOff means it’s retrying with backoff.

NAME         READY   STATUS             RESTARTS   AGE
my-app       0/1     ImagePullBackOff   0          3m

Common Causes

  • Wrong image name — Typo in the image name (ngingx instead of nginx)
  • Wrong tag — The specified tag doesn’t exist (nginx:1.99.0)
  • Private registry — The registry requires authentication but no imagePullSecrets is configured
  • Network issues — The node can’t reach the container registry

Debugging Steps

# Always start with describe — the Events section tells you exactly what's wrong
kubectl describe pod my-app

Look at the Events section at the bottom:

Events:
  Type     Reason     Age   From               Message
  ----     ------     ----  ----               -------
  Normal   Scheduled  1m    default-scheduler  Successfully assigned default/my-app to node01
  Normal   Pulling    1m    kubelet            Pulling image "ngingx:latest"
  Warning  Failed     1m    kubelet            Failed to pull image "ngingx:latest": rpc error: ...
  Warning  Failed     1m    kubelet            Error: ErrImagePull
  Normal   BackOff    30s   kubelet            Back-off pulling image "ngingx:latest"
  Warning  Failed     30s   kubelet            Error: ImagePullBackOff

Fix: Wrong Image Name

kubectl edit pod my-app
# Fix the image name from "ngingx" to "nginx"

Or delete and recreate with the correct image.

Fix: Private Registry

# Create the imagePullSecret
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password=pass

# Reference it in the Pod spec
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: registry.example.com/my-app:v1

Pending Pods

What It Means

The Pod has been accepted by the cluster but can’t be scheduled to a node.

NAME         READY   STATUS    RESTARTS   AGE
my-app       0/1     Pending   0          5m

Common Causes

  • Insufficient resources — CPU/memory requests exceed what’s available on any node
  • No matching node — nodeSelector, node affinity, or taints prevent scheduling
  • PVC not bound — The Pod references a PersistentVolumeClaim that isn’t bound

Debugging Steps

kubectl describe pod my-app

Check the Events section for FailedScheduling:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  1m    default-scheduler  0/3 nodes are available:
           1 Insufficient cpu, 2 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }

Fix: Resource Issue

# Check what resources the node has available
kubectl describe node <node-name> | grep -A 5 "Allocated resources"

# Option 1: Reduce the Pod's resource requests
# Option 2: Add more nodes to the cluster

Fix: Taint/Toleration Issue

# Check taints on nodes
kubectl describe node <node-name> | grep Taints

# Add toleration to the Pod, or remove the taint from the node
kubectl taint nodes <node-name> key=value:NoSchedule-

Fix: PVC Issue

# Check the PVC status
kubectl get pvc

# If the PVC is Pending, check if a matching PV exists
kubectl get pv

OOMKilled

What It Means

The container exceeded its memory limit and was killed by the Linux OOM (Out of Memory) killer.

NAME         READY   STATUS      RESTARTS      AGE
my-app       0/1     OOMKilled   3 (1m ago)    5m

Debugging Steps

kubectl describe pod my-app

Look for the Last State section:

    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137

Fix

# Increase the memory limit
spec:
  containers:
  - name: app
    image: my-app:v1
    resources:
      requests:
        memory: "128Mi"
      limits:
        memory: "512Mi"  # increase this if the app genuinely needs more

Exam Tip: Exit code 137 = OOMKilled (128 + signal 9 SIGKILL). Exit code 143 = graceful termination (128 + signal 15 SIGTERM). Know these codes!

Pod Status Reference

Status Meaning
Running Pod is running normally, all containers are up
Pending Pod is accepted but not scheduled (resource/scheduling issue)
Succeeded All containers completed successfully (common for Jobs)
Failed All containers terminated, at least one with failure
CrashLoopBackOff Container keeps crashing, restarts with exponential backoff
ImagePullBackOff Can’t pull the container image, retrying with backoff
ErrImagePull Initial failure to pull the container image
ContainerCreating Image pulled, container being set up (may indicate volume/network issue if stuck)
Terminating Pod is being deleted (may hang if finalizers or preStop hooks are slow)
Init:Error An init container failed
Init:CrashLoopBackOff An init container keeps crashing

Essential Debugging Commands

# Get all Pods with details
kubectl get pods -o wide
kubectl get pods -A -o wide    # all namespaces

# Describe a Pod — events, config, conditions
kubectl describe pod <pod>
kubectl describe pod <pod> -n <namespace>

# Logs — application output
kubectl logs <pod>
kubectl logs <pod> --previous           # previous crashed container
kubectl logs <pod> -c <container>       # specific container
kubectl logs <pod> --tail=50            # last 50 lines
kubectl logs <pod> -f                   # follow/stream

# Execute into a container
kubectl exec -it <pod> -- /bin/sh
kubectl exec -it <pod> -c <container> -- /bin/sh

# Events — cluster-wide debugging
kubectl get events --sort-by=.metadata.creationTimestamp
kubectl get events --field-selector involvedObject.name=<pod>

Practice Questions

Q1. A Pod named web-app in the default namespace is in CrashLoopBackOff. Diagnose and fix it.

Show Answer
# Step 1: Check the Pod status
kubectl get pod web-app

# Step 2: Check the logs (and previous logs)
kubectl logs web-app
kubectl logs web-app --previous

# Step 3: Describe the Pod for events and config
kubectl describe pod web-app

# Common fix: If it's a wrong command, edit the Pod spec
# You may need to delete and recreate since Pod specs are mostly immutable
kubectl get pod web-app -o yaml > web-app.yaml
# Edit web-app.yaml to fix the issue
kubectl delete pod web-app
kubectl apply -f web-app.yaml

The key is reading the logs — they almost always tell you exactly what’s wrong.

Q2. A Pod is stuck in ImagePullBackOff. The image is my-registry.io/app:v2. What do you check?

Show Answer
# Step 1: Describe the Pod to see the exact error
kubectl describe pod <pod-name>

# Look at Events — it will say something like:
# "Failed to pull image: unauthorized" → needs imagePullSecrets
# "Failed to pull image: not found" → wrong image name/tag

# Step 2: If it needs authentication
kubectl create secret docker-registry regcred \
  --docker-server=my-registry.io \
  --docker-username=<user> \
  --docker-password=<pass>

# Step 3: Add imagePullSecrets to the Pod spec or patch the deployment
kubectl patch deployment <deploy> -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}'

Q3. A Pod named data-processor has been Pending for 10 minutes. There are 3 worker nodes in the cluster. Find out why and fix it.

Show Answer
# Step 1: Check why it's pending
kubectl describe pod data-processor

# Look for FailedScheduling event. Common reasons:
# "Insufficient cpu" — requests too high
# "node(s) had untolerated taint" — needs toleration
# "node(s) didn't match Pod's node affinity/selector" — wrong label

# Step 2: Check nodes
kubectl describe nodes | grep -A 5 "Allocated resources"
kubectl get nodes --show-labels

# Step 3: Fix based on root cause
# For resource issue: reduce requests or add nodes
# For taint: add toleration or remove taint
# For nodeSelector: fix label or update selector

Q4. A Deployment’s Pods are showing OOMKilled with exit code 137. The current memory limit is 64Mi. Fix it.

Show Answer
# Step 1: Verify the OOMKilled status
kubectl describe pod <pod-name>
# Look for Last State: Terminated, Reason: OOMKilled

# Step 2: Increase the memory limit in the Deployment
kubectl edit deployment <deployment-name>
# Change spec.containers[].resources.limits.memory to a higher value, e.g., "256Mi"

# Or use kubectl set:
kubectl set resources deployment <deployment-name> --limits=memory=256Mi

# Step 3: Verify the new Pods are running
kubectl get pods -w

Q5. A multi-container Pod named logger has two containers: app and sidecar. The app container is running but the sidecar is in CrashLoopBackOff. Get the logs for the crashing container.

Show Answer
# Step 1: Check which container is failing
kubectl get pod logger
# Output shows READY 1/2 — one of two containers is not ready

# Step 2: Get logs for the specific container
kubectl logs logger -c sidecar
kubectl logs logger -c sidecar --previous

# Step 3: Describe the Pod to see container-specific events
kubectl describe pod logger
# Look at the "sidecar" container section for State and Last State

# Step 4: Fix based on what the logs reveal
# Export, edit, delete, and recreate if needed
kubectl get pod logger -o yaml > logger.yaml
# Fix the sidecar container spec
kubectl delete pod logger
kubectl apply -f logger.yaml

What’s Next

Pod issues are the most common troubleshooting scenario, but sometimes the problem isn’t the Pod — it’s the node. Next, we’ll learn how to diagnose and fix node-level issues.

Next: Troubleshoot Nodes →

Updated: