Common kubectl Commands

Lesson 3 of 39 14 min read Updated

In the previous lessons you connected kubectl to a cluster and used it to look at nodes. Now you will learn the everyday commands that work on every kind of Kubernetes object, from Pods to Deployments to Services.

Think of kubectl as a Swiss Army knife. A handful of blades (get, describe, apply, delete, logs and exec) cover most of your work, and each one behaves the same way whatever the object type.

Learn them once and every later lesson gets easier, because you will already know how to create, inspect, debug and clean up whatever you build.

What you will learn

  1. How kubectl commands are built and how they map to the Kubernetes API
  2. How to work with namespaces, and save a default namespace in a context
  3. How to view, create, update, label and delete objects
  4. How to debug workloads with logs, exec, port-forward, events and top
  5. How to take a node out of service safely with cordon, drain and uncordon

How kubectl commands work

Everything is an API object

Everything in Kubernetes is a RESTful resource, also called a Kubernetes object. Each object lives at its own HTTP path on the API server.

For example, a Pod named my-pod in the default namespace lives at /api/v1/namespaces/default/pods/my-pod. When you run a kubectl command, kubectl makes HTTP requests to paths like this for you.

Raise the log level with -v=6 to watch it happen:

kubectl get pods my-pod -v=6

Among the log lines you will see the GET request kubectl sent to /api/v1/namespaces/default/pods/my-pod and the 200 OK it got back, followed by the normal table.

The shape of a command

Most kubectl commands follow the same pattern:

kubectl <verb> <type> [<name>] [flags]
PartExamples
verbget, describe, apply, delete, logs, exec
typepods (or pod, po), deployments (deploy), services (svc), nodes (no)
namemy-pod. Leave it out to act on every object of that type
flags-n dev, -o yaml, --watch

Run kubectl api-resources to see every type your cluster knows, with its short name.

Namespaces

Namespaces are like folders inside your cluster. They group objects into logical sets, for example one per team or one per environment (dev, staging, production).

kubectl works in the default namespace unless you say otherwise. Target another namespace with --namespace, or its short form -n:

kubectl get pods --namespace=mystuff

# Same thing, shorter
kubectl get pods -n mystuff

To see objects in every namespace at once, use --all-namespaces, or its short form -A:

kubectl get pods --all-namespaces
kubectl get pods -A

Namespaces are objects too, so you create and list them with the same verbs:

kubectl create namespace mystuff
kubectl get namespaces

Contexts: save your default namespace

Typing -n mystuff on every command gets tedious. A context remembers the cluster, the user and the default namespace for you. Contexts live in your kubeconfig file, usually $HOME/.kube/config.

The kubeconfig lesson covers contexts in depth. Here you only need two moves. The quickest is to change the default namespace of the context you are using right now:

kubectl config set-context --current --namespace=mystuff

Or create a separate context for that namespace and switch to it. A new context needs a cluster and a user too, so copy their names from kubectl config get-contexts:

kubectl config set-context my-context --cluster=minikube --user=minikube --namespace=mystuff
kubectl config use-context my-context

From now on every command targets mystuff by default, with no -n needed.

A context needs a cluster and a user. kubectl config set-context my-context --namespace=mystuff on its own creates a context with only a namespace. Switch to it and kubectl no longer knows which cluster to call, so you get errors like The connection to the server localhost:8080 was refused. Use --current, or pass --cluster and --user.

Viewing objects

List and get

kubectl get lists objects. Give it a type to list all of them in the current namespace, or a type and a name for one object:

# Every Pod in the current namespace
kubectl get pods

# One Pod by name
kubectl get pods my-pod

# Several types at once
kubectl get pods,services

Choose the output format

The -o flag changes what get prints. The default is a short table.

FlagWhat you get
-o wideThe table plus extra columns, such as the Pod IP and node
-o yamlThe full object definition in YAML
-o jsonThe full object definition in JSON
-o jsonpath='{...}'Only the fields you pick
--no-headersThe table without its header row, handy when you pipe the output into other commands
kubectl get pods -o wide
kubectl get pods my-pod -o yaml
kubectl get pods my-pod -o json
kubectl get pods --no-headers

JSONPath pulls out a single field. This prints only the Pod's IP address:

kubectl get pods my-pod -o jsonpath='{.status.podIP}'

Expected output (your IP will differ):

10.244.1.7

Describe, explain and watch

kubectl describe prints a detailed, human-readable view of one object, including its recent events. It is usually the first command to run when something looks wrong:

kubectl describe pods my-pod

kubectl explain documents the fields a resource type supports, straight from your cluster's API. Add a field path to drill down:

kubectl explain pods
kubectl explain pods.spec.containers

--watch (or -w) keeps the command running and prints a new line every time the object changes. Press Ctrl+C to stop:

kubectl get pods my-pod --watch

Creating, updating and deleting objects

Declarative and imperative

Kubernetes encourages a declarative approach: you describe the desired state in a YAML file, and Kubernetes works out how to get there.

