Skip to content

Kubectl Cheat Sheet

A practical command reference for day-1 and day-2 Kubernetes operations.

Context and namespace safety

kubectl config get-contexts
kubectl config current-context
kubectl config use-context <context>
kubectl config set-context --current --namespace=<namespace>

Check your active target before write operations.

Scaffolding: generate YAML, don't type it

Almost every object has an imperative shortcut. Add --dry-run=client -o yaml to print the manifest instead of creating the object, then edit and apply it. Under exam time pressure this is the single highest-value habit.

export do='--dry-run=client -o yaml'    # then append $do to any command below

# Workloads
kubectl run web --image=nginx $do > pod.yaml
kubectl run web --image=nginx --port=8080 --env=KEY=value --labels=app=web $do
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- sh   # throwaway debug pod
kubectl create deployment web --image=nginx --replicas=3 $do
kubectl create job j --image=busybox $do -- /bin/sh -c 'echo hi'
kubectl create job manual --from=cronjob/nightly                      # trigger a CronJob now
kubectl create cronjob c --image=busybox --schedule='*/5 * * * *' $do -- date

# Config
kubectl create configmap cm --from-literal=K=V --from-file=./f --from-env-file=./e $do
kubectl create secret generic s --from-literal=password=x $do
kubectl create secret tls t --cert=c.crt --key=c.key
kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io --docker-username=<user> --docker-password=<token>
kubectl create namespace ns
kubectl create quota q --hard=pods=10,requests.cpu=2,limits.memory=4Gi -n ns

# Networking
kubectl expose deployment web --port=80 --target-port=8080 --type=NodePort $do
kubectl create service clusterip svc --tcp=80:8080 $do
kubectl create ingress ing --rule='host.example.com/path*=svc:80' --class=nginx $do

# Identity and RBAC
kubectl create serviceaccount sa -n <ns>
kubectl create role r --verb=get,list,watch --resource=pods -n <ns>
kubectl create clusterrole cr --verb=get --resource=secrets
kubectl create rolebinding rb --role=r --serviceaccount=<ns>:sa -n <ns>
kubectl create clusterrolebinding crb --clusterrole=cr --user=jane

Two flags worth memorising: --dry-run=server validates against the live API (catches admission and deprecation errors that client misses), and kubectl diff -f app.yaml shows exactly what an apply would change.

Core read commands

kubectl get pods -A -o wide
kubectl get deploy,sts,ds -A
kubectl get svc -A
kubectl get events -A --sort-by=.metadata.creationTimestamp
kubectl top nodes
kubectl top pods -A
kubectl get pods --show-labels                          # labels as a column
kubectl get pods -l 'env in (prod,staging)'             # set-based label selector
kubectl get pods --sort-by=.status.startTime
kubectl get pods --field-selector=status.phase=Running
kubectl api-resources --verbs=list -o name              # what can I even list here?

Debugging commands

kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --all-containers
kubectl logs <pod> -n <ns> --all-containers --previous
kubectl logs <pod> -n <ns> -c <container> -f          # follow live
kubectl exec -it <pod> -n <ns> -- sh
kubectl debug -it <pod> -n <ns> --image=busybox:1.36 --target=<container>
kubectl port-forward pod/<pod> 8080:8080 -n <ns>       # local tunnel to pod
kubectl port-forward svc/<svc> 8080:80 -n <ns>         # local tunnel to service
kubectl cp <pod>:/path/to/file ./local-file -n <ns>    # copy file from pod

Deployment operations

kubectl apply -f app.yaml
kubectl apply -f manifests/ -R                  # recurse into subdirectories
kubectl diff -f app.yaml                        # what would change, before it changes
kubectl rollout status deploy/<name> -n <ns>
kubectl rollout history deploy/<name> -n <ns>
kubectl rollout history deploy/<name> --revision=3 -n <ns>
kubectl rollout undo deploy/<name> -n <ns>
kubectl rollout undo deploy/<name> --to-revision=2 -n <ns>
kubectl rollout restart deploy/<name> -n <ns>   # roll all pods with no spec change
kubectl rollout pause deploy/<name> -n <ns>     # batch several edits into one rollout
kubectl rollout resume deploy/<name> -n <ns>
kubectl scale deploy/<name> --replicas=<count> -n <ns>
kubectl scale deploy/<name> --replicas=3 --current-replicas=2 -n <ns>   # no-op if it already moved
kubectl edit deploy/<name> -n <ns>              # opens in $KUBE_EDITOR
kubectl set image deploy/<name> <container>=<image>:<tag> -n <ns>
kubectl set resources deploy/<name> -c=<container> \
  --requests=cpu=100m,memory=128Mi --limits=cpu=500m,memory=512Mi -n <ns>
kubectl set env deploy/<name> KEY=value -n <ns>
kubectl set serviceaccount deploy/<name> <sa> -n <ns>
kubectl autoscale deploy/<name> --min=2 --max=10 --cpu-percent=60 -n <ns>

Record why a rollout happened so rollout history is useful later:

