☀️good morning

$ cheat sheet

CKAD
Imperative

Every kubectl command worth memorizing. Generate, verify, ship.

$

Setup & Context

1 snippet
alias & completion
alias k=kubectl
export do="--dry-run=client -o yaml"

kubectl config set-context --current --namespace=<namespace-name>
Drop these in your shell rc. The exam terminal already has kubectl completion sourced for bash.
--

Dry-run & Generate

2 snippets
the core pattern
# Generate YAML without creating the resource
kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml

# Shortcut with the alias
k run nginx --image=nginx $do > pod.yaml

# Then edit and apply
vim pod.yaml
k apply -f pod.yaml
This is the single most important CKAD trick. Generate a manifest, edit it, then apply.
what supports --dry-run
kubectl run         # Pod
kubectl create      # Deployment, Job, CronJob, ConfigMap,
                    # Secret, Service, Role, RoleBinding,
                    # ServiceAccount, Namespace, Quota, Ingress
kubectl expose      # Service from existing workload
kubectl set image   # Image update preview
Only kubectl run, create, expose, set, and a few others. kubectl edit, scale, label do not.
po

Pods

6 snippets
basic pod
# Simplest form
kubectl run nginx --image=nginx

# With port and labels
kubectl run nginx --image=nginx \
  --port=80 \
  --labels="app=web,tier=front"
pod with env vars
kubectl run nginx --image=nginx \
  --env="DB_HOST=db" \
  --env="DB_PORT=5432"
pod with command & args
# Override args only
kubectl run busybox --image=busybox -- sleep 3600

# Override entrypoint and args
kubectl run busybox --image=busybox \
  --command -- /bin/sh -c "echo hello; sleep 3600"
Everything after -- becomes the container args. Use --command to override the entrypoint.
pod with resources
kubectl run nginx --image=nginx \
  --requests='cpu=100m,memory=128Mi' \
  --limits='cpu=200m,memory=256Mi'
ephemeral debug pod
kubectl run tmp --rm -it \
  --image=busybox \
  --restart=Never -- sh

# Inside the pod:
# nslookup my-svc
# wget -O- http://my-svc:80
Auto-deletes when you exit. Lifesaver for testing connectivity or DNS from inside the cluster.
expose pod as service
kubectl run nginx --image=nginx \
  --port=80 --expose
Creates the pod AND a matching ClusterIP service in one shot.
deploy

Deployments

5 snippets
create deployment
# Basic
kubectl create deployment web --image=nginx

# With replicas and port
kubectl create deployment web \
  --image=nginx --replicas=3 --port=80
scale
kubectl scale deployment web --replicas=5

# Conditional scale
kubectl scale deployment web \
  --current-replicas=3 --replicas=5
update image (rolling)
kubectl set image deployment/web \
  nginx=nginx:1.25

# Multi-container
kubectl set image deployment/web \
  nginx=nginx:1.25 sidecar=fluentd:1.16
Triggers a rolling update. Verify with rollout status before moving on.
rollout commands
kubectl rollout status   deployment/web
kubectl rollout history  deployment/web
kubectl rollout history  deployment/web --revision=2
kubectl rollout undo     deployment/web
kubectl rollout undo     deployment/web --to-revision=2
kubectl rollout restart  deployment/web
kubectl rollout pause    deployment/web
kubectl rollout resume   deployment/web
Always verify after deploying. The exam wants to see you check status, not just apply.
canary pattern
# Stable: 4 replicas with version=stable
kubectl create deployment web-stable \
  --image=nginx:1.24 --replicas=4

# Canary: 1 replica with version=canary
kubectl create deployment web-canary \
  --image=nginx:1.25 --replicas=1

# Service selects on shared label only (app=web)
# Add: kubectl label deployment web-* app=web
Two deployments + one service with a shared label selector. Adjust replica ratios to shift traffic.
svc

Services

3 snippets
expose existing workload
kubectl expose deployment web \
  --port=80 --target-port=8080

# NodePort
kubectl expose deployment web \
  --port=80 --type=NodePort

# LoadBalancer
kubectl expose deployment web \
  --port=80 --type=LoadBalancer
Reuses the pod/deployment's labels as the service selector. Almost always the right call.
create service directly
kubectl create service clusterip my-svc \
  --tcp=80:8080

kubectl create service nodeport my-svc \
  --tcp=80:8080 --node-port=30080

kubectl create service externalname my-svc \
  --external-name=example.com
Use this when there's no workload yet to expose, or when the selector needs to be custom.
test a service
kubectl run curl --rm -it \
  --image=curlimages/curl \
  --restart=Never -- \
  curl http://my-svc:80