kubectl apply is the main tool for this. It creates objects that do not exist yet and updates the ones that do, so you can run it again and again with the same file.

kubectl apply -f obj.yaml

Imperative commands such as kubectl create only create. They fail if the object already exists.

Keep your YAML as the source of truth. Store manifests in Git and change them with kubectl apply. The files then always describe what is running, and anyone can recreate the setup from them.

Preview before you change

Add --dry-run=client to see what apply would do without sending any change to the cluster:

kubectl apply -f obj.yaml --dry-run=client

--dry-run=server goes one step further: the API server fully validates the request but still saves nothing. kubectl diff shows exactly which fields would change on the live object:

kubectl apply -f obj.yaml --dry-run=server
kubectl diff -f obj.yaml

Edit a live object

kubectl edit opens the live object in your default editor (set by KUBE_EDITOR or EDITOR). Saving and closing the file applies the change:

kubectl edit deployment my-deployment

Changes made this way are not in your YAML files, so the next apply of a file can undo them. Use edit for quick experiments, not as your normal workflow.

View the last applied configuration

Every kubectl apply saves the configuration you sent in an annotation on the object (kubectl.kubernetes.io/last-applied-configuration). Read it back with apply view-last-applied:

# Using the same file you applied
kubectl apply view-last-applied -f obj.yaml

# Or by type and name
kubectl apply view-last-applied deployment/my-deployment

Delete objects

Delete an object with the same file that created it, or by type and name:

# Everything defined in the file
kubectl delete -f obj.yaml

# One object by type and name
kubectl delete pods my-pod
kubectl delete does not ask for confirmation. The object is gone as soon as you press Enter, and deleting a namespace deletes everything inside it. Check your context with kubectl config current-context first, and add --interactive (-i) if you want kubectl to ask before it deletes.

Labels and annotations

Labels are key-value pairs that identify objects so you can select them, for example env=demo or app=web.

Annotations are key-value pairs too, but they hold non-identifying metadata such as build information or a contact address. You can add, change and remove both from the command line.

LabelsAnnotations
PurposeIdentify and group objectsAttach extra information
Used to select objectsYes, with -lNo
Examplecolor=reddescription="my test pod"
# Add a label
kubectl label pods bar color=red

# Change an existing label (kubectl refuses without --overwrite)
kubectl label pods bar color=blue --overwrite

# Remove a label: note the trailing minus
kubectl label pods bar color-

# Add an annotation
kubectl annotate pods bar description="my test pod"

Labels pay off when you filter by them with -l (short for --selector). --show-labels adds a LABELS column to the table:

kubectl get pods -l color=blue
kubectl get pods --show-labels

Debugging commands

Read logs

# Logs of a Pod
kubectl logs my-pod

# Logs of one container in a multi-container Pod
kubectl logs my-pod -c my-container

# Stream new lines as they arrive, like tail -f
kubectl logs my-pod -f

When a container keeps crashing, add --previous to read the logs of the run before the last restart.

Run commands inside a container

kubectl exec runs a command in a running container. With -it you get an interactive shell. Everything after -- is the command to run:

kubectl exec -it my-pod -- bash

Small images often have no bash. Use sh instead: kubectl exec -it my-pod -- sh.

kubectl attach connects your terminal to the container's main process instead of starting a new one:

kubectl attach -it my-pod

Copy files

# From a container to your machine
kubectl cp my-pod:/path/to/remote/file /path/to/local/file

# From your machine to a container
kubectl cp /path/to/local/file my-pod:/path/to/remote/file

kubectl cp needs the tar binary inside the container, so it does not work with every minimal image.

Forward a port

kubectl port-forward opens a tunnel from a port on your machine to a Pod or a Service, so you can test it without exposing it publicly. Here local port 8080 goes to port 80:

# To a Pod
kubectl port-forward my-pod 8080:80

# To a Service
kubectl port-forward services/my-service 8080:80

Watch cluster events

# Recent events in the current namespace
kubectl get events

# Stream events from every namespace as they happen
kubectl get events --watch -A

Events are not listed in time order by default. Add --sort-by=.metadata.creationTimestamp to read them oldest first. The cluster keeps events for about one hour.

Check resource usage

# CPU and memory per node
kubectl top nodes

# CPU and memory per Pod in the current namespace
kubectl top pods

# CPU and memory per Pod in every namespace
kubectl top pods --all-namespaces
kubectl top needs metrics-server. Without it you get error: Metrics API not available. On minikube, run minikube addons enable metrics-server; many managed clusters install it for you.

Cluster management: cordon, drain and uncordon

Sometimes you need to take a node out of service for maintenance, repairs or an upgrade. Three commands handle the whole cycle:

CommandWhat it does
kubectl cordonMarks the node unschedulable. No new Pods land on it, but existing Pods keep running.
kubectl drainCordons the node, then evicts its Pods so their controllers recreate them on other healthy nodes. Use it before maintenance or before removing a machine.
kubectl uncordonMakes the node schedulable again once the maintenance is done.
# Stop new Pods from being scheduled on the node
kubectl cordon node-name