kubectl annotate deploy/<name> kubernetes.io/change-cause="bump to v2.0.0" -n <ns>

Service and network checks

kubectl get svc -n <ns>
kubectl get endpointslices -n <ns>
kubectl exec -it <pod> -n <ns> -- nslookup <service>
kubectl exec -it <pod> -n <ns> -- wget -qO- http://<service>:<port>

RBAC and access checks

# Check
kubectl auth can-i get pods -n <ns>
kubectl auth can-i create deployments --as=<identity> -n <ns>
kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa> -n <ns>   # enumerate everything a subject can do
kubectl auth whoami
kubectl get role,rolebinding -n <ns>
kubectl get clusterrole,clusterrolebinding

# Create
kubectl create serviceaccount <sa> -n <ns>
kubectl create role <r> --verb=get,list,watch --resource=pods -n <ns>
kubectl create role <r> --verb=get --resource=pods --resource-name=<specific-pod> -n <ns>
kubectl create clusterrole <cr> --verb=get --resource=secrets
kubectl create rolebinding <rb> --role=<r> --serviceaccount=<ns>:<sa> -n <ns>
kubectl create rolebinding <rb> --clusterrole=view --serviceaccount=<ns>:<sa> -n <ns>
kubectl create clusterrolebinding <crb> --clusterrole=<cr> --user=<user>
kubectl create token <sa> -n <ns> --duration=1h                          # short-lived SA token

--list is the fastest way to answer "what can this service account actually do?", and a RoleBinding pointing at a ClusterRole grants that ClusterRole's rules only inside the binding's namespace.

JSONPath and output formatting

kubectl get pods -A -o jsonpath='{.items[*].spec.nodeName}'
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.podIP}'
kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}'

# Loop over items, one line each
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'

# Custom columns are usually easier to read than raw JSONPath
kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,IP:.status.podIP
kubectl get pv -o custom-columns=NAME:.metadata.name,CLAIM:.spec.claimRef.name,SIZE:.spec.capacity.storage

# Filter with a JSONPath expression
kubectl get nodes -o jsonpath='{.items[?(@.spec.unschedulable)].metadata.name}'

Node maintenance

kubectl get nodes -o wide
kubectl describe node <node>                    # Conditions, Taints, Allocated resources
kubectl cordon <node>                           # mark unschedulable, leave running pods alone
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --force   # also evict bare pods
kubectl uncordon <node>
kubectl taint nodes <node> key=value:NoSchedule
kubectl taint nodes <node> key=value:NoSchedule-        # trailing dash removes it
kubectl label node <node> disktype=ssd
kubectl top node <node>

--ignore-daemonsets and --delete-emptydir-data between them fix almost every drain failure. --force is only for pods no controller owns, and those pods are gone for good.

Patching resources

kubectl patch deployment web -n <ns> -p '{"spec":{"replicas":5}}'
kubectl set image deployment/web web=ghcr.io/example/web:v2.0.0 -n <ns>
kubectl label pod <pod> -n <ns> app=debug --overwrite
kubectl annotate deployment web -n <ns> kubernetes.io/change-cause="v2 rollout"

Cleanup and maintenance

kubectl delete pod --field-selector=status.phase=Failed -A
kubectl delete pod <pod> -n <ns> --force --grace-period=0   # force-delete stuck pod
kubectl api-resources
kubectl api-resources --namespaced=false                     # cluster-scoped only
kubectl explain deployment.spec.strategy
kubectl cluster-info

Suggested local aliases

Wire up completion before the aliases, and re-bind it to k - otherwise k gets no tab-completion, which is where most of the speed comes from.

# bash
source <(kubectl completion bash)
alias k='kubectl'
complete -F __start_kubectl k

# zsh
source <(kubectl completion zsh)
alias k='kubectl'
compdef __start_kubectl k

alias kg='kubectl get'
alias kga='kubectl get all -A'
alias kd='kubectl describe'
alias kl='kubectl logs'
alias kaf='kubectl apply -f'
alias kns='kubectl config set-context --current --namespace'
export do='--dry-run=client -o yaml'
export now='--force --grace-period=0'    # kubectl delete pod x $now

Make it survive a new shell: echo "source <(kubectl completion bash)" >> ~/.bashrc.

Certification notes

  • All three exams are time-limited and open-book against the official docs only. Speed comes from imperative generation, not typing YAML: kubectl create <kind> ... --dry-run=client -o yaml > file.yaml, then edit.
  • Set the namespace once per task instead of typing -n every time: kubectl config set-context --current --namespace=<ns>. Forgetting -n is the most common way to lose points on otherwise correct answers.
  • kubectl explain <resource>.<field> --recursive is available in the exam and is faster than searching the docs site.
  • Learn --force --grace-period=0 for stuck pods, -o jsonpath for extracting one field into an answer file, and kubectl get events --sort-by=.metadata.creationTimestamp for diagnosis questions.
  • Enable completion and set alias k=kubectl in the first minute of the exam; it pays for itself.