kubectl is the command-line face of the Kubernetes API. It is not the whole system — controllers, schedulers, and kubelets do the real work — but it is where most beginners spend their first hundred hours. The tool can feel overwhelming because it exposes almost everything. The good news is that a small set of verbs and flags covers most daily learning and debugging.
This post is a practical starter kit: what to run first, what the output means at a high level, and a calm order when something breaks. No attempt to list every subcommand.
How kubectl talks to the cluster
When you run kubectl, the client loads configuration from kubeconfig (usually ~/.kube/config). That file defines clusters (API server addresses), users (credentials), and contexts (a cluster + user + optional default namespace).
If you are looking at the wrong cluster, every correct command still gives useless answers.
kubectl config current-context
kubectl config get-contexts
kubectl cluster-info
current-context is the one line worth checking when results make no sense. get-contexts marks the active context with *. cluster-info confirms the API is reachable — not that your app is healthy, but that you are talking to something real.
Switch context when needed:
kubectl config use-context kind-my-cluster
Treat context switches like changing airports. The procedures look similar; the planes are not the same.
Namespaces: the scope you forgot
Kubernetes partitions objects into namespaces. Many tutorials use default. Real teams use staging, production, app names, or platform namespaces.
Without -n, kubectl uses the default namespace from the current context, or default if none is set.
kubectl get namespaces
kubectl get pods -n shop-staging
kubectl config set-context --current --namespace=shop-staging
Setting the namespace on the context saves typing during a focused session. While debugging an incident, I still prefer explicit -n on important commands so copy-paste from a runbook does not hit the wrong place.
get: the cockpit view
kubectl get lists resources in a compact table. It answers: what exists, and what is the headline status?
kubectl get pods -n shop-staging
kubectl get deploy,svc -n shop-staging
kubectl get all -n shop-staging
all is a convenience bundle (Pods, Services, Deployments, ReplicaSets, etc.). It is not literally every resource type — CronJobs, Ingresses, and many others need explicit types.
Useful output flags:
kubectl get pods -n shop-staging -o wide
kubectl get pods -n shop-staging --show-labels
kubectl get pods -n shop-staging -w
-o wideadds the node and Pod IP — helpful when Pods land on one bad node or when comparing with Service endpoints.--show-labelsprints labels used by selectors — essential when a Service has no endpoints.-wwatches for changes — useful during rollouts.
Other output formats appear later (-o yaml, -o json). For beginners, tables plus describe carry most of the load.
describe: the story behind the headline
kubectl describe fetches details and Events — short messages from Kubernetes components about what happened.
When get shows CrashLoopBackOff, Pending, or ImagePullBackOff, describe is the next step.
kubectl describe pod web-7d4f8c9b6-xk2jp -n shop-staging
kubectl describe deployment web -n shop-staging
kubectl describe svc api -n shop-staging
On a Pod, scroll to:
- Conditions — scheduled? ready?
- Containers — state, restart count, last termination reason.
- Events at the bottom — often the clearest clue (failed pull, quota exceeded, probe failed).
On a Deployment, check replica counts, conditions, and Events about rollouts.
On a Service, check the selector and whether endpoints exist. A running app with no endpoints is often a label mismatch.
Common beginner mistake: reading only the STATUS column from get. That column is a summary. describe is the article.
logs: application output, not Kubernetes opinion
Once the container has started at least once, application logs help.
kubectl logs deployment/web -n shop-staging
kubectl logs pod web-7d4f8c9b6-xk2jp -n shop-staging -c web
kubectl logs deployment/web -n shop-staging --previous
deployment/webpicks a Pod from the Deployment (convenient when replica names change).-cnames the container when the Pod has more than one.--previousshows the last crashed instance — valuable inCrashLoopBackOff.
For a live tail:
kubectl logs -f deployment/web -n shop-staging
Logs tell you what the process said. They do not replace describe for scheduling or image pull failures — those often never reach application logs.
apply and delete: changing desired state
kubectl apply -f creates or updates objects from YAML or directories. This is the usual GitOps and manifest workflow entry point.
kubectl apply -f k8s/web/ -n shop-staging
kubectl apply -f deployment.yaml -n shop-staging
kubectl delete -f deployment.yaml -n shop-staging
kubectl delete pod web-7d4f8c9b6-xk2jp -n shop-staging
Apply sends desired state to the API. Controllers reconcile. Delete removes objects; Pods owned by a ReplicaSet are often recreated unless you delete the controller or scale to zero.
Deleting a Pod to “restart” it sometimes works, but it is a blunt habit. Prefer rollouts:
kubectl rollout restart deployment/web -n shop-staging
kubectl rollout status deployment/web -n shop-staging
kubectl rollout history deployment/web -n shop-staging
rollout status blocks until success or timeout — good after apply in scripts or when learning.
explain: documentation at the terminal
Guessing YAML field names is slow. kubectl explain walks the API schema.
kubectl explain pod
kubectl explain pod.spec
kubectl explain pod.spec.containers
kubectl explain deployment.spec.strategy
kubectl explain service.spec.ports
Add --recursive for a deeper tree in one shot:
kubectl explain deployment.spec.template.spec --recursive
This keeps you aligned with what the API accepts. Kubernetes versions add or graduate fields; explain reflects the cluster you are connected to.
dry-run: practice without consequences
Client-side dry-run builds the object locally without sending it to the server (older style, still seen in docs).
Server-side dry-run asks the API to validate admission without persisting — closer to “will this be accepted?”
kubectl apply -f deployment.yaml --dry-run=server -n shop-staging
kubectl create deployment demo --image=nginx --dry-run=server -o yaml
Generating YAML from imperative commands is a legitimate learning path:
kubectl create deployment web --image=ghcr.io/example/web:1.0.0 \
--dry-run=client -o yaml > deployment.yaml
Then edit the file, add labels, probes, and resources, and switch to apply for the real change.
Labels, selectors, and field selectors
Labels connect Deployments to Pods and Services to Pods.
kubectl get pods -n shop-staging -l app=web
kubectl get pods -n shop-staging --field-selector status.phase=Pending
If a Service targets app=web but Pods have app=web-app, get endpoints shows empty. Labels are wiring, not decoration.
A calm debugging order
When something is wrong, speed often comes from order, not from more commands at random.
1. Confirm context and namespace.
kubectl config current-context
kubectl get ns
kubectl get deploy,pod,svc -n shop-staging
2. Identify the controller. Deployment? StatefulSet? Job?
3. Read the workload headline.
kubectl get deploy web -n shop-staging
kubectl rollout status deployment/web -n shop-staging
4. Inspect Pods.
kubectl get pods -n shop-staging -l app=web -o wide
kubectl describe pod <name> -n shop-staging
5. Check networking only after Pods are Ready.
kubectl get svc api -n shop-staging
kubectl get endpoints api -n shop-staging
6. Read logs last among kubectl basics — after you know the container started.
kubectl logs deployment/web -n shop-staging --tail=100
Skipping straight to logs when the Pod is ImagePullBackOff wastes time. Skipping describe when status is Pending misses Events about quotas or scheduling.
Impersonation and auth errors (briefly)
If commands fail with forbidden or unauthorized messages, the problem is credentials or RBAC — not the Deployment manifest.
kubectl auth can-i get pods -n shop-staging
kubectl auth can-i create deployments -n shop-staging
That does not fix permissions, but it clarifies whether the blocker is policy instead of application code.
Small habits that compound
- Prefer explicit namespaces in runbooks and incidents.
- Use
-o widewhen node placement matters. - Use
--show-labelswhen Services and Deployments disagree. - Use
explaininstead of copying three-year-old YAML from a forum. - Use
--dry-run=serverbefore applying unfamiliar manifests to production. - Prefer
rollout restartover deleting Pods when you own a Deployment.
What to learn next (without rushing)
Later commands worth their place: kubectl port-forward for local access, kubectl exec for shells (when justified), kubectl top for resource usage (metrics server required), kubectl api-resources to see what your cluster supports, and kubectl get events sorted by time for namespace-wide timelines.
None of those replace the core loop: get → describe → logs, with context and namespace confirmed first.
Final thought
kubectl is not Kubernetes — it is a client. Still, fluency in a handful of verbs makes the API feel approachable. The beginners who progress steadily are usually not the ones who memorised every flag. They are the ones who check where they are connected, read Events before restarting things, and treat labels as part of the debugging story. Build that order early; the rest of the surface area becomes easier to add on purpose rather than by accident.