Introduction
The Certified Kubernetes Application Developer (CKAD) exam is a hands-on, command-line-heavy challenge that tests your ability to design, build, and deploy applications in Kubernetes - all while racing the clock.
I recently took the CKAD and passed with an overall score of around 82%. Not perfect - but let’s be honest, in a terminal-only, time-boxed environment, "running" is the new "succeeding" 😄.
This article collects the practical snippets, workflows, and quick references I used during my preparation.
General Tips
-
Use a consistent naming schema for YAML files, e.g. Question 1: Create Deployment →
q1-deploy.yaml
-
Always run
helm repo update
before pulling from a Helm repo. -
To inspect a running pod, open a shell:
k exec -it podname — sh
-
Use
k explain
to explore Kubernetes object definitions, e.g.:-
k explain ingress
-
k explain ingress.spec
-
k explain ingress.spec.rules
-
Core Workflows
Pods and Deployments
Create a pod with image xxx:1.2.3
and container yyy
-
Generate YAML:
k run podname --image=xxx:1.2.3 --dry-run=client -o yaml > q1-pod.yaml
-
Edit
q1-pod.yaml
and set the container name.k create -f q1-pod.yaml k get po
Create a pod that runs a command
For example, a pod that simply runs du -hs .
|
k run podname --image=busybox:latest --dry-run=client -o yaml --command -- sh -c 'du -hs' > q1-pod.yaml
Convert a pod to a deployment
-
Export the pod YAML.
-
Copy a sample deployment from the docs.
-
Copy the pod’s
metadata
andspec
into the deployment’stemplate
section. -
Adjust
spec.selector.matchLabels
to match one of the labels inspec.template.metadata.labels
.
Rollback a deployment
k -n namespace get deploy (1)
k -n namespace rollout history deploy my-deployment (2)
k -n namespace rollout undo deploy my-deployment (3)
1 | List deployments |
2 | Show rollout history |
3 | Roll back to a previous revision |
Move a running pod to another namespace
k -n oldns get po thepod -o yaml > q1-pod.yaml
vim q1-pod.yaml (1)
k -n newns create -f q1-pod.yaml
k -n newns get po
1 | Change namespace and remove status , token volumes, and nodeName . |
Services and Networking
Create a service
The fastest way is to use k expose pod .
|
k -n namespace expose pod pod-name --name svc-name --port 3333 --target-port 80
Alternatively, create a YAML definition:
k -n namespace create service clusterip svcname --tcp 3333:80 --dry-run=client -o yaml > q1-svc.yaml
vim q1-svc.yaml (1)
1 | Adjust the selector labels. |
Check that the service has an active endpoint:
k -n namespace get ep
Run a temporary pod to test a service
k run tmp --restart=Never --rm --image=nginx:alpine -i -- curl http://svcname.namespace:3333
Jobs and CronJobs
Create a job that runs a command
We want to create a job that runs ls -la
with 3 completions and 2 parallel pods.
k create job jobname --image=xxxx:1.2.3 -- sh -c "ls -la" --dry-run=client -o yaml > q2-job.yaml
Edit:
spec:
completions: 3
parallelism: 2
Configuration
Secrets (as environment variables and volumes)
We need a secret thesecret
with username=admin
and password=irsosecret
.
-
Create the secret:
k -n namespace create secret generic thesecret --from-literal=username=admin --from-literal=password=irsosecret
-
Reference it in a pod:
- name: S_USERNAME valueFrom: secretKeyRef: name: thesecret key: username - name: S_PASSWORD valueFrom: secretKeyRef: name: thesecret key: password
-
Test:
k exec -it nginx -- sh -c printenv | grep S_
Mount the secret as a volume:
spec:
volumes:
- name: mysecret-volume
secret:
secretName: thesecret
containers:
- name: nginx
volumeMounts:
- name: mysecret-volume
mountPath: /tmp/mysecret
Check contents:
k exec -it nginx -- sh -c 'ls -la /tmp/mysecret && cat /tmp/mysecret/*'
ConfigMaps
Mount as env
k create cm envconf --from-literal=mykey=myvalue
k run nginx --image=nginx:latest -o yaml --dry-run=client > q1_pod.yaml
Modify q1_pod.yaml
:
envFrom:
- configMapRef:
name: envconf
Create and test:
k create -f q1_pod.yaml
k exec -it nginx -- sh -c printenv | grep mykey
mykey=myvalue
Mount as volume
echo '<html><head><body><h1>served from configmap</h1></body></html>' > file.html
k create cm htmlconfig --from-file=index.html=file.html
k run nginx --image=nginx:latest -o yaml --dry-run=client > q2_pod.yaml
Modify q2_pod.yaml
:
volumes:
- name: config-volume
configMap:
name: htmlconfig
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /usr/share/nginx/html
Deploy and test:
k create -f q2_pod.yaml
k exec -it nginx -- sh -c 'curl localhost:80'
<html><head><body><h1>served from configmap</h1></body></html>
Storage (Volumes, PVs, PVCs)
We’ll create a PersistentVolume, PersistentVolumeClaim, and a Deployment that mounts it.
-
Copy PV and PVC specs from Kubernetes docs and adjust.
-
Verify that both are
Bound
:k -n namespace get pv,pvc
-
Create a Deployment referencing the PVC and confirm mount:
k -n namespace describe pod pod-from-deployment | grep -A2 Mounts:
Helm
Always run helm repo update before installing.
|
helm -n namespace ls
helm -n namespace uninstall releasename
helm repo list
helm repo update
helm search repo nginx
helm -n namespace upgrade myrelease bitnami/nginx
helm show values bitnami/apache (1)
helm -n namespace install myrelease bitnami/apache --set replicaCount=2 (2)
1 | Show default Helm values |
2 | Override via --set |
helm -n namespace ls -a (1)
1 | The -a flag shows all (including failed) releases. |
Containers, Docker, Podman
Docker
xoxo
with tags v1
and latest
sudo docker build -t registry:5000/xoxo:latest -t registry:5000/xoxo:v1 .
sudo docker image ls
sudo docker push registry:5000/xoxo:latest
sudo docker push registry:5000/xoxo:v1
Podman
xoxo:v1
podman build -t registry:5000/xoxo:v1 .
podman images
podman push registry:5000/xoxo:v1
podman run -d --name xoxo registry:5000/xoxo:v1
podman ps
podman logs xoxo
Advanced Topics
InitContainers
apiVersion: v1
kind: Pod
metadata:
name: pod-with-init
spec:
initContainers:
- name: init
image: busybox
command: ['sh', '-c', 'echo initializing && sleep 5']
containers:
- name: main
image: nginx
NetworkPolicies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: netpol-test
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Resource Limits
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
Labels and Annotations
k label pod mypod env=prod
k annotate pod mypod description="frontend pod"
Common Pitfalls
-
Forgetting
-n
when switching namespaces. -
Copying
status:
or serviceAccount tokens when cloning pods. -
Misspelled image names.
-
Missing
spec.selector.matchLabels
in deployments. -
Ignoring
kubectl get events
for debugging.
Exam Time Management
-
Duration: 2 hours - aim for ~5 minutes per question.
-
Mark tricky questions and return later.
-
Use
kubectl explain
instead of searching docs. -
Practice with
alias k=kubectl
for speed. -
Generate YAMLs with
--dry-run=client -o yaml
. -
Always verify with
kubectl get all -n <ns>
.
Resources
Final Thoughts
The CKAD exam is less about theory and more about speed and precision under pressure.
Practice each task until it feels automatic.
Good luck - and may your pods always be Running
!