Spin up a throwaway pod in the same namespace, then curl/wget the service name.
cm

ConfigMaps

3 snippets
from literal values
kubectl create configmap app-config \
  --from-literal=DB_HOST=db \
  --from-literal=DB_PORT=5432
from a file
# File name as key
kubectl create configmap app-config \
  --from-file=nginx.conf

# Custom key
kubectl create configmap app-config \
  --from-file=config=nginx.conf

# Multiple files
kubectl create configmap app-config \
  --from-file=./config-dir/
The filename becomes the key by default. Use key=path to rename.
from an env file
# app.env contains:
# DB_HOST=db
# DB_PORT=5432

kubectl create configmap app-config \
  --from-env-file=app.env
Each line becomes a key=value entry. Useful for porting .env files.
sec

Secrets

5 snippets
generic secret
kubectl create secret generic db-creds \
  --from-literal=username=admin \
  --from-literal=password='S!B\\*d$zDsb='
from file
kubectl create secret generic ssh-key \
  --from-file=ssh-privatekey=~/.ssh/id_rsa
docker registry
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password=pass \
  --docker-email=user@example.com
Used as imagePullSecrets on the pod spec to pull from a private registry.
tls secret
kubectl create secret tls my-tls \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key
decode a secret
kubectl get secret db-creds \
  -o jsonpath='{.data.password}' | base64 -d
Secrets are base64, not encrypted. -d decodes the value back to plaintext.
ns

Namespaces

2 snippets
create and switch
kubectl create namespace dev

# Set as default for current context
kubectl config set-context --current \
  --namespace=dev
list across namespaces
kubectl get pods -A
kubectl get all -n dev
kubectl get all --all-namespaces
sa

Service Accounts

2 snippets
create
kubectl create serviceaccount app-sa

# Attach to a pod (in spec)
# spec:
#   serviceAccountName: app-sa
issue a token
kubectl create token app-sa
kubectl create token app-sa --duration=24h
1.24+ doesn't auto-mount long-lived tokens. Use this to mint one when you need it.
rbac

RBAC

5 snippets
create a role
kubectl create role pod-reader \
  --verb=get,list,watch \
  --resource=pods

# Multiple resources and verbs
kubectl create role pod-rw \
  --verb=* \
  --resource=pods,deployments,services
cluster role
kubectl create clusterrole node-reader \
  --verb=get,list,watch --resource=nodes

# Non-resource URL
kubectl create clusterrole metrics-reader \
  --verb=get --non-resource-url=/metrics
Same as Role but cluster-scoped. Also supports non-resource URLs like /healthz.
role binding
# To a user
kubectl create rolebinding alice-reader \
  --role=pod-reader --user=alice

# To a service account
kubectl create rolebinding app-reader \
  --role=pod-reader \
  --serviceaccount=dev:app-sa
Bind a Role to a user, group, or service account in the same namespace.
cluster role binding
kubectl create clusterrolebinding admin-bind \
  --clusterrole=cluster-admin \
  --user=alice
test permissions
# As yourself
kubectl auth can-i create pods

# As another user
kubectl auth can-i list secrets \
  --as=alice -n dev

# As a service account
kubectl auth can-i get pods \
  --as=system:serviceaccount:dev:app-sa \
  -n dev
The fastest way to verify RBAC works. Use --as to impersonate a user or SA.
cj

Jobs & CronJobs

3 snippets
one-off job
kubectl create job hello \
  --image=busybox \
  -- /bin/sh -c "echo hello world"
cronjob
kubectl create cronjob backup \
  --image=busybox \
  --schedule="*/5 * * * *" \
  -- /bin/sh -c "echo backup"
trigger cronjob now
kubectl create job manual-run \
  --from=cronjob/backup
Manually kick off a one-time run from an existing cronjob. Handy for verification.
ing

Ingress

4 snippets
single rule
kubectl create ingress web \
  --rule="example.com/*=web-svc:80"
Format is host/path=service:port. The * matches any path under that host.
multiple rules
kubectl create ingress multi \
  --rule="foo.com/=svc1:80" \
  --rule="bar.com/=svc2:80"
path-based routing
kubectl create ingress api \
  --rule="example.com/api/*=api-svc:8080" \
  --rule="example.com/web/*=web-svc:80"
with TLS and class
kubectl create ingress secure \
  --class=nginx \
  --rule="example.com/*=svc:80,tls=tls-secret"
quota

Quotas & Limits

2 snippets
resource quota
kubectl create quota team-quota \
  --hard=cpu=4,memory=4Gi,pods=10
