Meet kubectl

The remote control for your cluster.

Beginner10 min · lesson 4 of 24
In plain terms
kubectl is the TV remote for your cluster: you press buttons (commands) from the couch and things happen across the room. You never get up and touch the TV itself.

A television remote lets you change the channel from the couch, without walking over and poking the TV. kubectl (most people say "kube-control," a few say "kube-cuttle") is that remote for a Kubernetes cluster. A cluster is just a group of computers, called nodes, that run your apps together. You never log into those nodes and poke at them by hand. You sit at your own laptop, type one command, and kubectl carries your request over to the cluster and shows you what came back. Nearly everything in this course happens through kubectl, so getting comfortable with a small handful of commands is the foundation for all of it.

Where your commands actually go

This is the part that trips people up early on. kubectl doesn't run anything on those computers itself. Every command is a request for something. Think of the cluster as a busy restaurant. You don't march into the kitchen and start cooking. You tell the person at the front counter what you want, and they pass it back to the cooks. In a cluster, that front counter is the API server. (API stands for application programming interface, which is a fancy name for a doorway one program uses to talk to another.) The API server is the single entrance to the control plane, the group of programs that make the cluster's decisions. kubectl only ever talks to that one doorway. You ask a question, and the answer comes back to your screen. That one rule explains a lot of how kubectl behaves.

kubectl is your remote control
1You type a kubectlcommandget, describe, logs, apply, or…2It reaches the APIserverthe one doorway into the…3The cluster actslists, creates, or changes…4Results return toyouprinted in your terminal
kubectl sends every request to the API server and shows you the result. It never touches the worker machines directly, which is why the same commands work no matter which cluster you point at.

The five commands you'll use every day

You can get surprisingly far with five verbs. get lists things ("show me the pods"). describe prints the full details of one thing, including a running list of recent events. logs shows what a container printed out while it ran. apply creates or updates things from a file. delete removes them. After the verb you name a resource type, like pods or nodes or deployments, and often a specific name after that. Learn those five and you can handle most of a normal day.

Start simple. Ask the cluster which machines it's built from:

terminal
kubectl get nodes
output
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 9d v1.31.0
kind-worker Ready <none> 9d v1.31.0
kind-worker2 Ready <none> 9d v1.31.0

Now list what's actually running. A Pod is the smallest thing Kubernetes runs. It's usually a single container wrapped up so the cluster can schedule it and keep an eye on it. By default kubectl shows you only one slice of the cluster, called a namespace. (Think of namespaces as separate apartments inside one building, each with its own stuff, all under one roof.) Add -o wide for extra columns, like which node a Pod landed on and its IP address. Add -A when you want to see across every namespace at once:

terminal
kubectl get pods -o wide
output
NAME READY STATUS RESTARTS AGE IP NODE
web-7c9fd8b6f5-2xk9q 1/1 Running 0 12m 10.244.1.7 kind-worker
web-7c9fd8b6f5-lp8vt 1/1 Running 0 12m 10.244.2.3 kind-worker2

You'll mistype one of these before long, and it helps to have already seen what that looks like so it doesn't throw you. Ask for a resource type that doesn't exist, and kubectl stops you with this:

terminal
kubectl get pdos
output
error: the server doesn't have a resource type "pdos"

Ask for a Pod that simply isn't there, and the message reads differently:

terminal
kubectl get pod web-old
output
Error from server (NotFound): pods "web-old" not found

Read the two apart. The first says the kind of thing you asked for, pdos, isn't a word Kubernetes knows, so it's nearly always a spelling slip (pods, not pdos). The second is the opposite: pods is a real kind, the cluster understood you and went looking, and there just isn't a Pod named web-old. One error means "I don't know that word"; the other means "I know the word, but that thing isn't here." That second line, Error from server, is the API server answering you back through the same front door from earlier. Telling those two apart is half of learning to read Kubernetes errors, and you'll meet both in your first hour.

When a Pod is genuinely stuck rather than just misspelled, describe is the command that tells you why. It prints a lot of detail. Scroll down to the Events section at the bottom, which reads in plain sentences:

terminal
kubectl describe pod web-7c9fd8b6f5-2xk9q
output
Name: web-7c9fd8b6f5-2xk9q
Namespace: default
Node: kind-worker/172.18.0.3
Status: Running
Containers:
web:
Image: nginx:1.27
State: Running
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12m default-scheduler Successfully assigned default/web-7c9fd8b6f5-2xk9q to kind-worker
Normal Pulled 12m kubelet Successfully pulled image "nginx:1.27"
Normal Started 12m kubelet Started container web

Those Events are gold. Each line names who did what. The scheduler (the part of the control plane that picks which node each Pod should run on) placed the Pod, and then the kubelet on that node pulled the image and started the container. (The kubelet is the small agent Kubernetes runs on every node to start containers and watch over them.) When a Pod is stuck, this is where the reason turns up, in words you can actually read, long before you need to open the logs.

