kubectl Commands Cheatsheet
- kubectl describe shows events — always check the Events section when a pod is not starting.
- kubectl logs --previous retrieves logs from a crashed container — essential for crash debugging.
- kubectl port-forward lets you access a pod or service without a LoadBalancer — great for development.
- kubectl get: read state (GET requests).
- kubectl describe: read state + events (aggregated GET + events query).
- kubectl apply: write desired state (POST/PUT/PATCH requests).
- kubectl delete: remove state (DELETE requests).
- The --output flag controls formatting: -o yaml, -o json, -o wide, -o custom-columns.
- The -n flag scopes commands to a namespace. Without it, commands target 'default'.
- The --context flag switches between clusters (dev, staging, prod).
- kubectl describe is the single most valuable debugging command — the Events section shows scheduler decisions, image pull failures, and probe failures.
- kubectl logs --previous is the only way to see why a container crashed before it restarted.
- Running kubectl delete pod --force --grace-period=0 on a StatefulSet pod — this destroys the pod without letting it drain connections, potentially corrupting persistent storage.
Pod not starting — no events visible.
kubectl get pods -n kube-system | grep schedulerkubectl describe nodes | grep -A 5 'Allocated resources'Container keeps restarting (CrashLoopBackOff).
kubectl logs <pod> --previous --tail=50kubectl describe pod <pod> | grep -A 5 'Last State'Service unreachable — connection refused or timeout.
kubectl get endpoints <service-name>kubectl get pods -l app=<selector> -o wideNode marked NotReady — Pods being evicted.
kubectl describe node <node> | grep -A 10 'Conditions'kubectl get events --field-selector involvedObject.name=<node> --sort-by='.lastTimestamp'PersistentVolumeClaim stuck in Pending.
kubectl get pvkubectl describe pvc <pvc-name>ImagePullBackOff — container image cannot be pulled.
kubectl describe pod <pod> | grep -A 5 'Events'kubectl get secret <imagepullsecret-name> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -dProduction Incident
Production Debug GuideFrom symptom to root cause using only kubectl commands.
kubectl is not just a command-line tool — it is a Kubernetes API client. Every command you type translates into HTTP requests to the kube-apiserver. Understanding this relationship explains why certain commands are fast (cached reads), why others are slow (watch calls), and why some fail with 'connection refused' when the API server is unreachable.
The common misconception is that kubectl is a deployment tool. It is a state inspection and mutation tool. Deployment is one use case. The real value is observability — seeing what the cluster is doing right now, what it did in the past, and why a specific Pod is stuck. Engineers who master kubectl's inspection commands debug production incidents in minutes. Engineers who only know apply and delete debug by redeploying and hoping.
This cheatsheet is organized by intent: what are you trying to do? Find a resource? Debug a failure? Roll back a deployment? Each section includes the commands, the output you should expect, and the production gotchas that bite when you use them at scale.
Getting Information
The get and describe commands are your primary observability tools. They read cluster state from the API server without modifying anything — safe to run in production at any time.
kubectl get lists resources in a compact table format. It is fast because the API server caches the response. kubectl describe shows the same resources with full detail including events, conditions, and annotations — this is where the debugging signal lives.
The --output flag is more powerful than most engineers realize. -o jsonpath lets you extract specific fields programmatically. -o custom-columns builds custom dashboards in your terminal. -o name outputs only resource names, perfect for scripting.
# List resources kubectl get pods kubectl get pods -o wide # + node and IP kubectl get pods --all-namespaces # all namespaces kubectl get deployments kubectl get services kubectl get nodes kubectl get all # pods, services, deployments in current namespace # Get YAML/JSON of a resource kubectl get pod myapp-pod -o yaml kubectl get deployment myapp-deployment -o json # Detailed view with events — essential for debugging kubectl describe pod myapp-pod kubectl describe node my-node # Watch resources update in real time kubectl get pods -w # Custom columns output kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase # ── PRODUCTION-GRADE EXTRAS ────────────────────────────────────────────── # Extract a specific field with jsonpath kubectl get pod myapp-pod -o jsonpath='{.status.podIP}' kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}' # Get all pods with their resource requests kubectl get pods -o custom-columns=NAME:.metadata.name,CPU_REQ:.spec.containers[0].resources.requests.cpu,MEM_REQ:.spec.containers[0].resources.requests.memory # Find pods without resource limits (security/compliance check) kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].resources.limits == null) | .metadata.namespace + "/" + .metadata.name' # Get events sorted by time — critical for post-incident analysis kubectl get events --sort-by='.lastTimestamp' -A # Get events for a specific resource kubectl get events --field-selector involvedObject.name=myapp-pod # Check API server health kubectl get --raw /healthz kubectl get --raw /readyz kubectl get --raw /livez
myapp-pod-abc 1/1 Running 0 5m
10.244.1.45
myapp-pod-abc 10.244.1.45
myapp-pod-def 10.244.2.78
ok
- Events have a TTL (default: 1 hour). If you investigate late, the events are gone. Export them early.
- kubectl get events --sort-by='.lastTimestamp' shows the chronological story of what happened.
- kubectl describe pod shows events scoped to that Pod. kubectl get events -A shows cluster-wide events.
Debugging
The debugging commands — logs, exec, port-forward, cp — are how you interact with running (or crashed) containers. These commands go through the kubelet on the target node, not directly to the container runtime.
kubectl logs retrieves stdout/stderr from the container. The --previous flag is critical — it retrieves logs from the container instance that just crashed, which is the only way to see why a CrashLoopBackOff Pod is failing.
kubectl exec opens a shell inside the container. This is the Kubernetes equivalent of SSH. It requires the container to be running and have a shell binary (bash or sh). Distroless containers do not have a shell — use ephemeral debug containers instead.
kubectl port-forward creates a tunnel from your local machine to a Pod or Service inside the cluster. It bypasses Ingress, LoadBalancer, and NodePort — useful for accessing services that are not exposed externally.
# Container logs kubectl logs myapp-pod kubectl logs myapp-pod -c container-name # specific container kubectl logs myapp-pod --previous # logs from crashed container kubectl logs -f deployment/myapp # follow deployment logs kubectl logs myapp-pod --tail=100 # last 100 lines # Execute commands inside a container kubectl exec -it myapp-pod -- bash kubectl exec -it myapp-pod -- sh # if bash not available kubectl exec myapp-pod -- env # print env vars kubectl exec myapp-pod -- cat /config/app.properties # Port forward to access a service locally kubectl port-forward pod/myapp-pod 8080:8000 kubectl port-forward service/myapp-svc 8080:80 # Copy files to/from a pod kubectl cp myapp-pod:/var/log/app.log ./app.log kubectl cp ./config.yaml myapp-pod:/app/config.yaml # ── PRODUCTION-GRADE DEBUGGING ─────────────────────────────────────────── # Logs from all pods in a deployment (parallel) kubectl logs deployment/myapp --all-containers --prefix # Logs with timestamps — essential for correlating with external events kubectl logs myapp-pod --timestamps # Logs from a specific time window kubectl logs myapp-pod --since=1h kubectl logs myapp-pod --since-time=2026-04-07T10:00:00Z # Ephemeral debug container for distroless images (K8s 1.23+) kubectl debug -it myapp-pod --image=busybox --target=app-container # Debug a node by creating a privileged debug Pod kubectl debug node/my-node -it --image=ubuntu # Copy logs from a crashed pod before it is garbage collected kubectl logs <pod> --previous > crash.log 2>&1 # Stream logs from multiple pods with a label selector kubectl logs -l app=payment-service --all-containers --prefix --follow # Check what environment variables a running pod has kubectl exec myapp-pod -- printenv | sort # Test network connectivity from inside a pod kubectl exec myapp-pod -- curl -s http://other-service.default.svc.cluster.local:8080/health kubectl exec myapp-pod -- nslookup other-service.default.svc.cluster.local # Check mounted volumes and config kubectl exec myapp-pod -- df -h kubectl exec myapp-pod -- mount | grep config
# ephemeral debug container
Defaulting debug container name to debugger-xxxxx.
/ # ls /app
config.yaml app.jar lib/
- kubectl debug -it <pod> --image=busybox --target=<container> shares the process namespace.
- kubectl debug node/<node> -it --image=ubuntu creates a Pod on that node with host namespaces mounted.
- Always use --rm with debug containers to ensure they are cleaned up.
Managing Deployments
The apply, set image, scale, and rollout commands are how you modify cluster state. These are write operations — they change what is running. Use them with the same care as database writes.
kubectl apply is the declarative entry point. It reads a YAML file, compares it with the live state, and sends a PATCH request to the API server to reconcile the difference. This is idempotent — running apply twice with the same file produces no change.
kubectl rollout undo is the most important safety net. It reverts a Deployment to the previous ReplicaSet revision. This is not a delete-and-recreate — it is a controlled rollback that respects rolling update parameters (maxUnavailable, maxSurge).
kubectl delete is the imperative counterpart to apply. It removes resources. For Deployments, it deletes the Deployment and its ReplicaSets but does not delete the Pods — the Pods become orphaned until garbage collected. Always prefer apply with a modified YAML over delete-then-create.
# Apply config kubectl apply -f deployment.yaml kubectl apply -f ./k8s/ # apply all files in directory # Update image kubectl set image deployment/myapp api=myapp:2.0 # Scale kubectl scale deployment myapp --replicas=5 # Rollout management kubectl rollout status deployment/myapp kubectl rollout history deployment/myapp kubectl rollout undo deployment/myapp # rollback one step kubectl rollout undo deployment/myapp --to-revision=2 # rollback to specific revision # Delete kubectl delete pod myapp-pod kubectl delete deployment myapp kubectl delete -f deployment.yaml # delete what was applied # Force delete a stuck pod kubectl delete pod myapp-pod --force --grace-period=0 # ── PRODUCTION-GRADE DEPLOYMENT MANAGEMENT ─────────────────────────────── # Apply with server-side diff (dry-run) — see what would change kubectl apply -f deployment.yaml --server-dry-run --diff # Apply with field validation — reject invalid manifests kubectl apply -f deployment.yaml --validate=true --server-side # Restart all pods in a deployment (rolling restart) kubectl rollout restart deployment/myapp # Pause a rollout mid-way (canary testing) kubectl rollout pause deployment/myapp # ... verify canary pods ... kubectl rollout resume deployment/myapp # Check rollout revision details kubectl rollout history deployment/myapp --revision=3 # Scale with resource-aware scripting CURRENT=$(kubectl get deployment myapp -o jsonpath='{.spec.replicas}') DESIRED=$((CURRENT + 2)) kubectl scale deployment myapp --replicas=$DESIRED echo "Scaled from $CURRENT to $DESIRED replicas" # Delete with label selector (dangerous — always dry-run first) kubectl delete pods -l app=temp-worker --dry-run=client kubectl delete pods -l app=temp-worker # Apply with pruning — delete resources not in the applied set kubectl apply -f ./k8s/ --prune -l app=myapp # Export current state before making changes (safety myapp-backup-$(date +%Y%m%d-%H%M%S).yaml
Waiting for deployment "myapp" rollout to finish: 3 out of 5 new replicas have been updated...
deployment.apps/myapp-deployment successfully rolled out
- apply with --server-side (K8s 1.22+) avoids conflicts from multiple actors updating the same resource.
- apply with --prune deletes resources that are no longer in your manifest directory — powerful but dangerous.
- Always run --dry-run=client or --server-dry-run before applying to production.
| Command | HTTP Verb | Use Case | Production Risk |
|---|---|---|---|
| kubectl get | GET | Read current state of resources. Fast, cached, safe. | None — read-only operation. |
| kubectl describe | GET + Events query | Detailed view with events, conditions, annotations. Primary debugging tool. | None — read-only. Events have 1-hour TTL; investigate promptly. |
| kubectl apply | PATCH (strategic merge) | Declarative state management. Idempotent. GitOps standard. | Medium — selector changes can orphan Pods. Always dry-run first. |
| kubectl create | POST | Create a new resource. Fails if it already exists. | Low — fails safely if resource exists. Not idempotent. |
| kubectl replace | PUT | Full resource replacement. Overwrites everything. | High — replaces entire resource. Missing fields are removed. Use with caution. |
| kubectl delete | DELETE | Remove a resource. Triggers graceful termination. | Medium — orphaned Pods if Deployment is deleted. --force on StatefulSets corrupts storage. |
| kubectl logs | GET (via kubelet proxy) | Retrieve container stdout/stderr. | None — read-only. Logs are ephemeral; aggregate externally. |
| kubectl exec | POST (SPDY/WebSocket) | Run commands inside a container. | Medium — can modify running state. Audit exec usage in production. |
| kubectl port-forward | POST (SPDY tunnel) | Tunnel local port to Pod/Service. | Low — bypasses network policies. Do not use as permanent access method. |
| kubectl rollout undo | PATCH (roll back ReplicaSet) | Revert Deployment to previous revision. | Low — safest rollback method. Respects rolling update parameters. |
🎯 Key Takeaways
- kubectl describe shows events — always check the Events section when a pod is not starting.
- kubectl logs --previous retrieves logs from a crashed container — essential for crash debugging.
- kubectl port-forward lets you access a pod or service without a LoadBalancer — great for development.
- kubectl rollout undo is a one-command rollback — no redeployment needed.
- Use -n NAMESPACE for all commands if not in the default namespace.
- Always dry-run before applying to production: kubectl apply -f file.yaml --dry-run=client.
- kubectl debug creates ephemeral containers for debugging distroless images — the modern replacement for exec.
- kubectl get with -o jsonpath or jq is the foundation of production automation and monitoring scripts.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QHow do you debug a pod that is in CrashLoopBackOff? Walk me through the exact kubectl commands you would run, in order.
- QHow do you rollback a failed deployment in Kubernetes? What is the difference between rollout undo and deleting the new ReplicaSet?
- QHow do you access a service running in Kubernetes from your local machine without exposing it externally?
- QWhat is the difference between kubectl apply, kubectl replace, and kubectl create? When would you use each?
- QA developer ran kubectl delete pod kafka-2 --force --grace-period=0 on a StatefulSet. The replacement Pod is stuck in Pending. How do you diagnose and fix this?
- QHow would you find all Pods in a cluster that do not have resource limits set? Write the kubectl command.
- QWhat does kubectl rollout pause do, and how would you use it for canary deployments?
- QHow do you export the current state of a Deployment before making changes, as a safety net?
Frequently Asked Questions
How do I debug a pod that is in CrashLoopBackOff?
Check the crash reason: kubectl describe pod POD_NAME — look at the 'Last State' section and 'Events'. Check logs from the crashed container: kubectl logs POD_NAME --previous. Common causes: application crash on startup (check logs for stack trace), readiness probe failing before app starts (check probe config), missing environment variables or ConfigMap, and OOMKilled (memory limit too low).
How do I access a ConfigMap or Secret in a running pod?
kubectl exec -it POD_NAME -- env shows all environment variables including those from ConfigMaps and Secrets. kubectl exec -it POD_NAME -- cat /etc/config/KEY shows a file-mounted ConfigMap. kubectl get configmap NAME -o yaml shows the ConfigMap contents.
What is the difference between kubectl delete pod and kubectl delete pod --force?
kubectl delete pod sends a graceful termination signal. The kubelet gives the container time to shut down (default 30 seconds), unmounts volumes, and cleans up. kubectl delete pod --force --grace-period=0 skips all of this — the Pod is removed from the API server immediately, but the container may still be running on the node. Use --force only when the Pod is stuck in Terminating and the kubelet is unreachable. Never use it on StatefulSet Pods.
How do I see what kubectl is actually sending to the API server?
Add -v=6 (or higher) to any kubectl command to see the HTTP request and response. -v=6 shows request URLs and response codes. -v=8 shows full request and response bodies. -v=9 shows curl commands you could use to replicate the request. Example: kubectl get pods -v=6 shows the GET request to /api/v1/namespaces/default/pods.
How do I switch between multiple Kubernetes clusters?
kubectl config get-contexts shows all available contexts (clusters). kubectl config use-context <context-name> switches to a different cluster. kubectl config current-context shows which cluster you are targeting. For safety, always run kubectl config current-context before applying changes to verify you are on the right cluster. Use kubectx (a popular third-party tool) for faster context switching.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.