kubectl is the tool you will type most as a Kubernetes user, and knowing its shortcuts is the difference between a five-second fix and a five-minute one during an incident. This cheat sheet is organised by what you are trying to do, not alphabetically, and every command has been checked against Kubernetes 1.33. Bookmark it; the CKA and CKAD exams reward exactly this kind of fluency.
Prerequisites: a cluster and a working kubectl (see our Kubernetes learning path). Install the client with your package manager or from dl.k8s.io; keep it within one minor version of the cluster.
1. Setup and productivity
# Shell completion + alias (bash; zsh: replace bash with zsh)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl; complete -o default -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc
kubectl version # client and server versions
kubectl api-resources # every kind with short names (po, svc, deploy, ing…)
kubectl api-resources --namespaced=false
kubectl explain pod.spec.containers.resources # built-in docs for any field
kubectl explain deployment --recursive | less # whole schema2. Contexts and namespaces
kubectl config get-contexts # * marks the current one
kubectl config current-context
kubectl config use-context prod-eks
kubectl config set-context --current --namespace=payments # default namespace for this context
kubectl config view --minify # only the current context, secrets redacted
kubectl config delete-context old-lab
# Merge several kubeconfig files (e.g. after aws eks update-kubeconfig)
export KUBECONFIG=~/.kube/config:~/.kube/eks-prod
kubectl config view --flatten > ~/.kube/merged && mv ~/.kube/merged ~/.kube/config
# Faster switching: install kubectx/kubens (krew plugins ctx and ns)3. Looking at things: get, describe, logs
kubectl get pods # current namespace
kubectl get pods -A # all namespaces
kubectl get pods -o wide # node, IP, readiness gates
kubectl get pods -w # watch changes (Ctrl-C to stop)
kubectl get pods -l app=web,tier!=cache # label selectors
kubectl get pods --field-selector status.phase!=Running
kubectl get pods --sort-by=.metadata.creationTimestamp
kubectl get pods --sort-by=.status.containerStatuses[0].restartCount
kubectl get deploy,svc,ing -n shop # several kinds at once
kubectl get all -n shop # common kinds (not literally all)
kubectl get events --sort-by=.lastTimestamp -n shop
kubectl get events --field-selector type=Warning -A
kubectl describe pod web-7d9f8-abcde # events at the bottom = why it is not running
kubectl describe node w1 # allocatable resources, taints, conditions
kubectl logs web-7d9f8-abcde
kubectl logs web-7d9f8-abcde -c sidecar # specific container
kubectl logs -f deploy/web --all-containers --tail=100
kubectl logs web-7d9f8-abcde --previous # crashed container's last run
kubectl logs -l app=web --prefix --since=10m # all pods of a label4. Output formats: JSONPath, custom columns, YAML
kubectl get pod web-7d9f8-abcde -o yaml
kubectl get pod web-7d9f8-abcde -o json | jq '.status.podIP'
# JSONPath
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"t"}{.spec.nodeName}{"n"}{end}'
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}'
kubectl get secret db -o jsonpath='{.data.password}' | base64 -d
# Custom columns (easier to read than JSONPath)
kubectl get pods -o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,IMAGE:.spec.containers[0].image,RESTARTS:.status.containerStatuses[0].restartCount'
# Images used across the cluster
kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"n"}{end}{end}' | sort | uniq -c | sort -rn5. Creating objects fast (imperative + dry-run)
Generate YAML instead of writing it from memory: --dry-run=client -o yaml prints a manifest without touching the cluster. This is the single most valuable exam trick.
kubectl run web --image=nginx:1.27 --port=80 --dry-run=client -o yaml > pod.yaml
kubectl create deployment web --image=nginx:1.27 --replicas=3 --dry-run=client -o yaml > deploy.yaml
kubectl expose deployment web --port=80 --target-port=8080 --type=ClusterIP --dry-run=client -o yaml > svc.yaml
kubectl create service nodeport web --tcp=80:8080 --node-port=30080
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-file=config.yaml
kubectl create secret generic db --from-literal=password='S3cure!'
kubectl create secret docker-registry regcred --docker-server=ghcr.io --docker-username=me --docker-password="$TOKEN"
kubectl create job once --image=busybox:1.36 -- sh -c 'echo hello'
kubectl create cronjob nightly --image=busybox:1.36 --schedule='0 2 * * *' -- sh -c 'date'
kubectl create ingress web --rule="shop.example.com/*=web:80" --class=nginx
kubectl create namespace staging
kubectl create serviceaccount deployer -n staging
kubectl create role pod-reader --verb=get,list,watch --resource=pods
kubectl create rolebinding read-pods --role=pod-reader --serviceaccount=default:deployer
kubectl create quota team-a --hard=pods=20,requests.cpu=4,requests.memory=8Gi
# Temporary pod for testing, deleted on exit
kubectl run -it --rm debug --image=busybox:1.36 --restart=Never -- sh
kubectl run -it --rm curl --image=curlimages/curl --restart=Never -- curl -s http://web.shop.svc.cluster.local6. Applying, diffing, editing, deleting
kubectl apply -f deploy.yaml
kubectl apply -f manifests/ -R # directory, recursive
kubectl apply -k overlays/prod # Kustomize
kubectl apply -f https://example.com/manifest.yaml
kubectl apply -f deploy.yaml --server-side --field-manager=ci # server-side apply (preferred in CI)
kubectl diff -f deploy.yaml # what would change (exit code 1 = differences)
kubectl apply -f deploy.yaml --dry-run=server # validate against admission webhooks
kubectl edit deployment web # opens $EDITOR
kubectl set image deployment/web nginx=nginx:1.28
kubectl set env deployment/web LOG_LEVEL=debug
kubectl set resources deployment/web -c nginx --limits=memory=256Mi --requests=cpu=100m
kubectl patch deployment web -p '{"spec":{"replicas":5}}'
kubectl patch deployment web --type=json -p='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"nginx:1.28"}]'
kubectl label pod web-7d9f8-abcde env=prod --overwrite
kubectl annotate deployment web kubernetes.io/change-cause="bump to 1.28"
kubectl delete -f deploy.yaml
kubectl delete pod web-7d9f8-abcde --grace-period=0 --force # last resort
kubectl delete pods -l app=web --wait=false
kubectl delete pods --field-selector status.phase=Succeeded -A # clean completed pods
kubectl delete namespace staging # deletes everything in it7. Rollouts and scaling
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout history deployment/web --revision=3
kubectl rollout undo deployment/web # previous revision
kubectl rollout undo deployment/web --to-revision=2
kubectl rollout restart deployment/web # recreate pods (e.g. after a ConfigMap change)
kubectl rollout pause deployment/web && kubectl rollout resume deployment/web
kubectl scale deployment/web --replicas=5
kubectl scale statefulset/db --replicas=3
kubectl autoscale deployment/web --min=2 --max=10 --cpu-percent=70
kubectl get hpa8. Getting inside: exec, port-forward, cp, debug
kubectl exec -it web-7d9f8-abcde -- sh
kubectl exec web-7d9f8-abcde -c sidecar -- env
kubectl exec deploy/web -- cat /etc/nginx/nginx.conf
kubectl port-forward svc/web 8080:80 # localhost:8080 → service
kubectl port-forward pod/db-0 5432:5432 --address 0.0.0.0
kubectl proxy --port=8001 # API server on localhost:8001
kubectl cp web-7d9f8-abcde:/var/log/nginx/access.log ./access.log
kubectl cp ./config.yaml web-7d9f8-abcde:/tmp/config.yaml -c nginx
# Ephemeral debug container (distroless images have no shell)
kubectl debug -it web-7d9f8-abcde --image=busybox:1.36 --target=nginx
# Copy of the pod with a different image/command
kubectl debug web-7d9f8-abcde -it --copy-to=web-debug --container=nginx -- sh
# Shell on a node (host filesystem under /host)
kubectl debug node/w1 -it --image=ubuntu:24.049. Nodes and maintenance
kubectl get nodes -o wide
kubectl top nodes # needs Metrics Server
kubectl top pods -A --sort-by=memory
kubectl describe node w1 | grep -A5 "Allocated resources"
kubectl cordon w1 # no new pods
kubectl drain w1 --ignore-daemonsets --delete-emptydir-data --grace-period=60
# ... patch / reboot the node ...
kubectl uncordon w1
kubectl taint nodes w2 dedicated=gpu:NoSchedule
kubectl taint nodes w2 dedicated- # remove taint
kubectl label nodes w2 disktype=ssd10. Security and RBAC checks
kubectl auth whoami # who am I (1.28+)
kubectl auth can-i create deployments -n shop
kubectl auth can-i '*' '*' # am I cluster-admin?
kubectl auth can-i list secrets --as=system:serviceaccount:shop:deployer -n shop
kubectl auth can-i --list -n shop # everything I can do here
kubectl get rolebindings,clusterrolebindings -A -o wide | grep deployer
kubectl create token deployer -n shop --duration=1h # short-lived SA token
kubectl get pods -A -o jsonpath='{range .items[?(@.spec.hostNetwork==true)]}{.metadata.namespace}/{.metadata.name}{"n"}{end}'11. Troubleshooting flow
| Symptom | First command | Usual cause |
|---|---|---|
Pending | kubectl describe pod → Events | Insufficient CPU/memory, unsatisfied nodeSelector/affinity, taint without toleration, PVC not bound |
ImagePullBackOff | kubectl describe pod | Typo in image tag, private registry without imagePullSecrets, rate limit |
CrashLoopBackOff | kubectl logs --previous | App exits at start: bad config, missing env, failing liveness probe |
CreateContainerConfigError | kubectl describe pod | Referenced ConfigMap/Secret key does not exist |
| Running but unreachable | kubectl get endpoints svc | Service selector does not match pod labels; readiness probe failing; wrong targetPort |
Node NotReady | kubectl describe node, journalctl -u kubelet on the node | kubelet down, CNI broken, disk/memory pressure |
Terminating forever | kubectl get pod -o yaml | grep finalizers | Stuck finalizer; remove it with kubectl patch … -p '{"metadata":{"finalizers":null}}' |
12. Plugins worth installing (krew)
kubectl krew install ctx ns tree neat view-secret resource-capacity who-can
kubectl ctx # switch context
kubectl ns shop # switch namespace
kubectl tree deployment web # owner hierarchy: Deployment → ReplicaSet → Pods
kubectl get deploy web -o yaml | kubectl neat # strip managed fields and status
kubectl view-secret db password
kubectl resource-capacity --pods --util
kubectl who-can delete pods -n shop13. One-liners to keep
# Pods not Running/Succeeded, cluster-wide
kubectl get pods -A --field-selector 'status.phase!=Running,status.phase!=Succeeded'
# Restart count leaderboard
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount | tail -10
# Delete all Evicted pods
kubectl get pods -A -o json | jq -r '.items[] | select(.status.reason=="Evicted") | "(.metadata.namespace) (.metadata.name)"'
| xargs -n2 sh -c 'kubectl delete pod -n $0 $1'
# Decode every key of a secret
kubectl get secret db -o json | jq -r '.data | map_values(@base64d)'
# Which pods run on a node
kubectl get pods -A -o wide --field-selector spec.nodeName=w1
# Wait for a condition in scripts
kubectl wait --for=condition=available deployment/web --timeout=120s
kubectl wait --for=delete pod/web-7d9f8-abcde --timeout=60sKey takeaways
describefor why,logs --previousfor what,get eventsfor when.--dry-run=client -o yamlwrites your manifests;kubectl explaindocuments them.- Use
apply --server-sideanddiffin automation;edit/patchonly for emergencies. kubectl debugreplaces the missing shell in distroless images and gives you node access.- Alias
k, completion and krew plugins pay for themselves within a day.
Related: Pods, Deployments, RBAC, Monitoring and troubleshooting. Official reference: kubectl Quick Reference.


