kubectl get and describe
Inspect Kubernetes resources with get and describe. Output formats, label and field selectors, jsonpath, and reading the events that explain most failures.
get lists resources and answers "what exists". describe shows one resource in full, including the events attached to it, and answers "why is this not working".
Most Kubernetes debugging is describe followed by reading the bottom of the output.
get
kubectl get pods
kubectl get pods,services,deployments # several types at once
kubectl get all # common types in this namespace
kubectl get pods -A # every namespace
kubectl get pods -n payments
kubectl get all is a convenience rather than a promise. It covers Pods, Services, Deployments, ReplicaSets and StatefulSets, but not ConfigMaps, Secrets, PVCs, Ingresses or anything custom.
Wider output
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
payments-api-7d4f-x8k2 1/1 Running 0 12m 10.244.1.17 node-1
payments-api-7d4f-m3p9 0/1 Running 4 (30s ago) 12m 10.244.2.31 node-2
Three columns carry most of the signal:
READYis containers passing readiness over total.0/1whileRunningmeans the process started but the readiness probe is failing, so the Pod receives no traffic.RESTARTSwith a timestamp means it restarted recently, which is very different from four restarts two days ago.NODEmatters when a failure correlates with one machine.
Watching
kubectl get pods -w
Live updates as things change. The clearest way to see a rolling update actually happen.
Selecting
kubectl get pods -l app=payments-api
kubectl get pods -l 'env in (staging,prod)'
kubectl get pods -l 'app=payments-api,version!=1.4.2'
kubectl get pods --show-labels
Label selectors are how Kubernetes objects find each other, and --show-labels is how you check why a Service selector is not matching.
Field selectors filter on object fields rather than labels:
kubectl get pods --field-selector status.phase=Running
kubectl get pods --field-selector spec.nodeName=node-1
kubectl get events --field-selector type=Warning
Sorting
kubectl get pods --sort-by=.status.containerStatuses[0].restartCount
kubectl get events --sort-by=.lastTimestamp
Sorting events by timestamp is worth memorising. The default order is not chronological, which is confusing exactly when you are in a hurry.
Output formats
kubectl get pod payments-api-7d4f-x8k2 -o yaml
kubectl get pod payments-api-7d4f-x8k2 -o json
-o yaml shows the object as stored, including status and defaults you never wrote. It is the definitive answer to "what is actually configured".
jsonpath
Extracting one value, for scripts or just to avoid scrolling:
kubectl get pod <name> -o jsonpath='{.status.podIP}'
# Why did the container die?
kubectl get pod <name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
# Every image in the namespace
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
custom-columns
A readable table of exactly what you want:
kubectl get pods -o custom-columns=\
NAME:.metadata.name,\
STATUS:.status.phase,\
READY:.status.containerStatuses[0].ready,\
RESTARTS:.status.containerStatuses[0].restartCount,\
NODE:.spec.nodeName
describe
kubectl describe pod payments-api-7d4f-m3p9
describe assembles the object, its defaults, related objects, and, the part that matters, the events for it.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m default-scheduler Successfully assigned payments/payments-api-7d4f-m3p9 to node-2
Normal Pulled 2m kubelet Container image "ghcr.io/acme/payments-api:1.5.0" already present
Warning Unhealthy 30s (x6 over 90s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 404
That last line is the whole diagnosis: the readiness path returns 404, so the Pod never becomes Ready and gets no traffic. No log reading required.
Events you will meet constantly:
| Reason | Meaning |
|---|---|
FailedScheduling | No node fits (the message lists why per node) |
Failed / ErrImagePull | Image cannot be pulled; the registry's own error is quoted |
Unhealthy | A probe failed, with the status code or error |
OOMKilled | Exceeded the memory limit |
FailedMount | Volume could not be attached or mounted |
BackOff | Restarting with increasing delay |
describe works on everything, and is just as useful elsewhere:
kubectl describe node node-2 # conditions, capacity, allocated resources
kubectl describe service payments-api # selector and endpoints
kubectl describe pvc payments-data # why a claim is Pending
kubectl describe deployment payments-api
Events on their own
describe only shows events for one object. Cluster-wide:
kubectl get events -A --sort-by=.lastTimestamp
kubectl get events --field-selector type=Warning
kubectl get events --field-selector involvedObject.name=payments-api-7d4f-m3p9
Events expire after about an hour by default. If an incident is more than an hour old, the event that explains it is already gone, which is a good argument for shipping events into your logging system.
get or describe?
| Use | For |
|---|---|
get | What exists, current status, many objects at once, scripting |
get -o yaml | Exactly what is configured, including defaults |
describe | Why one object is unhealthy (events are the payoff) |
The habit worth building: get to find the broken thing, describe to find out why, logs only if describe did not say.
Quick reference
| Task | Command |
|---|---|
| List with detail | kubectl get pods -o wide |
| Everything common | kubectl get all |
| Across namespaces | kubectl get pods -A |
| Watch live | kubectl get pods -w |
| By label | kubectl get pods -l app=<label> |
| Show labels | kubectl get pods --show-labels |
| Full object | kubectl get pod <n> -o yaml |
| One field | kubectl get pod <n> -o jsonpath='{.status.podIP}' |
| Diagnose | kubectl describe pod <n> |
| Recent events | kubectl get events -A --sort-by=.lastTimestamp |
| Warnings only | kubectl get events --field-selector type=Warning |
Key takeaways
describeis the debugging command, and the Events section at the bottom is why.READY 0/1with statusRunningmeans a failing readiness probe: the Pod gets no traffic.get -o yamlshows the object as stored, including defaults you never wrote.--show-labelsresolves most Service selector mismatches in one command.- Sort events by
.lastTimestamp: the default order is not chronological. - Events expire after roughly an hour. Ship them somewhere if you want them later.
kubectl get alldoes not include ConfigMaps, Secrets, PVCs or Ingresses.
Check your understanding
Running but not serving
kubectl get pods shows a Pod as Running with READY 0/1 and RESTARTS 0. What does this tell you?
An incident with no evidence
You investigate a crash that happened three hours ago. kubectl describe pod shows no events at all, though the Pod clearly restarted. Why?
Frequently Asked Questions
What is the difference between kubectl get and kubectl describe?
get lists resources with a short status summary and works across many objects at once, which makes it the right tool for finding things and for scripting. describe renders one object in full. Its spec, its defaults, related objects, and crucially the events attached to it. Events are where Kubernetes explains itself, so describe is what you run when something is broken and get is what you run to find out what exists.
How do I see why a Pod is failing?
kubectl describe pod <name> and read the Events section at the bottom. It usually states the cause outright: FailedScheduling with the per-node reasons, an image pull error quoting the registry's own message, Unhealthy with the probe's status code, or a FailedMount for a volume problem. Only if the events do not explain it do you need kubectl logs <pod> --previous, which shows output from the container run that actually crashed.
What does READY 0/1 mean?
Zero of one containers are passing their readiness probe. If the Pod's status is also Running, the process started fine and the probe itself is failing. A wrong path or port, an endpoint behind authentication returning 401, or an initialDelaySeconds shorter than real startup time. A Pod in this state is deliberately excluded from its Service's endpoints, so it receives no traffic, which is exactly what readiness is for.
How do I get just one field from a resource?
Use jsonpath: kubectl get pod <name> -o jsonpath='{.status.podIP}'. It is particularly useful for values buried in status, such as kubectl get pod <name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}' which returns OOMKilled for a container killed on memory. For tabular output across many objects, -o custom-columns lets you build exactly the table you want rather than scrolling through YAML.
Why are my events missing?
They expire. Kubernetes keeps events for about an hour by default, set by the API server's --event-ttl, because they are high volume and treated as transient. Anything older than that is gone, which routinely means the event explaining an overnight failure has vanished before anyone looks. Exporting events into your log store, the Kubernetes Event Exporter is the usual tool, preserves them, and it is not something the common monitoring stacks do for you.
Does kubectl get all really show everything?
No, and the name is misleading. It covers the common workload and networking types. Pods, Services, Deployments, ReplicaSets, StatefulSets, DaemonSets and Jobs, but omits ConfigMaps, Secrets, PersistentVolumeClaims, Ingresses, ServiceAccounts and every custom resource. To see genuinely everything in a namespace you need to enumerate the API resources and query each, which kubectl api-resources supports but is rarely worth doing by hand.