CKAD command cheat sheet
The commands a CKAD task actually needs, grouped by what you are trying to do - generate a manifest, change a live object, wire in configuration, expose something, and find out why it is broken. Every output below is from a real session on the path's cluster, not an illustration.
- Kubernetes1.36.4
- Runtimecontainerd 2.2.6
- CNICalico v3.32.1
- Commands49
- Reviewed23 August 2026
Generate a manifest instead of typing one
-
kubectl run NAME --image=IMG --dry-run=client -o yamlA Pod skeleton. Redirect it to a file and edit - never type YAML from memory.
bash Example session kubectl -n ckad-gen run web --image=nginx:alpine --dry-run=client -o yamlapiVersion: v1kind: Podmetadata: labels: run: web name: web namespace: ckad-genspec: containers: - image: nginx:alpine name: web resources: {} dnsPolicy: ClusterFirst restartPolicy: Alwaysstatus: {} -
kubectl create deployment NAME --image=IMG --replicas=N --dry-run=client -o yamlA Deployment skeleton, including the selector and template labels.
bash Example session kubectl -n ckad-gen create deployment api --image=nginx:alpine --replicas=3 --dry-run=client -o yaml | head -20apiVersion: apps/v1kind: Deploymentmetadata: labels: app: api name: api namespace: ckad-genspec: replicas: 3 selector: matchLabels: app: api -
kubectl create job NAME --image=IMG --dry-run=client -o yaml -- CMD ARGSA Job. Everything after -- becomes the container args.
bash Example session kubectl -n ckad-gen create job pi --image=perl:5.34 --dry-run=client -o yaml -- perl -Mbignum=bpi -wle 'print bpi(200)'apiVersion: batch/v1kind: Jobmetadata: name: pi namespace: ckad-genspec: template: metadata: {} spec: containers: - command: - perl - -Mbignum=bpi - -wle -
kubectl create cronjob NAME --image=IMG --schedule='*/1 * * * *' -- CMDA CronJob, with the four-level jobTemplate nesting filled in correctly.
bash Example session kubectl -n ckad-gen create cronjob tick --image=busybox:1.36 --schedule='*/1 * * * *' --dry-run=client -o yaml -- /bin/sh -c dateapiVersion: batch/v1kind: CronJobmetadata: name: tick namespace: ckad-genspec: jobTemplate: metadata: name: tick spec: template: metadata: {} spec: containers: -
kubectl create secret generic NAME --from-literal=k=v --dry-run=client -o yamlA Secret with the base64 already done for you.
bash Example session kubectl -n ckad-gen create secret generic api-key --from-literal=token=s3cr3t --dry-run=client -o yamlapiVersion: v1data: token: czNjcjN0kind: Secretmetadata: name: api-key namespace: ckad-gen -
kubectl expose deployment NAME --port=P --target-port=T --dry-run=client -o yamlA Service whose selector is copied from the Deployment - the field most often got wrong by hand.
bash Example session kubectl -n ckad-gen expose deployment api --port=80 --target-port=80 --dry-run=client -o yamlapiVersion: v1kind: Servicemetadata: labels: app: api name: api namespace: ckad-genspec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: api -
kubectl create ingress NAME --rule='host/path=svc:port' --dry-run=client -o yamlAn Ingress, expanded from a compact rule string.
bash Example session kubectl -n ckad-gen create ingress site --rule='shop.example.com/*=api:80' --dry-run=client -o yamlapiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: site namespace: ckad-genspec: rules: - host: shop.example.com http: paths: - backend: service: name: api port: number: 80 path: /
Namespace and context
-
kubectl config set-context --current --namespace=NSSet the namespace once per task. Removes the whole class of mistakes caused by a forgotten -n.
bash Example session kubectl config set-context --current --namespace=ckad-nsContext "kubernetes-admin@kubernetes" modified. -
kubectl config view --minify -o jsonpath='{..namespace}'Which namespace kubectl is pointed at. Empty means default.
bash Example session kubectl config view --minify -o jsonpath='{..namespace}'ckad-ns -
kubectl get pods -A --field-selector=status.phase=RunningServer-side filtering on the handful of indexed fields.
bash Example session kubectl get pods -A --field-selector=status.phase=Running --no-headers | wc -l18 -
kubectl api-resourcesapiVersion and short name for every kind the cluster serves.
bash Example session kubectl api-resources | grep -E '^(cronjobs|ingresses|horizontalpodautoscalers|networkpolicies) 'horizontalpodautoscalers hpa autoscaling/v2 true HorizontalPodAutoscalercronjobs cj batch/v1 true CronJobnetworkpolicies crd.projectcalico.org/v1 true NetworkPolicyingresses ing networking.k8s.io/v1 true Ingressnetworkpolicies netpol networking.k8s.io/v1 true NetworkPolicynetworkpolicies cnp,caliconetworkpolicy,caliconetworkpolicies projectcalico.org/v3 true NetworkPolicy
Change something that already exists
-
kubectl set image deployment/NAME CONTAINER=IMAGEChange the image and trigger a rollout, with no manifest edit.
bash Example session kubectl -n ckad-mod set image deployment/api nginx=nginx:1.28-alpinedeployment.apps/api image updated -
kubectl set env deployment/NAME KEY=VALUEAdd or change an environment variable. A trailing dash (KEY-) removes one.
bash Example session kubectl -n ckad-mod set env deployment/api MODE=proddeployment.apps/api env updated -
kubectl set resources deployment/NAME --requests=cpu=100m --limits=memory=128MiRequests and limits without opening an editor.
bash Example session kubectl -n ckad-mod set resources deployment/api --limits=memory=128Mi --requests=memory=64Mideployment.apps/api resource requirements updated -
kubectl patch deployment NAME --type=merge -p '{...}'Reaches any field - but a Deployment selector is immutable, as shown.
bash Example session kubectl -n ckad-mod patch deployment api --type=merge -p '{"spec":{"selector":{"matchLabels":{"app":"api","tier":"backend"}}}}'The Deployment "api" is invalid: spec.selector: Invalid value: {"matchLabels":{"app":"api","tier":"backend"}}: field is immutable[exit 1] -
kubectl scale deployment NAME --replicas=NImmediate, no rollout. --replicas=0 stops a workload without deleting it.
bash Example session kubectl -n ckad-scale scale deployment web --replicas=4deployment.apps/web scaled -
kubectl rollout history deploy/NAMERevisions, with CHANGE-CAUSE if the annotation was set.
bash Example session kubectl -n ckad-undo rollout history deploy/apideployment.apps/apiREVISION CHANGE-CAUSE1 v1: initial rollout on nginx 1.252 v2: bump to nginx 1.273 v3: bump to nginx 1.28 -
kubectl rollout undo deploy/NAME --to-revision=NGo back to a named revision. The history renumbers afterwards.
bash Example session kubectl -n ckad-undo rollout undo deploy/api --to-revision=1deployment.apps/api rolled back -
kubectl rollout pause deploy/NAMEBatch several edits into one rollout. Resume with rollout resume.
bash Example session kubectl -n ckad-undo rollout pause deploy/apideployment.apps/api paused
Configuration, secrets and limits
-
kubectl create configmap NAME --from-literal=K=VOne key per --from-literal. --from-env-file gives one key per line; --from-file gives one key holding the whole file.
bash Example session kubectl -n ckad-cm create configmap literal --from-literal=MODE=prod --from-literal=TIMEOUT=30configmap/literal created -
kubectl get pod NAME -o jsonpath='{.status.containerStatuses[0].state.waiting.message}'CreateContainerConfigError names the missing ConfigMap or Secret key exactly.
bash Example session kubectl -n ckad-cm get pod missingkey -o 'custom-columns=PHASE:.status.phase,REASON:.status.containerStatuses[0].state.waiting.reason,MSG:.status.containerStatuses[0].state.waiting.message'PHASE REASON MSGPending CreateContainerConfigError couldn't find key DOES_NOT_EXIST in ConfigMap ckad-cm/literal -
kubectl get secret NAME -o jsonpath='{.data.KEY}' | base64 -dDecode a Secret. Base64 is encoding, not encryption.
bash Example session kubectl -n ckad-sec get secret api -o jsonpath='{.data.password}' | base64 -dhunter2-not-secret -
kubectl create secret docker-registry NAME --docker-server=... --docker-username=...The typed Secret imagePullSecrets requires. An Opaque one is ignored.
bash Example session kubectl -n ckad-sec create secret docker-registry regcred --docker-server=registry.example.com --docker-username=bot --docker-password=s3cr3t --docker-email=bot@example.comsecret/regcred created -
kubectl get pod NAME -o jsonpath='{.status.containerStatuses[0].state.terminated}'OOMKilled with exit 137 means the memory limit was hit. SIGKILL leaves no log.
bash Example session kubectl -n ckad-lim get pod greedy -o 'custom-columns=PHASE:.status.phase,REASON:.status.containerStatuses[0].state.terminated.reason,EXIT:.status.containerStatuses[0].state.terminated.exitCode'PHASE REASON EXITFailed OOMKilled 137 -
kubectl run NAME --image=IMG # in a namespace with a compute ResourceQuotaA quota on a compute resource makes requests and limits mandatory.
bash Example session kubectl -n ckad-quota run plain --image=nginx:alpine --restart=NeverError from server (Forbidden): pods "plain" is forbidden: failed quota: compute: must specify limits.cpu for: plain; limits.memory for: plain; requests.cpu for: plain; requests.memory for: plain[exit 1] -
kubectl auth can-i VERB RESOURCE --as=system:serviceaccount:NS:NAMECheck a permission without deploying anything. Note the four-part username.
bash Example session kubectl -n ckad-sa auth can-i list pods --as=system:serviceaccount:ckad-sa:readeryes -
kubectl create role NAME --verb=get,list,watch --resource=podsA namespaced Role. Bind it with kubectl create rolebinding.
bash Example session kubectl -n ckad-sa create role pod-reader --verb=get,list,watch --resource=podsrole.rbac.authorization.k8s.io/pod-reader created
Services, ingress and reaching things
-
kubectl get endpointslice -l kubernetes.io/service-name=SVCThe first check on any Service. No endpoints means the selector matches nothing.
bash Example session sleep 5; kubectl -n ckad-dns get endpointslice -l kubernetes.io/service-name=api -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{" ready="}{.conditions.ready}{"\n"}{end}'10.244.86.211 ready=true10.244.100.5 ready=true -
kubectl exec POD -- cat /etc/resolv.confThe search list and ndots that decide which short names resolve.
bash Example session kubectl -n ckad-dns exec client -- cat /etc/resolv.confsearch ckad-dns.svc.cluster.local svc.cluster.local cluster.local practicelabpro.localnameserver 10.96.0.10options ndots:5 -
kubectl get pod -l app=X -o jsonpath='{.items[0].spec.containers[0].ports}'containerPort is documentation. targetPort is what routes.
bash Example session kubectl -n ckad-port get pod -l app=web -o jsonpath='{.items[0].spec.containers[0].ports}'[{"containerPort":9999,"protocol":"TCP"}] -
kubectl exec POD -- nslookup NAMEA default-deny egress policy blocks DNS too - the symptom looks like broken DNS.
bash Example session kubectl -n ckad-np exec client -- nslookup api 2>&1 | head -4;; connection timed out; no servers could be reached command terminated with exit code 1 -
kubectl port-forward svc/NAME LOCAL:REMOTEReach a ClusterIP Service from your own machine without changing it.
bash Example session (kubectl -n ckad-fwd port-forward svc/web 18080:80 >/tmp/pf.log 2>&1 & echo $! > /tmp/pf.pid) ; sleep 4; curl -s -o /dev/null -w 'HTTP %{http_code} from 127.0.0.1:18080\n' http://127.0.0.1:18080/HTTP 200 from 127.0.0.1:18080 -
kubectl patch svc NAME --type=merge -p '{"spec":{"type":"NodePort"}}'The real way to expose something. port-forward is for debugging only.
bash Example session kubectl -n ckad-fwd patch svc web --type=merge -p '{"spec":{"type":"NodePort"}}'service/web patched -
kubectl get pvcPending with WaitForFirstConsumer and no Pod yet is normal, not a fault.
bash Example session sleep 10; kubectl -n ckad-vol get pvc dataNAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGEdata Pending local-path <unset> 10s
Resources that extend Kubernetes
-
kubectl get crdEverything installed on this cluster that is not built in.
bash Example session kubectl get crd --no-headers | wc -l76 -
kubectl api-resources --api-group=GROUPThe kinds one extension serves, with short names - exactly as for native objects.
bash Example session kubectl api-resources --api-group=argoproj.ioNAME SHORTNAMES APIVERSION NAMESPACED KINDanalysisruns ar argoproj.io/v1alpha1 true AnalysisRunanalysistemplates at argoproj.io/v1alpha1 true AnalysisTemplateapplications app,apps argoproj.io/v1alpha1 true Applicationapplicationsets appset,appsets argoproj.io/v1alpha1 true ApplicationSetappprojects appproj,appprojs argoproj.io/v1alpha1 true AppProjectclusteranalysistemplates cat argoproj.io/v1alpha1 false ClusterAnalysisTemplateexperiments exp argoproj.io/v1alpha1 true Experimentrollouts ro argoproj.io/v1alpha1 true Rollout -
kubectl explain KIND.spec --api-version=GROUP/VERSIONThe schema of a custom resource, read from the CRD the cluster holds.
bash Example session kubectl explain widget.spec --api-version=ckad.certgrid.test/v1GROUP: ckad.certgrid.testKIND: WidgetVERSION: v1 FIELD: spec <Object> DESCRIPTION: <empty>FIELDS: colour <string> <no description> size <integer> -required- <no description> -
kubectl api-resources | grep KINDThe APIVERSION column is the fix for every "no matches for kind" error.
bash Example session kubectl api-resources | grep -E '^(ingresses|poddisruptionbudgets|cronjobs|horizontalpodautoscalers) 'horizontalpodautoscalers hpa autoscaling/v2 true HorizontalPodAutoscalercronjobs cj batch/v1 true CronJobingresses ing networking.k8s.io/v1 true Ingresspoddisruptionbudgets pdb policy/v1 true PodDisruptionBudget -
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apisWhich deprecated APIs this cluster is still being asked for - before you upgrade.
bash Example session kubectl get --raw /metrics 2>/dev/null | grep '^apiserver_requested_deprecated_apis' | head -5apiserver_requested_deprecated_apis{group="",removed_release="",resource="endpoints",subresource="",version="v1"} 1 -
kubectl get deploy NAME -o custom-columns=READY:.status.readyReplicas,AVAILABLE:.status.availableReplicasreadyReplicas ahead of availableReplicas means minReadySeconds has not elapsed.
bash Example session sleep 8; kubectl -n ckad-ready get deploy web -o 'custom-columns=READY:.status.readyReplicas,AVAILABLE:.status.availableReplicas,MINREADY:.spec.minReadySeconds'READY AVAILABLE MINREADY4 3 20 -
kubectl get pods -o custom-columns=POD:.metadata.name,OWNER:.metadata.ownerReferences[0].kindWhich controller made each Pod. Deployment Pods are owned by a ReplicaSet.
bash Example session kubectl -n ckad-wl get pods -o 'custom-columns=POD:.metadata.name,OWNER:.metadata.ownerReferences[0].kind,NODE:.spec.nodeName' --sort-by=.metadata.namePOD OWNER NODEagent-6ckgf DaemonSet cka1001-node01agent-h2c7b DaemonSet cka1001-node02agent-rfhqf DaemonSet cka1001-node03db-0 StatefulSet cka1001-node02db-1 StatefulSet cka1001-node03once-xstmc Job cka1001-node03web-5d6b5c7df9-4pmxx ReplicaSet cka1001-node02web-5d6b5c7df9-r7q9f ReplicaSet cka1001-node01
Find out why it is broken
-
kubectl logs POD --previousThe log of the container that actually crashed. The current one has not failed yet.
bash Example session kubectl -n ckad-logs logs flaky --previousstarting upconfig loadedFATAL: cannot reach database -
kubectl logs -l LABEL --prefix=true --tail=NEvery matching Pod at once, with the Pod name on each line.
bash Example session kubectl -n ckad-logs logs -l app=web --tail=1 --prefix=true[pod/web-5d6b5c7df9-4flqp/nginx] 2026/08/23 04:22:07 [notice] 1#1: start worker process 30[pod/web-5d6b5c7df9-k7qz7/nginx] 2026/08/23 04:22:07 [notice] 1#1: start worker process 31[pod/web-5d6b5c7df9-zzq5p/nginx] 2026/08/23 04:22:07 [notice] 1#1: start worker process 31 -
kubectl get events --sort-by=.lastTimestampEvents are unordered by default. Add --field-selector type=Warning to cut the noise.
bash Example session kubectl -n ckad-ev get events --sort-by=.lastTimestamp -o 'custom-columns=TIME:.lastTimestamp,TYPE:.type,REASON:.reason,OBJECT:.involvedObject.name,MESSAGE:.message' --no-headers | tail -62026-08-23T04:17:14Z Warning FailedScheduling toobig 0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 Insufficient cpu, 3 Insufficient memory. no new claims to deallocate, preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.2026-08-23T04:17:30Z Normal Pulling nosuchimage Pulling image "nginx:this-tag-does-not-exist"2026-08-23T04:17:33Z Warning Failed nosuchimage Failed to pull image "nginx:this-tag-does-not-exist": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/nginx:this-tag-does-not-exist": failed to resolve image: docker.io/library/nginx:this-tag-does-not-exist: not found2026-08-23T04:17:33Z Warning Failed nosuchimage Error: ErrImagePull -
kubectl get events --field-selector type=WarningUsually the whole diagnosis in a busy namespace.
bash Example session kubectl -n ckad-ev get events --field-selector type=Warning -o 'custom-columns=REASON:.reason,OBJECT:.involvedObject.name' --no-headers | sort -uFailed nosuchimageFailedScheduling toobig -
kubectl top pods --containersActual consumption. The scheduler, though, counts requests rather than usage.
bash Example session sleep 45; kubectl -n ckad-ev top podsNAME CPU(cores) MEMORY(bytes)busy-58d6d9dfc6-hnzpz 0m 3Mibusy-58d6d9dfc6-rg4c2 0m 6Mi -
kubectl debug POD --image=busybox --target=CONTAINER -it -- shAttach a shell to a Pod whose image has none. Shares the Pod network.
bash Example session kubectl -n ckad-dbg debug minimal --image=busybox:1.36 --target=app --container=dbg -- /bin/sh -c 'echo "--- hostname is the pod name:"; hostname; echo "--- /etc/hosts is the pod network:"; grep -v "^#" /etc/hosts | grep -v "^$"; echo "--- my own IP:"; ifconfig eth0 2>/dev/null | grep "inet addr" || hostname -i'Targeting container "app". If you don't see processes from this container it may be because the container runtime doesn't support this feature. -
kubectl debug POD --copy-to=NEW --container=C --image=busybox -- shDebug a copy when the original will not stay up. The original is untouched.
bash kubectl -n ckad-dbg debug minimal --copy-to=minimal-debug --container=app --image=busybox:1.36 -- /bin/sh -c 'echo "this copy runs a shell instead"; sleep 300' -
kubectl describe pod NAME | grep -E "Liveness|Killing"A restart loop on a slow-starting app is usually the liveness probe, not a bug.
bash Example session kubectl -n ckad-start describe pod slow | grep -E "Liveness probe failed|Killing" | head -3 Warning Unhealthy 0s (x6 over 55s) kubelet spec.containers{app}: Liveness probe failed: Get "http://10.244.86.238:80/": dial tcp 10.244.86.238:80: connect: connection refused Normal Killing 0s (x2 over 45s) kubelet spec.containers{app}: Container app failed liveness probe, will be restarted
No command matches that search.