A troubleshooting method you can run under pressure

A fixed loop of commands that finds the cause of almost any broken workload in minutes, a map from every Pod status to the place its cause is written, and the habit of fixing with a command instead of an editor.

Troubleshooting · 30%35 min2 tasks

After this lesson

  • 01You can run the same loop (get, describe, logs, exec, node, control plane) on any failing object without stopping to decide what to type next.
  • 02You can read a Pod's STATUS column and go straight to the one place where the cause is recorded.
  • 03You can apply the fix with kubectl set, patch or replace --force and prove the end state before moving on.

Troubleshooting is 30% of the exam and its tasks carry the biggest weights. Few of them are hard once you know where the cause is written down. What costs candidates the exam is wandering: running get pods three times, reading a 200-line YAML from the top, or opening kubectl edit before knowing what to change. This lesson gives you a loop to run on autopilot and a map from every Pod status to the single place that explains it.

The loop

  1. Read the question twice. Note the namespace, the object names, and what fixed means: Running, Ready, reachable through a Service, a file written. That last part is what the grader checks.
  2. Get, wide, across the namespace. kubectl -n <ns> get all -o wide. The STATUS, READY, RESTARTS and NODE columns usually tell you which lesson of this module you are in. Remember that all omits ConfigMaps, Secrets, PVCs, Ingresses and NetworkPolicies; add them when the question mentions them.
  3. Describe the broken object and read from the bottom. Events first, then the container's State and Last State, then Conditions. The Events section names the missing key, the bad tag, the probe that fails, or the reason nothing could be scheduled.
  4. Logs. kubectl logs, with -c for a named container and --previous when the container restarted and is running again. This is where a wrong command, a missing file or a refused connection shows up.
  5. Exec. If the container runs, look from the inside: env, ls /etc/config, wget -qO- localhost:8080. It settles arguments about what the Pod actually sees.
  6. Node. kubectl get nodes and describe node. If the node is NotReady or under pressure, the Pod is a symptom; go to the worker-node lesson.
  7. Control plane. kubectl -n kube-system get pods. Pods that stay Pending with no events, Deployments that create nothing, or kubectl refusing to connect point at a component, not at the application.
The loop as commands (replace ns and pod)bash
ns=ts-method
kubectl -n $ns get all -o wide
kubectl -n $ns get events --sort-by=.lastTimestamp | tail -20   # or: kubectl -n $ns events --types=Warning
kubectl -n $ns describe pod <pod> | tail -25
kubectl -n $ns logs <pod> [-c <container>] [--previous]
kubectl -n $ns exec <pod> -- env
kubectl get nodes -o wide
kubectl -n kube-system get pods

What each status is telling you

Stop at the first step of the loop that names a cause; the exam breaks one thing per task, and hunting for a second problem is how a six-minute task becomes a fifteen-minute one. Most of the time the first step is enough, because the STATUS column already points at the place to look. It is a kubectl summary built from the Pod's phase, its container states and their reason fields (kubectl get pod -o jsonpath='{.status.containerStatuses[0].state}' shows the raw material). Read the table once; after that, each word sends you to the right place.

