Common kubectl commands every developer should know
There are hundreds of kubectl subcommands and you will use about fifteen. This is that fifteen,
with the flags that turn each one from "prints something" into "answers the question."
The mental model
kubectl is a REST client. Every command is an HTTP request to the API server, and you can see
exactly which one:
kubectl get pods -v=6 # show the request URLs
kubectl get pods -v=8 # show full request and response bodies
That is genuinely useful when a command behaves unexpectedly — you can see whether the problem is your query or the cluster's answer.
get — what exists
kubectl get pods
kubectl get pods -A # every namespace
kubectl get pods -o wide # + node, pod IP, nominated node
kubectl get pod web-abc -o yaml # the full object as stored
kubectl get deploy,svc,ing -l app=web # several kinds at once
The flags worth memorising
# Watch changes as they happen
kubectl get pods -w
# Filter server-side by field (cheaper than grep, and exact)
kubectl get pods --field-selector=status.phase=Running
kubectl get events --field-selector type=Warning
# Sort by anything in the object
kubectl get pods --sort-by=.status.containerStatuses[0].restartCount
kubectl get events --sort-by=.lastTimestamp
# Pull out exactly the columns you want
kubectl get pods -o custom-columns=\
'NAME:.metadata.name,NODE:.spec.nodeName,IMAGE:.spec.containers[*].image'
# JSONPath for scripting
kubectl get pods -o jsonpath='{.items[*].spec.nodeName}'
kubectl get svc web -o jsonpath='{.spec.clusterIP}'
kubectl api-resources is the command that makes the rest discoverable — it lists every kind the
cluster knows about, along with its short name and whether it is namespaced.
kubectl api-resources | head -20
kubectl explain deployment.spec.strategy.rollingUpdate # inline schema docs
describe — why it is in that state
kubectl describe pod web-abc
kubectl describe node worker-1
kubectl describe svc web
describe is not just a prettier get. It joins the object with related state — most importantly
its Events, which appear at the bottom and are where the actual explanation lives:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 2m kubelet Error: ImagePullBackOff
Warning Unhealthy 1m kubelet Readiness probe failed: connection refused
Always read the bottom of describe first. It answers "why" more often than the logs do.
# Two high-value describe habits
kubectl describe pod web-abc | sed -n '/Events:/,$p' # events only
kubectl describe node worker-1 | sed -n '/Allocated/,$p' # what is claimed on a node
logs — what the application said
kubectl logs web-abc
kubectl logs web-abc -f # follow
kubectl logs web-abc --tail=100
kubectl logs web-abc --since=15m
kubectl logs web-abc -c sidecar # a specific container in the Pod
The two flags people miss
# The container that CRASHED, not the one that just started
kubectl logs web-abc --previous
# Every Pod behind a label, with the pod name prefixed on each line
kubectl logs -l app=web --all-containers --prefix --tail=50
--previous is the single most important debugging flag in kubectl. On a CrashLoopBackOff Pod,
plain kubectl logs shows a container that has been alive for two seconds and has nothing to say.
# Timestamps are not on by default
kubectl logs web-abc --timestamps --since-time='2026-05-18T10:00:00Z'
exec — get inside
kubectl exec web-abc -- env | sort
kubectl exec -it web-abc -- sh
kubectl exec -it web-abc -c sidecar -- bash
The -- matters: everything after it is passed to the container rather than parsed by kubectl.
Without it, kubectl exec web-abc ls -la tries to interpret -la as a kubectl flag.
# Useful one-liners from inside a Pod
kubectl exec web-abc -- cat /etc/resolv.conf
kubectl exec web-abc -- nslookup db.default.svc.cluster.local
kubectl exec web-abc -- wget -qO- --timeout=2 localhost:8080/healthz
When the image has no shell — distroless, scratch — exec cannot help. Use an ephemeral debug
container instead, which joins the running Pod's namespaces:
kubectl debug -it web-abc --image=nicolaka/netshoot --target=app
port-forward — reach it from your laptop
kubectl port-forward pod/web-abc 8080:8080
kubectl port-forward svc/web 8080:80 # picks one backing Pod
kubectl port-forward deploy/web 8080:8080
# Bind on all interfaces (careful on shared networks)
kubectl port-forward --address 0.0.0.0 svc/web 8080:80
This is a tunnel through the API server, so it works even when the Service is ClusterIP-only and
the cluster is behind a bastion. It is the fastest way to reach a database or an admin UI you would
never expose.
Note that forwarding to a Service still selects one Pod and stays with it — it is not load balanced. For testing balancing behaviour, use a NodePort or an Ingress.
apply — declare the desired state
kubectl apply -f deployment.yaml
kubectl apply -f k8s/ # a whole directory
kubectl apply -k overlays/prod # kustomize
kubectl apply -f https://example.com/manifest.yaml
Before you apply
# Validate against the server's schema without changing anything
kubectl apply -f deployment.yaml --dry-run=server
# See exactly what would change on the live object
kubectl diff -f deployment.yaml
kubectl diff is underused and excellent — it is git diff for the cluster, and it catches the
"someone edited this by hand in production" case immediately.
apply vs create vs replace. create fails if the object exists. replace overwrites the whole
object, dropping fields you did not send. apply performs a three-way merge between your manifest,
the live object, and the last-applied configuration — which is why it is the only one safe to run
repeatedly.
Rollouts
kubectl rollout status deployment/web --timeout=120s
kubectl rollout history deployment/web
kubectl rollout history deployment/web --revision=3
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=2
kubectl rollout restart deployment/web # recreate every Pod, no spec change
rollout restart is the clean way to force a restart — it patches an annotation on the Pod
template, which triggers a normal rolling update. Deleting Pods by hand skips the surge and
availability guarantees.
Quick reference
The commands worth committing to memory
| Command | What it does |
|---|---|
kubectl get pods -o wide | Pods with node and IP columns |
kubectl get all -n <ns> | Common resource kinds in a namespace |
kubectl describe pod <pod> | Full state plus the events that explain it |
kubectl explain <kind>.<field> | Schema documentation, offline |
kubectl api-resources | Every kind the cluster knows, with short names |
kubectl logs <pod> --previous | Logs from the container that crashed |
kubectl logs -l app=web --prefix -f | Follow logs from every matching Pod |
kubectl exec -it <pod> -- sh | Interactive shell in a container |
kubectl debug -it <pod> --image=nicolaka/netshoot --target=<c> | Ephemeral debug container sharing the Pod namespaces |
kubectl get events --sort-by=.lastTimestamp | Cluster events in chronological order |
kubectl top pod --containers | Live CPU and memory per container |
kubectl apply -f <file> | Create or update via three-way merge |
kubectl diff -f <file> | Show what applying would change |
kubectl scale deploy/<name> --replicas=5 | Change replica count immediately |
kubectl rollout restart deploy/<name> | Roll every Pod without a spec change |
kubectl rollout undo deploy/<name> | Revert to the previous ReplicaSet |
kubectl port-forward svc/<name> 8080:80 | Tunnel a Service port to localhost |
kubectl cp <pod>:/path ./local | Copy files out of a container |
kubectl auth can-i <verb> <resource> | Check your own RBAC permissions |
kubectl config get-contexts | List configured clusters |
kubectl config use-context <name> | Switch cluster |
kubectl config set-context --current --namespace=<ns> | Set the default namespace |
Make it faster
# Completion and a short alias that keeps completion working
source <(kubectl completion bash)
alias k=kubectl
complete -o default -F __start_kubectl k
# Stop typing -n every time
alias kn='kubectl config set-context --current --namespace'
Two tools that pay for themselves in a week:
- kubectx / kubens — switch cluster and namespace in one word.
- k9s — a terminal UI over the cluster. Logs, exec, describe, and delete without typing a
command; it replaces most of the
get/describeloop.
The habits that matter
- Read the Events at the bottom of
describebefore anything else. - Use
--previouson any Pod that has restarted. - Run
kubectl diffbeforekubectl applyon a cluster you care about. - Use
--dry-run=serverto validate manifests in CI. - Set the namespace in your context instead of typing
-ntwo hundred times a day.
Further reading
- The kubectl cheat sheet — the full searchable reference on this site.
- Debugging pod failures — these commands applied to a specific problem.
- The official kubectl reference.