CKAD study materials
CKAD study resources

CKAD common mistakes

CKAD · Understand the traps and how to avoid them.

Mistake 1

Writing Pod, Deployment, and Job manifests from memory in the exam editor, then spending minutes hunting for the indentation or field error after `kubectl apply` rejects the file.

Hand-writing YAML feels faster under time pressure, but recall of exact field nesting (containers list, ports, resources) is unreliable and typos are silent or produce cryptic validation errors.

What to do instead

Always generate a skeleton imperatively and edit it: kubectl run web --image=nginx --dry-run=client -o yaml > pod.yaml, kubectl create deployment web --image=nginx --replicas=3 --dry-run=client -o yaml > deploy.yaml, kubectl create job/cronjob ... --dry-run=client -o yaml. Then only add the fields the task actually requires.

Mistake 2

Needing a container that waits for a dependency or prepares data before the app starts, and adding it as a second entry under `containers:` (or expecting a sidecar to run and finish first).

The candidate knows multi-container pods exist but confuses run-to-completion ordering with the always-running sidecar pattern, so the app container starts before its dependency is ready and crashes.

What to do instead

Put sequential, must-finish-first work under initContainers: (they run one at a time, in order, and must exit 0 before app containers start). Use extra containers only for long-running helpers (log shipper, proxy). Share files between them via an emptyDir volume mounted at the same path in both containers.

Mistake 3

Rolling back a bad Deployment by editing the image back by hand with `kubectl edit`/`kubectl set image`, instead of using revision history.

Editing feels like the obvious undo, but it creates a new revision, may not restore other changed fields (env, resources, probes), and wastes time reconstructing the previous spec.

What to do instead

Use the rollout subcommands: kubectl rollout status deploy/web, kubectl rollout history deploy/web, kubectl rollout undo deploy/web or --to-revision=N. Annotate revisions for readability with kubernetes.io/change-cause in the pod template metadata rather than the deprecated --record flag.

Mistake 4

Running `kubectl apply -f kustomization.yaml` (or `-f ./overlays/prod`) and getting a confusing error, or hand-assembling an overlay directory from scratch.

Kustomize input is not a Kubernetes object manifest, so applying it directly fails; and building base/overlay trees from memory under time pressure invites path and field mistakes.

What to do instead

Apply a directory with kubectl apply -k ./overlays/prod and render it locally with kubectl kustomize ./overlays/prod to inspect output before applying. Scaffold with kubectl create deployment ... --dry-run=client -o yaml > base/deploy.yaml, then add a minimal kustomization.yaml with resources: and only the patches needed (e.g. images: or a strategic-merge patch).

Mistake 5

Debugging a CrashLoopBackOff or pending Pod by repeatedly changing the manifest while running only `kubectl logs <pod>`, which returns nothing or an old container's output.

For a restarted container the current log stream is empty, and Pod-level scheduling or image-pull problems never appear in container logs at all; without reading events the candidate guesses at the cause.

What to do instead

Triage in a fixed order: kubectl describe pod <pod> (Events section shows scheduling, image pull, probe and config errors), kubectl logs <pod> --previous for the crashed instance, kubectl logs <pod> -c <container> --all-containers for multi-container pods, kubectl get events --sort-by=.metadata.creationTimestamp, and kubectl exec -it <pod> -- sh to inspect the running container's filesystem and env.

Mistake 6

Adding a `livenessProbe` with a short `initialDelaySeconds` to a slow-starting application, which then restarts continuously, and using liveness and readiness interchangeably.

The distinction is often memorized as "health checks" without the operational consequence: failing liveness kills and restarts the container, failing readiness only removes it from Service endpoints.

What to do instead

Use readinessProbe to gate traffic while the app warms up, livenessProbe only to recover from true deadlocks, and a startupProbe (with failureThreshold/periodSeconds sized to the boot time) to protect slow starters from liveness restarts. Verify the chosen port and path actually answer, using kubectl exec to curl the endpoint before finalizing thresholds.

Mistake 7

Referencing a ConfigMap or Secret key that does not match what was created (or using `envFrom` vs `configMapKeyRef` inconsistently), producing CreateContainerConfigError and a Pod stuck in Pending/ContainerCreating.

Keys are easy to mistype and the failure message only appears in Pod events, not in kubectl apply output, so the candidate assumes the manifest was fine because it was accepted by the API server.

What to do instead

Create the object imperatively so the keys are known-good (kubectl create configmap app-cfg --from-literal=APP_MODE=prod), list keys with kubectl get cm app-cfg -o yaml, then reference them exactly. Use envFrom.configMapRef to inject all keys, and valueFrom.configMapKeyRef/secretKeyRef with optional: false semantics only for specific keys. Describe the Pod to confirm the mismatch.

Mistake 8

Setting `runAsNonRoot: true` or `readOnlyRootFilesystem: true` at the Pod level and applying it to an image that runs as root or writes to its own filesystem, leaving the container unable to start; or granting permissions by editing the ServiceAccount instead of creating a Role and RoleBinding.

The fields are remembered but their interaction with the actual image user and filesystem is not, and RBAC is often attempted through the ServiceAccount object, which carries no permissions itself.

What to do instead

Check the image's user first (kubectl run t --image=<img> --rm -it -- whoami), then set securityContext.runAsUser/runAsNonRoot at the container level where it applies, and mount an emptyDir at any path the app must write to when using a read-only root. For permissions, create a ServiceAccount, a Role with explicit apiGroups/resources/verbs, and a RoleBinding (kubectl create role ..., kubectl create rolebinding ...), then set serviceAccountName in the pod spec.

Mistake 9

Deploying a Service whose `selector` does not match the Pod template labels, or confusing `port` with `targetPort`/`nodePort`, and never confirming that endpoints were populated.

The Service applies cleanly and looks healthy in kubectl get svc, so the mistake only shows up as connection failures; label mismatch and port mismatch are the two silent causes.

What to do instead

Copy the selector from the workload's spec.template.metadata.labels verbatim, treat port as the Service's own port and targetPort as the container's listening port, and after applying verify with kubectl get endpoints <svc> (or kubectl get endpointslices) that Pod IPs are listed. If the list is empty, the selector is wrong; if it has IPs but connections fail, the targetPort or the app's bind address is wrong.

Mistake 10

Writing a NetworkPolicy that intends to allow traffic but leaves the Pod isolated, e.g. allowing ingress only, forgetting that selecting a Pod for `Ingress` denies all other inbound, or omitting an egress rule that permits DNS on port 53.

Policies are additive and default-deny once a Pod is selected, and the selector semantics inside from/to elements are easy to get wrong: selectors in the same element are ANDed, separate elements are ORed.

What to do instead

For each policy, reason about both directions explicitly: select the target Pods with podSelector, then list only the allowed peers. Include a namespace allowance via namespaceSelector combined with podSelector in the same element, and for egress add a rule permitting UDP and TCP port 53 to the cluster DNS namespace plus whatever the app must reach. Test with kubectl exec from a Pod that should be allowed and one that should be denied.