kubectl Cheatsheet
What it is
kubectl is the command-line client for talking to a Kubernetes cluster. Every cluster operation — inspecting pods, rolling out a new image, port-forwarding a service for debugging, rolling back a bad deploy — funnels through it. If you ship containerised apps on Kubernetes, kubectl is the daily driver.
Most kubectl cheatsheets read like an alphabetised dump of every flag. This one is organised in the order an engineer actually uses the commands across a deploy: cluster check → namespace → workloads → services → config → rollout → debug → cleanup. The same flow you'd expect from a real deploy pipeline.
Quick reference
Cluster and context
kubectl cluster-info # API server endpoints, sanity check
kubectl version --short # client + server versions
kubectl config get-contexts # all configured clusters
kubectl config current-context # which cluster you're pointed at now
kubectl config use-context prod-eu # switch clusters
kubectl config set-context --current --namespace=billing # default namespace for this context
kubectl get nodes # all worker nodes
kubectl get nodes -o wide # adds IPs, OS, kernel, runtime
kubectl describe node ip-10-0-1-23 # capacity, conditions, scheduled pods
kubectl top nodes # CPU / memory usage (needs metrics-server)
Always confirm current-context before any write command. The cost of running kubectl delete against the wrong cluster is a war story you don't want.
Namespaces
kubectl get namespaces # list namespaces
kubectl create namespace staging
kubectl get pods -n staging # one-off namespace override
kubectl get pods -A # every pod across every namespace
kubectl delete namespace staging # destructive — wipes everything in it
Pods
kubectl get pods # pods in current namespace
kubectl get pods -o wide # node + pod IP columns
kubectl get pods -l app=api # label selector
kubectl get pods --field-selector status.phase=Running
kubectl get pod api-7c9d-x4q2 -o yaml # full manifest with status
kubectl describe pod api-7c9d-x4q2 # events + container state, the debug starting point
kubectl logs api-7c9d-x4q2 # last container log
kubectl logs -f api-7c9d-x4q2 # follow (tail -f)
kubectl logs --previous api-7c9d-x4q2 # logs from a crashed container
kubectl logs -l app=api --tail=100 # aggregate logs across pods by label
kubectl exec -it api-7c9d-x4q2 -- sh # shell into the container
kubectl exec api-7c9d-x4q2 -- env # run one command, no TTY
kubectl port-forward pod/api-7c9d-x4q2 8080:3000 # local 8080 → pod 3000
kubectl cp api-7c9d-x4q2:/tmp/heap.dump ./heap.dump # copy file out of a pod
Deployments
kubectl create deployment api --image=registry.example.com/api:1.4.2
kubectl apply -f deploy/api.yaml # declarative — the canonical workflow
kubectl apply -f deploy/ # whole directory
kubectl get deployments
kubectl describe deployment api # rollout state, replica counts, conditions
kubectl scale deployment/api --replicas=5 # imperative scale
kubectl set image deployment/api api=registry.example.com/api:1.4.3 # ship a new image
kubectl edit deployment/api # opens manifest in $EDITOR (last resort)
kubectl delete deployment api
apply -f should be the default. set image and scale are convenient for one-offs but drift from your Git-tracked manifests — anything you'd want to reproduce belongs in the YAML and lives under version control.
Services and networking
kubectl get svc # services in current namespace
kubectl expose deployment api --port=80 --target-port=3000 --type=ClusterIP
kubectl describe svc api # endpoints + selector
kubectl get endpoints api # which pods the service actually points to
kubectl port-forward svc/api 8080:80 # forward through the service (load balanced)
kubectl proxy # local HTTP proxy to the API server on :8001
kubectl get ingress -A # all ingresses, useful for debugging routing
If kubectl get endpoints is empty for a service, the selector doesn't match any pod labels — that's the bug, not the ingress.
ConfigMaps and Secrets
kubectl create configmap api-config --from-literal=LOG_LEVEL=info --from-literal=FEATURE_X=true
kubectl create configmap api-config --from-file=config.yaml # whole file
kubectl create configmap api-config --from-env-file=.env # one key per env var
kubectl get configmap api-config -o yaml
kubectl create secret generic api-secrets \
--from-literal=DATABASE_URL='postgres://...' \
--from-literal=JWT_SECRET='...'
kubectl create secret docker-registry ghcr \
--docker-server=ghcr.io --docker-username=$USER --docker-password=$GITHUB_TOKEN
kubectl get secrets
kubectl get secret api-secrets -o jsonpath='{.data.DATABASE_URL}' | base64 -d # decode one key
Secrets are base64-encoded, not encrypted. Treat the YAML as sensitive — don't commit it. For real encryption-at-rest, enable etcd encryption providers or use Sealed Secrets / External Secrets Operator.
Rollouts and rollbacks
This is where kubectl earns its keep on production deploys.
kubectl rollout status deployment/api # blocks until rollout completes or fails
kubectl rollout history deployment/api # revision list
kubectl rollout history deployment/api --revision=4 # what changed in revision 4
kubectl rollout undo deployment/api # roll back to previous revision
kubectl rollout undo deployment/api --to-revision=3 # roll back to a specific revision
kubectl rollout restart deployment/api # recreate all pods (picks up new ConfigMap/Secret)
kubectl rollout pause deployment/api # pause mid-rollout (e.g. before canary cutover)
kubectl rollout resume deployment/api
rollout restart is the under-documented command that matters most in practice: ConfigMap and Secret changes do not automatically recreate the pods that mount them. Without a restart, your "deploy" updates the ConfigMap object but the running pods keep the old values until their next eviction.
Kubernetes' rolling-update strategy gives you zero downtime deployments by default — old pods only terminate after new pods pass readiness probes. If a rollout goes sideways, rollout undo gives you a fast manual rollback, and DeployHQ's one-click rollback wraps the same pattern across both your Kubernetes manifests and any non-Kubernetes hosts in the same release.
Debugging
kubectl get events --sort-by=.lastTimestamp # cluster-wide event timeline
kubectl get events -n billing --field-selector type=Warning
kubectl describe pod <name> # events scoped to one pod (the 80% case)
kubectl top pods # CPU + memory per pod (needs metrics-server)
kubectl top pods --containers # per-container breakdown
kubectl top pods --sort-by=memory
kubectl debug node/ip-10-0-1-23 -it --image=ubuntu # ephemeral debug pod with host access
kubectl debug api-7c9d-x4q2 -it --image=busybox --target=api # sidecar into a running pod
kubectl get pods --watch # live updates as state changes
kubectl wait --for=condition=Ready pod/api-7c9d-x4q2 --timeout=120s
When a pod is CrashLoopBackOff, the loop is describe → logs --previous → fix → apply -f. Reaching for kubectl edit should be a last resort because it drifts from your manifests.
Apply, diff, delete
kubectl apply -f deploy/ # declarative apply, idempotent
kubectl diff -f deploy/api.yaml # what would change vs current cluster state
kubectl apply --dry-run=server -f deploy/api.yaml # full server-side validation, no write
kubectl apply -k overlays/production # apply a Kustomize overlay
kubectl delete -f deploy/api.yaml # delete everything in the manifest
kubectl delete pod api-7c9d-x4q2 # let the Deployment recreate it
kubectl delete pod api-7c9d-x4q2 --grace-period=0 --force # nuclear, skip graceful shutdown
kubectl delete deployment api --cascade=orphan # delete deployment, keep pods
kubectl diff before kubectl apply is the single best habit on a shared cluster. It catches manifest drift, accidental field changes, and "is this even pointing at the right cluster?" before you write anything.
Kustomize
kubectl kustomize overlays/production # render to stdout, no apply
kubectl apply -k overlays/production # render and apply
kubectl delete -k overlays/production
Kustomize lives inside kubectl now — no separate binary needed. Use it before reaching for Helm if your manifests just need environment-specific patches.
Output filtering and jsonpath
kubectl get pods -o json | jq '.items[].metadata.name'
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName
kubectl get pods --no-headers -o custom-columns=:metadata.name # names only, scriptable
kubectl get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}' # current image
kubectl get pod api-7c9d-x4q2 -o jsonpath='{.status.containerStatuses[0].restartCount}'
jsonpath is faster and dependency-free for scripts; pipe through jq when the query gets complex. Both beat parsing kubectl describe output with regex.
Deployment workflows
The reference above is portable — every Kubernetes tutorial has those commands. The patterns below are the ones that matter once a manifest leaves your editor and has to survive a real production cluster.
Declarative deploys with kubectl diff as the gate
The reliable production pattern is diff → apply, every time:
# from the repo root, on a deploy branch
kubectl config use-context prod
kubectl config set-context --current --namespace=billing
kubectl diff -f deploy/ # review every change before applying
kubectl apply -f deploy/ # apply the whole directory
kubectl rollout status deployment/api --timeout=5m # block until healthy
If rollout status exits non-zero, your deploy hook should fail loudly — that's the trigger for an automated kubectl rollout undo. The pattern works equally well wrapped inside a CI/CD pipeline or a DeployHQ build pipeline running kubectl against your cluster.
Image tagging for safe rollback
Mirror the Docker rule: never deploy :latest to production. Always tag images with the immutable build SHA and reference that SHA in the manifest:
# deploy/api.yaml — SHA-pinned, not :latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/api:build-abc123def
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
With a SHA-pinned image, kubectl rollout undo is genuinely safe — Kubernetes brings back the previous ReplicaSet pointing at the previous SHA, no rebuild, no dependency drift. With :latest, "rollback" rolls forward to whatever :latest resolves to today.
Readiness vs liveness probes — get these right
Mix them up and rollouts misbehave in subtle ways:
- Readiness controls service traffic. Failing readiness removes the pod from the service endpoints — used to gate rolling updates.
- Liveness controls restarts. Failing liveness gets the container killed and restarted by the kubelet.
If your liveness probe fails during a slow boot, the kubelet will kill the pod before it ever becomes ready, and your rollout will loop forever. Use initialDelaySeconds plus a separate startupProbe for slow-starting apps instead of cranking liveness timeouts.
Single-cluster blue/green with two Services
For low-risk cutovers without a service mesh, run two Deployments side by side and flip the service selector:
# blue is live, green is the new version
kubectl apply -f deploy/api-green.yaml # new pods with label version=green
kubectl rollout status deployment/api-green
kubectl port-forward deployment/api-green 8080:3000 # smoke test
# flip traffic
kubectl patch svc api -p '{"spec":{"selector":{"app":"api","version":"green"}}}'
# rollback if it goes wrong
kubectl patch svc api -p '{"spec":{"selector":{"app":"api","version":"blue"}}}'
The whole cutover is one kubectl patch — under a second of traffic flip, instant rollback. Once green is stable for your soak period, delete the blue Deployment.
kubectl run for one-off jobs
Running migrations or data scripts in-cluster is safer than tunnelling out of the network:
kubectl run -it --rm migrate \
--image=registry.example.com/api:build-abc123def \
--restart=Never \
--env="DATABASE_URL=$DATABASE_URL" \
-- npm run migrate:up
kubectl run -it --rm dbshell \
--image=postgres:16-alpine \
--restart=Never \
-- psql "$DATABASE_URL"
--rm cleans up the pod automatically on exit. For repeatable jobs, use a real Job or CronJob manifest instead — kubectl run is for ad-hoc work.
Common errors and fixes
| Error | Cause | Fix |
|---|---|---|
Unable to connect to the server: dial tcp ...: i/o timeout |
Wrong context, expired credentials, or VPN not connected | kubectl config current-context then kubectl config view --minify. Re-auth (aws eks update-kubeconfig, gcloud container clusters get-credentials, etc.) before retrying. |
error: the server doesn't have a resource type "..." |
CRD not installed, or you're talking to an old cluster | `kubectl api-resources \ |
Pod stuck Pending |
No node has the requested CPU/memory, or PVC can't bind | kubectl describe pod <name> — the Events section names the reason. Lower requests, add nodes, or fix the StorageClass. |
Pod stuck ContainerCreating |
Image pull failure, missing Secret, or volume mount issue | kubectl describe pod <name> again. ErrImagePull means a registry credential problem — check the imagePullSecrets. |
CrashLoopBackOff |
App crashes shortly after start | kubectl logs --previous <pod> for the crash, then describe for exit codes. Don't keep restarting — fix the manifest and apply. |
OOMKilled (exit 137) |
Container hit its memory limit | Raise resources.limits.memory, or fix the leak. kubectl top pod shows steady-state usage. |
kubectl exec returns immediately with no error |
Container has no shell (distroless / scratch) | Use kubectl debug <pod> --image=busybox --target=<container> for an ephemeral debug sidecar instead. |
Service works but pods can't reach each other |
NetworkPolicy denies the traffic | kubectl get networkpolicy -A. Either widen the policy or test the path with kubectl debug from inside the namespace. |
kubectl apply says nothing changed but the cluster is wrong |
The field is owned by another controller (HPA, mutating webhook) | `kubectl get |
| Rollout stuck halfway | New pods failing readiness, so old pods never get cleaned up | kubectl rollout status shows it; kubectl describe deployment shows conditions. Fix readiness probe or roll back with kubectl rollout undo. |
error: metrics not available from kubectl top |
metrics-server is not installed | Install metrics-server via your cluster's add-on manager. Without it, no top, no HPA. |
Companion: where kubectl fits in a real deploy pipeline
kubectl is excellent for direct cluster interaction — debugging a pod, flipping a Service selector during a cutover, rolling back a bad release. For day-to-day deploys, you want all of that wrapped in a pipeline that runs the same commands the same way every time, from the same source of truth.
A typical containerised deploy looks like this:
- Push to your
mainbranch on GitHub or GitLab. - Your CI builds a SHA-tagged image and pushes to your registry.
- A DeployHQ build pipeline renders your Kustomize overlay with the new SHA, then runs
kubectl diff -fandkubectl apply -fagainst the production context. kubectl rollout statusgates the release — on failure, the same pipeline runskubectl rollout undoand reports a red deploy.- The exact same release is pinned by SHA, so one-click rollback brings back the previous revision instantly.
Kubernetes isn't the only target — DeployHQ also ships plain SCP/SFTP releases, Docker workloads, and serverless functions through the same pipeline. The principle is the same: Git is the source of truth, the pipeline applies it, the cluster (or server) reflects it. The deploy from GitHub to your server guide walks through the GitHub side end-to-end. For the broader pattern across both Kubernetes and non-Kubernetes targets, What Is GitOps? covers the principles.
Related cheatsheets
- Docker cheatsheet — the runtime under every pod; build the image before you deploy it.
- Bash scripting cheatsheet — for the deploy hooks that wrap
kubectl diff && kubectl apply && kubectl rollout statuswith proper error handling. - SSH cheatsheet — for bastion access to clusters that aren't exposed to the public internet.
- AWS CLI cheatsheet — for the
aws eks update-kubeconfig, ECR login, and CloudFormation steps that bracket every EKS deploy.
Ship Kubernetes workloads with DeployHQ
DeployHQ runs your kubectl pipeline from Git on every push — build, render, diff, apply, verify, rollback — with the same build pipelines and atomic releases used for non-Kubernetes targets. Start a free trial or read the pricing tiers.
Need help? Email support@deployhq.com or follow us on @deployhq on X.