STATUSWhat it meansWhere the cause is written
Pending, NODE <none>The scheduler has not placed it: not enough resources, no node matches the selector or affinity, a taint is not tolerated, or a PVC is not bound. If the scheduler itself is down there is no event at all.describe pod → Events: FailedScheduling, which lists per node why it was rejected
ContainerCreatingScheduled; the kubelet is pulling the image, mounting volumes and setting up the network. Stuck for more than a minute means one of those failed.describe pod → Events: FailedMount (missing ConfigMap, Secret or PVC), FailedCreatePodSandBox (CNI), or a Pulling event that never finishes
ErrImagePull → ImagePullBackOffThe runtime could not fetch the image: wrong name or tag, private registry without credentials, or a registry that does not resolve.describe pod → Events: the exact reference the runtime tried and the registry's answer (not found, unauthorized, no such host)
CreateContainerConfigErrorThe image is fine but the container's configuration cannot be assembled: an env or envFrom reference points to a ConfigMap or Secret (or key) that does not exist.describe pod → Events: couldn't find key X in ConfigMap ns/name, or secret "name" not found
RunContainerError / StartErrorThe runtime could not start the process: the command's executable does not exist in the image, or a volume mount path is invalid.describe pod → Last State: Terminated, Reason StartError, and the Message with the runtime's error
CrashLoopBackOffThe process started and exited; the kubelet waits before restarting it (10s, 20s, 40s… up to 5 minutes, reset after 10 minutes of healthy running).logs (the last run's output), and describe pod → Last State: Exit Code
ErrorA container exited non-zero and is not being restarted right now: restartPolicy Never, or you caught it between exit and the next backoff.logs, then describe → State: Terminated, Exit Code
OOMKilledThe kernel killed the process for exceeding its memory limit. Exit code 137. Usually shows as CrashLoopBackOff with OOMKilled in Last State.describe pod → Last State: Terminated, Reason OOMKilled; compare resources.limits.memory with what the app needs
CompletedThe container exited 0 and the restart policy did not restart it. Correct for Jobs; for a Deployment it means the command returns immediately.logs, and spec.containers[].command
Init:0/1, Init:Error, Init:CrashLoopBackOffAn init container is still running, or failed. The app containers never started.logs <pod> -c <init-container>, and describe → Init Containers section
Running but READY 0/1The process runs but the readiness probe fails, so the Service will not send it traffic.describe pod → Events: Readiness probe failed, with the path and port it tried
Terminating (for minutes)Deletion is requested but not confirmed: the node is down so the kubelet cannot report, or a finalizer blocks it.get pod -o yaml → metadata.finalizers and spec.nodeName; get nodes
UnknownThe kubelet on the Pod's node has stopped reporting.get nodes → NotReady; the worker-node lesson

Reading describe from the bottom

kubectl describe pod prints the spec first and the useful part last. Read Events first: the From column tells you whether the scheduler or the kubelet complained, and the Message is usually the cause verbatim. Then look at each container's State, Last State, Reason and Exit Code. Then Conditions: PodScheduled false means the scheduler has not placed it; Initialized false means an init container has not finished; ContainersReady false means a probe or a crash.

Events are kept for one hour by default. A task that says the Pod broke yesterday may have no events left; the container's Last State and the logs do not expire, so go there. For a whole namespace at once, kubectl -n <ns> events --types=Warning shows only the problems, in time order.

Logs: current, previous, and gone

kubectl logs <pod> reads the current container. While a container sits in CrashLoopBackOff there is no current container, so the kubelet serves the output of the last one that ran; plain logs already shows the crash. kubectl logs --previous is for the other case: the container restarted and is Running again, so the crash output belongs to the previous instance. The kubelet keeps only one dead instance per container by default, so if --previous answers unable to retrieve container logs, that instance is already gone and the plain command is the best you have.

Log commands you will usebash
kubectl -n $ns logs <pod>                      # current container, or the last crashed one
kubectl -n $ns logs <pod> --previous           # the instance before the current one
kubectl -n $ns logs <pod> -c <container>       # multi-container and init containers
kubectl -n $ns logs <pod> --all-containers --tail=50
kubectl -n $ns logs deploy/<name>              # one Pod of the Deployment, chosen for you
kubectl -n $ns logs -l app=<label> --prefix    # every matching Pod, each line prefixed

Fix with a command, not an editor

kubectl edit is fine when you know the exact field, but it hides two traps: you can save a YAML with an indentation error and spend a minute reading the validation message, and for a bare Pod most fields are immutable, so the save is rejected anyway. Prefer a command that changes one field and nothing else. The grader reads the resulting object; it has no opinion about how it got there.

One-line fixes for the common causesbash
kubectl -n $ns set image deploy/web nginx=nginx:1.27
kubectl -n $ns set env deploy/web APP_MODE=production
kubectl -n $ns set resources deploy/web -c nginx --limits=memory=256Mi --requests=memory=128Mi
kubectl -n $ns patch deploy web -p '{"spec":{"template":{"spec":{"nodeSelector":{"kubernetes.io/hostname":"cka-worker"}}}}}'
kubectl -n $ns patch deploy web --type=json -p='[{"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/httpGet/port","value":80}]'
kubectl -n $ns scale deploy web --replicas=3
kubectl -n $ns rollout undo deploy/web

# A bare Pod with a wrong command, env, volume or probe cannot be patched in place: export, fix, recreate.
kubectl -n $ns get pod app -o yaml > app.yaml
vim app.yaml
kubectl -n $ns replace --force -f app.yaml      # add --grace-period=0 if the old Pod takes 30s to die

On a running Pod you may change only the image, activeDeadlineSeconds, terminationGracePeriodSeconds and add tolerations. Everything else (command, args, env, volumes, probes, nodeSelector, resources) needs a new Pod. When the Pod belongs to a Deployment, change the Deployment's template instead and let the rollout replace the Pods; fixing a Pod the ReplicaSet owns is undone the moment it is recreated.

Worked example: a Deployment that never comes up

The question says: Deployment report in namespace ts-method has no available Pods. Fix it. Steps 2 and 3 of the loop, trimmed to the lines that matter:

get, then describe the Pod and read its last eventtext
$ kubectl -n ts-method get deploy,pods -o wide
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/report   0/1     1            0           2m
NAME                          READY   STATUS    RESTARTS   AGE   IP       NODE
pod/report-677544647f-9m7nq   0/1     Pending   0          2m    <none>   <none>

$ kubectl -n ts-method describe pod report-677544647f-9m7nq | tail -2
  Type     Reason            Age   From               Message
  Warning  FailedScheduling  2m    default-scheduler  0/3 nodes are available: 1 node(s) had untolerated taint(s), 2 Insufficient memory. no new claims to deallocate, preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.

NODE is <none>, so the scheduler never placed it, and the event says why, node by node: the control plane rejects the Pod because of its taint, which is normal; the two workers reject it for memory. The request is bigger than any worker can offer. Confirm, fix with one command, and prove the end state:

Confirm, fix, verifybash
kubectl -n ts-method get deploy report -o jsonpath='{.spec.template.spec.containers[0].resources}'
# {"requests":{"memory":"64Gi"}}
kubectl describe node cka-worker | grep -A6 '^Allocatable'

kubectl -n ts-method set resources deploy report --requests=memory=64Mi
kubectl -n ts-method rollout status deploy/report --timeout=60s
kubectl -n ts-method get pods -o wide        # Running, 1/1, a NODE name

Three commands of diagnosis, one of repair, one of proof. If the question had said the application needs 2Gi, the right fix would have been the same command with that value; the question defines what correct means, the events only tell you what is wrong.

In the docs · the tab you get on exam day

Taskts-method-1
Troubleshooting4%~5 min

context · kubectl config use-context kind-cka

A Deployment with no ready Pods

The Deployment web in namespace ts-method is supposed to serve nginx 1.27 but has had no ready Pods since it was created. Find out why and fix it. When you are done, both replicas must be Running and Ready and the Deployment must use the image nginx:1.27.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-1/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-1/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-1/cleanup | bash
Taskts-method-2
Troubleshooting5%~7 min

context · kubectl config use-context kind-cka

A Pod waiting on configuration that is not there

The Pod app in namespace ts-method has not started. It reads its settings from the ConfigMap app-config, which the application team has confirmed is correct and must not be modified. Make the Pod Running and Ready without changing the ConfigMap.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-2/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-2/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/ts-method-2/cleanup | bash

Finished reading?

0 of 2 tasks passed. Readiness counts tasks, so run them before you move on.