Two ways to tell kubectl what you want

There are two styles here, and knowing the difference saves a lot of confusion later. The imperative style gives a direct order for right now: kubectl run, kubectl create, kubectl scale. It's like getting in a taxi and giving the driver turn-by-turn directions. Quick, and great for learning or for throwaway experiments. The declarative style works the other way around. You write down what you want in a file and hand it over with kubectl apply -f, and the cluster figures out how to make reality match your file. That's like giving the driver an address and letting them find the route. Real projects run this way, because a file can be saved, shared with teammates, and kept in version control (a system like Git that records every change you make to your files), so there's one agreed record of what the cluster should look like.

That file is written in YAML, a plain-text format for settings that's meant to be easy for people to read. A complete Kubernetes file always has four parts: apiVersion (which version of the Kubernetes API to speak), kind (what type of object this is), metadata (its name and any labels, which are simple tags you attach so you can find things later), and spec (what you actually want). Here's a whole, applyable file that describes one Pod:

web-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80

Apply it, and kubectl tells you plainly what changed:

terminal
kubectl apply -f web-pod.yaml
output
pod/web created

From here you'd run kubectl get pods again to watch it come up, and if it misbehaves, kubectl describe pod web to see why. Same five verbs, over and over. That loop, apply then check then fix, is most of what working with Kubernetes feels like day to day.

kubectl is a remote control aimed at the API server using a kubeconfig. Contexts pick cluster + user + namespace defaults. Most "wrong cluster" outages are a context mistake, not a mysterious bug. Make a habit of printing the current context before destructive commands.

Imperative commands (kubectl run, kubectl create) are fine for learning and break-glass. Declarative apply of YAML is what you want for anything that should survive the next engineer. Prefer apply for shared environments rather than a pile of one-off imperative edits nobody can replay.

On-call, the trio get / describe / logs covers most first-pass triage. Events on describe often explain a failure faster than staring at STATUS alone.

Try this

Confirm who you are talking to (context), list namespaces, and practice get + describe on a short-lived pod.

terminal
$ kubectl config current-context
kind-kind
$ kubectl get ns
NAME STATUS AGE
default Active 12d
kube-system Active 12d
kube-public Active 12d
$ kubectl run tip --image=busybox:1.36 --restart=Never --command -- sleep 120
pod/tip created
$ kubectl get pod tip
NAME READY STATUS RESTARTS AGE
tip 1/1 Running 0 5s
$ kubectl describe pod tip | Select-String -Pattern 'Status:|Node:|Events:' -Context 0,8
Status: Running
Node: kind-worker/172.18.0.3
Events:
Normal Scheduled ... Successfully assigned default/tip ...
$ kubectl delete pod tip
pod "tip" deleted

Takeaway

Always know your context, prefer declarative apply for lasting objects, and triage with get → describe → logs. kubectl is how you read the API the controllers already trust.

Quick check
01You run kubectl get pod web-old and get back: Error from server (NotFound): pods "web-old" not found. What is Kubernetes actually telling you?
Incorrect — That's the other error. A misspelled type gives "the server doesn't have a resource type" instead. Here pod is spelled fine and Kubernetes understood it.
Correct — The cluster understood the request, looked, and found nothing by that name. Check the name with kubectl get pods, or add -A to look across namespaces.
Incorrect — A dead cluster gives a connection error instead, like "The connection to the server was refused." Here the server clearly answered, it just had nothing to return.
Incorrect — A config problem shows up as an auth or connection failure, not a clean NotFound. Getting a NotFound back actually proves the connection is working.
02The lesson describes two styles: the imperative style (kubectl run, kubectl create) and the declarative style (kubectl apply -f a file). Why do real projects prefer the declarative style?
Incorrect — every kubectl command, apply included, goes through the API server; speed is not the reason.
Incorrect — the imperative kubectl run also creates Pods; the difference is how the intent is recorded, not whether it works.
Correct — a declarative file is like handing a driver an address instead of turn-by-turn directions; it can be shared with teammates and re-applied exactly.
Incorrect — that describes the imperative style; the declarative style works precisely by writing the state into a file.
03Two web Pods are Running, and you need to see, in a single command, which node each one landed on and the IP address it was given. Which command do you run?
Correct — -o wide adds extra columns to the listing, including the node each Pod landed on and its IP address.
Incorrect — the plain listing shows name, ready, status, restarts, and age, but not the node or the IP address.
Incorrect — logs prints what a container wrote out; it does not report which node a Pod is on or its IP address.
Incorrect — -A lists Pods across all namespaces but still does not add the node or IP columns; that is what -o wide is for.
Always know which cluster you're pointing at
kubectl talks to whatever cluster your current settings point at, and it's easy to fire a command at the wrong one, like deleting something in production when you thought you were on a test cluster. Before you run anything destructive, run kubectl config current-context to confirm where you're aimed. That one habit saves real pain the moment you're juggling more than one cluster.

Related