How to use this page
Find the job you're trying to do, and copy the command. Words in capitals, like NAME or FILE, are yours to replace. Every row links to the lesson that explains it, if you want the why as well as the how. Use the print button at the top to keep a copy next to you.
Two habits that make every command here easier:
- Most commands work on the current namespace. Add
-n NAMESPACEto choose another, or-Afor all of them. - Ask before you change.
kubectl get,describeandlogsonly read.apply,delete,patchandrolloutchange things.
When a pod won't start
Start here when something's wrong. The STATUS column of kubectl get pods usually tells you where to look.
| Status | Usually means | Look first | Lesson |
|---|---|---|---|
Pending | No node can take it yet: not enough room, a taint, or storage that isn't ready | kubectl describe pod NAME, the Events at the bottom | 9, 11 |
ContainerCreating | Downloading the image or attaching storage. Fine briefly; a problem if it stays | kubectl describe pod NAME | 3 |
ErrImagePull, ImagePullBackOff | The image name or tag is wrong, or the image is private | kubectl describe pod NAME, then check the image name | 3 |
CrashLoopBackOff | The container starts, exits, and is restarted, over and over | kubectl logs NAME --previous | 3 |
OOMKilled | It used more memory than its limit, and was stopped | kubectl describe pod NAME, under Last State | 9 |
CreateContainerConfigError | A ConfigMap or Secret it needs, or a key inside one, doesn't exist | kubectl describe pod NAME, then check the names | 7 |
Running, but READY 0/1 | It's running, but its readiness check is failing, so it gets no traffic | kubectl get events --field-selector reason=Unhealthy | 6 |
Running, and RESTARTS keeps climbing | Its liveness check is failing, so Kubernetes keeps restarting it. Often the app is fine and the check is wrong | kubectl get events --field-selector reason=Unhealthy | 6 |
Seeing what's there
| To | Run | Lesson |
|---|---|---|
| List pods | kubectl get pods | 3 |
| Which node each pod is on, and its address | kubectl get pods -o wide | 2 |
| Watch pods change, live | kubectl get pods --watch | 3 |
| Several kinds at once | kubectl get deployments,replicasets,pods | 5 |
| Everything, in every namespace | kubectl get deployments -A | 10 |
| Show labels | kubectl get pods --show-labels | 8 |
| Only pods with a label | kubectl get pods -l app=NAME | 8 |
| The full details, as YAML | kubectl get pod NAME -o yaml | 4 |
| Details and recent events, readable | kubectl describe pod NAME | 3 |
| Recent events of one kind | kubectl get events --field-selector reason=Failed | 7 |
| The nodes | kubectl get nodes | 2 |
| What a node has room for | kubectl describe node NAME | 9 |
| Memory and CPU in use (needs metrics-server) | kubectl top pods, kubectl top node | 9 |
| What a field in a YAML file means | kubectl explain deployment.spec.replicas | 4 |
Inside a running pod
| To | Run | Lesson |
|---|---|---|
| Its logs | kubectl logs NAME | 3 |
| Logs from before the last crash | kubectl logs NAME --previous | 3 |
| Run one command inside it | kubectl exec NAME -- COMMAND | 3 |
| The same, on any pod of a Deployment | kubectl exec deploy/NAME -- env | 7 |
| Reach it from your laptop | kubectl port-forward pod/NAME 8080:80 | 3 |
| Reach a Service from your laptop | kubectl port-forward service/NAME 8080:80 | 8 |
| Test a Service from inside the cluster | kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME | 8 |
| Check a Service's DNS name | kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- nslookup NAME.NAMESPACE.svc.cluster.local | 8 |
Creating and changing things
| To | Run | Lesson |
|---|---|---|
| Create or update from a file | kubectl apply -f FILE.yaml | 3 |
| Check a file against the cluster, without changing anything | kubectl apply -f FILE.yaml --dry-run=server | 4 |
| Write the YAML for you, without creating anything | kubectl create deployment NAME --image=nginx:1.27 --replicas=2 --dry-run=client -o yaml > deploy.yaml | 4 |
| Create a Deployment | kubectl create deployment NAME --image=IMAGE --replicas=3 | 2 |
| Run a single pod | kubectl run NAME --image=IMAGE | 3 |
| Give a Deployment a Service | kubectl expose deployment NAME --port=80 --target-port=5678 | 12 |
| Set an environment variable | kubectl set env deployment/NAME KEY="value" | 12 |
| Change one field in place | kubectl patch configmap NAME -p '{"data":{"KEY":"value"}}' | 7 |
| Label a node | kubectl label node NAME disk=ssd | 2 |
| Keep ordinary pods off a node | kubectl taint nodes NAME dedicated=gpu:NoSchedule | 9 |
| Wait until something is ready | kubectl wait --for=condition=Available deployment/NAME --timeout=5m | 12 |
| Delete a pod (a Deployment replaces it) | kubectl delete pod NAME | 2 |
| Delete everything with a label | kubectl delete pod -l app=NAME | 11 |
Rolling out a change
| To | Run | Lesson |
|---|---|---|
| Watch an update finish | kubectl rollout status deployment/NAME | 5 |
| Go back to the previous version | kubectl rollout undo deployment/NAME | 5 |
| See past versions | kubectl rollout history deployment/NAME | 5 |
| Restart every pod, one at a time | kubectl rollout restart deployment/NAME | 7 |
Health checks (probes)
A probe is a check Kubernetes runs on a container, on a schedule. There are three, and each one does something different when it fails.
| Probe | Asks | When it fails | Use it for |
|---|---|---|---|
readinessProbe | Can you take requests right now? | The pod gets no traffic, but keeps running. Nothing restarts | Almost every app that serves requests |
livenessProbe | Are you still working at all? | The container is restarted | Apps that can freeze without exiting |
startupProbe | Have you finished starting? | The other two wait until it passes. If it never passes, the container is restarted | Apps that take a long time to start |
A probe asks in one of three ways: httpGet sends an HTTP request (any 200 to 399 reply passes), tcpSocket checks that a port accepts connections, and exec runs a command inside the container (exit code 0 passes). This goes under a container in a Deployment:
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
livenessProbe:
httpGet:
path: /alive
port: 8080
periodSeconds: 10
failureThreshold: 3
startupProbe:
httpGet:
path: /alive
port: 8080
periodSeconds: 10
failureThreshold: 30
That startup probe gives the app up to 5 minutes to start (30 checks, 10 seconds apart) before the liveness probe takes over.
| Setting | Means | Default |
|---|---|---|
periodSeconds | How often to check | 10 |
failureThreshold | Failures in a row before acting | 3 |
timeoutSeconds | How long to wait for a reply before it counts as a failure | 1 |
initialDelaySeconds | How long to wait after the container starts before the first check. A startup probe is usually the better choice | 0 |
| To | Run | Lesson |
|---|---|---|
| See failed probes, and why | kubectl get events --field-selector reason=Unhealthy | 6 |
| See a pod's probes and their settings | kubectl describe pod NAME, the Liveness: and Readiness: lines | 6 |
| Watch readiness and restarts change | kubectl get pods -w, the READY and RESTARTS columns | 6 |
| Hit the probe's path yourself | kubectl port-forward pod/NAME 8080:8080, then curl -i localhost:8080/PATH | 3 |
| Look up every probe field | kubectl explain pod.spec.containers.livenessProbe | 4 |
| Undo a release whose probe is wrong | kubectl rollout undo deployment/NAME | 6 |
Two rules that prevent most probe trouble:
- Liveness checks only the app itself. Never make it depend on a database or another service. If it does, one database outage makes Kubernetes restart every healthy pod. Put those checks in readiness instead.
- Not sure an app needs a liveness probe? Leave it out. Kubernetes already restarts containers that exit. A wrong liveness probe turns a healthy app into a crashing one.
Settings and Secrets
| To | Run | Lesson |
|---|---|---|
| Create a ConfigMap | kubectl create configmap NAME --from-literal=KEY="value" | 7 |
| Type a secret without it showing or being saved in history | read -s API_KEY | 7 |
| Create a Secret from it, then forget it | kubectl create secret generic NAME --from-literal=API_KEY="$API_KEY", then unset API_KEY | 7 |
| Read one value from a Secret | kubectl get secret NAME -o jsonpath='{.data.API_KEY}' | base64 -d | 7 |
| See a pod's environment | kubectl exec deploy/NAME -- env | 7 |
Secrets are encoded, not encrypted: anyone who can read them can decode them, as the base64 -d row shows. Protect them with access control.
Namespaces and who can do what
| To | Run | Lesson |
|---|---|---|
| List namespaces | kubectl get namespaces | 10 |
| Create one | kubectl create namespace NAME | 10 |
| Work in one by default | kubectl config set-context --current --namespace=NAME | 10 |
| Create a ServiceAccount | kubectl create serviceaccount NAME -n NAMESPACE | 10 |
| Check what an account is allowed to do | kubectl auth can-i list pods -n NAMESPACE --as=system:serviceaccount:NAMESPACE:NAME | 10 |
| Check what you're allowed to do | kubectl auth can-i delete pods | 10 |
Storage
| To | Run | Lesson |
|---|---|---|
| Which kinds of storage the cluster offers | kubectl get storageclass | 11 |
| Claims, and whether they're bound | kubectl get pvc | 11 |
| The volumes behind them | kubectl get pv | 11 |
| Why a claim is stuck | kubectl describe pvc NAME | 11 |
Getting traffic in
| To | Run | Lesson |
|---|---|---|
| A Service's address and ports | kubectl get service NAME | 8 |
| Which pods a Service sends traffic to | kubectl describe service NAME, under Endpoints | 8 |
| List routes | kubectl get httproute | 12 |
| Why a route isn't working | kubectl describe httproute NAME, under Conditions | 12 |
| Test a route that matches on host name | curl -H "Host: www.example.com" http://localhost:8080/ | 12 |
Your cluster and context
| To | Run | Lesson |
|---|---|---|
| Which cluster kubectl is talking to | kubectl config current-context | 2 |
| Where that cluster is | kubectl cluster-info | 2 |
| The kubectl version | kubectl version --client | 2 |
| Make a practice cluster | kind create cluster --name learn | 2 |
| Delete it | kind delete cluster --name learn | 2 |
Check the context before anything that changes things. kubectl changes whichever cluster it's pointed at. One kubectl config current-context before an apply or delete is the cheapest habit in Kubernetes.