Caps total resource use across a namespace. Applies to all new objects.
limitrange — YAML only
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
spec:
  limits:
  - default:
      cpu: 200m
      memory: 256Mi
    defaultRequest:
      cpu: 100m
      memory: 128Mi
    type: Container
No imperative create flag exists. Generate a Pod manifest and adapt, or write from scratch.
-l

Labels & Selectors

3 snippets
label & annotate
kubectl label pod nginx env=prod
kubectl label pod nginx env=dev --overwrite
kubectl label pod nginx env-   # remove

kubectl annotate pod nginx \
  description="frontend tier"
selector queries
kubectl get pods -l env=prod
kubectl get pods -l 'env in (prod,stage)'
kubectl get pods -l env!=prod
kubectl get pods -l env=prod,tier=front
bulk operations by label
kubectl delete pods -l app=web
kubectl get all -l app=web -o wide
?

Debugging

5 snippets
describe & events
kubectl describe pod nginx
kubectl describe deployment web

kubectl get events --sort-by=.lastTimestamp
kubectl get events --field-selector type=Warning
describe is your first stop. The Events at the bottom usually tell you what's wrong.
logs
kubectl logs nginx
kubectl logs nginx -c container-name
kubectl logs nginx --previous     # last crashed container
kubectl logs nginx -f             # follow
kubectl logs -l app=nginx --tail=20
exec
kubectl exec nginx -- ls /
kubectl exec -it nginx -- sh
kubectl exec -it nginx -c sidecar -- bash
port forward
kubectl port-forward pod/nginx 8080:80
kubectl port-forward svc/web 8080:80
kubectl port-forward deploy/web 8080:80
Bypasses the service. Useful when you suspect the service selector is wrong.
top — metrics-server
kubectl top pod
kubectl top pod --containers
kubectl top pod -l app=web --sort-by=cpu
kubectl top node
-o

Output & Explain

4 snippets
explain — your offline docs
kubectl explain pod
kubectl explain pod.spec.containers
kubectl explain pod --recursive
kubectl explain deploy.spec.strategy
kubectl ships with the entire API schema. Use it instead of opening kubernetes.io tabs.
jsonpath
# All pod names
kubectl get pods \
  -o jsonpath='{.items[*].metadata.name}'

# Images across all pods
kubectl get pods \
  -o jsonpath='{.items[*].spec.containers[*].image}'
custom columns
kubectl get pods -o custom-columns=\
NAME:.metadata.name,\
STATUS:.status.phase,\
NODE:.spec.nodeName
sort
kubectl get pods \
  --sort-by=.metadata.creationTimestamp

kubectl get pods \
  --sort-by=.status.containerStatuses[0].restartCount
~

Edit & Patch

4 snippets
edit live
kubectl edit deployment web
kubectl edit svc/web

# Use nano instead
KUBE_EDITOR=nano kubectl edit deploy web
Opens vim with the live object. Not all fields are mutable — image, replicas, env yes; selector no.
patch (strategic merge)
kubectl patch deployment web \
  -p '{"spec":{"replicas":5}}'
patch (json)
kubectl patch deployment web --type=json \
  -p='[{"op":"replace","path":"/spec/replicas","value":5}]'
Use when you need to remove a field or modify a list element by index.
force replace
kubectl replace --force -f pod.yaml
Equivalent to delete + create. Use when a field can't be edited in place.
+/-

Apply & Delete

3 snippets
apply
kubectl apply -f pod.yaml
kubectl apply -f ./manifests/
kubectl apply -f https://example.com/manifest.yaml
delete
kubectl delete pod nginx
kubectl delete -f pod.yaml
kubectl delete pods -l app=web
kubectl delete all -l app=web
force delete (stuck pod)
kubectl delete pod nginx \
  --force --grace-period=0

# With the alias
k delete pod nginx $now
Use only when a pod is stuck Terminating. Skips graceful shutdown.
y

YAML-only Resources

2 snippets
no imperative form
# NetworkPolicy
# PersistentVolume / PersistentVolumeClaim
# LimitRange
# HorizontalPodAutoscaler (use 'kubectl autoscale' instead)
# Pod with readinessProbe / livenessProbe
# Pod with securityContext / capabilities
# Pod with volumes / volumeMounts
# Pod with initContainers / sidecars
These need raw YAML. Memorize the minimum required spec — they show up often.
shortcut — generate and edit
# Generate a pod skeleton
k run nginx --image=nginx $do > pod.yaml

# Add probes, security context, volumes in vim
vim pod.yaml
k apply -f pod.yaml
Start from a kubectl-generated manifest, then add the YAML-only fields manually.