# Evict the Pods (DaemonSet Pods stay; emptyDir data is deleted)
kubectl drain node-name --ignore-daemonsets --delete-emptydir-data

# Allow scheduling again after maintenance
kubectl uncordon node-name

Without --ignore-daemonsets, drain stops with an error on almost every node, because DaemonSet Pods such as kube-proxy belong on every node and cannot be moved. --delete-emptydir-data confirms you accept losing data in emptyDir volumes.

Drain also refuses to evict bare Pods that no controller manages, because nothing would recreate them. --force evicts them anyway, and they are gone for good.

Working faster

Command autocompletion

Tab completion fills in commands, flags and even object names from your cluster. For bash, install the bash-completion package first:

# macOS
brew install bash-completion

# CentOS / Red Hat
yum install bash-completion

# Debian / Ubuntu
apt-get install bash-completion

Activate kubectl completion for your current terminal session:

source <(kubectl completion bash)

Make it permanent by adding it to your shell profile:

echo "source <(kubectl completion bash)" >> ${HOME}/.bashrc

macOS and many Linux setups use zsh by default. zsh needs no extra package:

source <(kubectl completion zsh)
echo "source <(kubectl completion zsh)" >> ${HOME}/.zshrc

If zsh reports command not found: compdef, add autoload -Uz compinit && compinit near the top of ~/.zshrc.

Getting help

# General help
kubectl help

# Help for one command, with examples
kubectl help get

# Same thing
kubectl get --help

Hands-on: a complete workflow

Now put it all together: create a namespace and a context, run a Pod, label it, inspect it, debug it and clean up.

Step 1 — Create a namespace and a context

Create a demo namespace and a context that defaults to it. Replace minikube with your own cluster and user names from kubectl config get-contexts (for kind they are kind-kind):

kubectl create namespace demo
kubectl config set-context demo-ctx --namespace=demo --cluster=minikube --user=minikube
kubectl config use-context demo-ctx

Step 2 — Write a Pod manifest

Save this as nginx-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80

The image is pinned to nginx:1.27 rather than latest, so the Pod behaves the same every time you run it.

Step 3 — Create the Pod

kubectl apply -f nginx-pod.yaml

Expected output:

pod/nginx created

Step 4 — Verify the Pod is running

kubectl get pods

Expected output:

NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 10s

If STATUS shows ContainerCreating, the image is still downloading. Add --watch and wait for Running.

Step 5 — Add a label and filter by it

kubectl label pods nginx env=demo
kubectl get pods -l env=demo --show-labels

Expected output:

NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 1m env=demo

Step 6 — Inspect the Pod

kubectl describe pods nginx

Check the Events at the bottom. You should see the Pod being scheduled, the image being pulled and the container being started.

Step 7 — Read the logs

kubectl logs nginx

nginx prints its startup messages here, and later one line per request it serves.

Step 8 — Open a shell inside the container

kubectl exec -it nginx -- bash

Look around, for example with ls /usr/share/nginx/html, then type exit to leave the shell.

Step 9 — Forward a port and test it

kubectl port-forward nginx 8080:80

Open http://localhost:8080 in your browser. You should see the nginx welcome page. Press Ctrl+C to stop forwarding, then run kubectl logs nginx again to see your request in the log.

You just used the whole toolkit. apply, get, label, describe, logs, exec and port-forward work the same way for every object you will meet in later lessons.

Step 10 — Clean up

kubectl delete -f nginx-pod.yaml

# Switch back to your original context, then remove the demo context and namespace
kubectl config use-context minikube
kubectl config delete-context demo-ctx
kubectl delete namespace demo

Further reading

  1. kubectl Reference Documentation
  2. kubectl Quick Reference (cheat sheet)
  3. Namespaces
  4. Organizing Cluster Access Using kubeconfig Files
  5. Debug Applications

Summary

A small set of kubectl commands works on every Kubernetes object. Learn the pattern once (kubectl <verb> <type> <name> <flags>) and you can manage anything in the cluster.

  1. Namespaces group objects. Use -n to target one namespace and -A to see all of them.
  2. Contexts save your default namespace, cluster and user; kubectl config set-context --current --namespace=<ns> is the quick way.
  3. kubectl get lists objects, describe shows details and explain documents fields; -o wide, -o yaml and -o jsonpath shape the output.
  4. kubectl apply -f creates or updates objects declaratively; preview with --dry-run or kubectl diff.
  5. kubectl delete removes objects without asking, so check your context first.
  6. kubectl label and kubectl annotate attach metadata, and -l selects objects by label.
  7. For debugging, logs, exec, port-forward, get events and top are your best friends.
  8. For maintenance, cordon, drain and uncordon take nodes out of service and back safely.
  9. Tab completion saves a lot of typing.

With these commands in hand, the next lessons can focus on the objects themselves: Pods, Deployments and Services.