skip to content

What is the difference between kubectl describe pod and kubectl get pod -o yaml, and how do you read Kubernetes Events correctly?

level: middleimportance: must knowfreq 55%

answer

  1. describe = rendered view + joined events
  2. -o yaml = real spec + status, scriptable
  3. events: namespaced objects, ~1h TTL
  4. aggregated with count, unsorted by default
  5. pod events only; check RS, node, HPA too

basics

~20 s

describe is a human summary that also joins in Events referencing the object; -o yaml is the exact API object, spec plus status, suitable for scripting. Events are separate namespaced objects, expire after about an hour, are aggregated with counts, and are not sorted by default.

solid answer

~50 s

`kubectl describe pod p` renders a formatted view — conditions, container state and last termination, image, probes, requests and limits, QoS class, node, `Controlled By` — and then queries the Events API for events whose `involvedObject` is that pod and prints them at the bottom. It is derived and lossy. `kubectl get pod p -o yaml` returns the true object: full `spec` after defaulting and injection, `status.conditions`, `status.containerStatuses[].state` and `.lastState.terminated` with exit code and reason, `ownerReferences`, annotations. It is what you script against with `-o jsonpath` and what you attach to a ticket. Events deserve care: they are first-class namespaced objects with their own TTL, roughly one hour by default, so a silent Events section may just mean the incident is old. They are deduplicated with a `count` plus first and last timestamps, and `kubectl get events` is unordered — use `--sort-by=.lastTimestamp` or `kubectl events --for pod/p`. Events for the ReplicaSet, node, or HPA are separate objects that describing the pod will not show.

code

bash · 4 lines
bash
kubectl -n prod describe pod api-7d9f-abcde
kubectl -n prod get events --sort-by=.lastTimestamp | tail -30
kubectl -n prod get events --field-selector involvedObject.name=api-7d9f-abcde
kubectl -n prod get pod api-7d9f-abcde -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'

go deeper

for a junior

Know that describe is the readable summary with events, and -o yaml shows the raw object.

for a middle

Explain which status fields answer which question, and the event TTL, aggregation and sorting behaviour.

for a senior

Chase events across owning objects and nodes, script with jsonpath, and preserve yaml plus sorted events as incident evidence.

for a principal

Recognise events as best-effort and short-lived, and decide whether the platform exports them to durable storage for post-incident analysis.

## Two different views of the same object `describe` is a **client-side rendering** built for humans. It reads the object, formats the parts people usually want, computes a few things (QoS class, effective node, controller reference), hides noise such as `managedFields`, and then makes an extra API call for related Events. Because it is a rendering, it omits fields — if something you expect is not on screen, that does not mean it is absent from the object. `get -o yaml` (or `-o json`) is the **object itself** as the API server stores and serves it. You get the entire `spec` after admission and defaulting — including injected sidecars, service-account token volumes, resolved `nodeName`, tolerations added by admission — and the entire `status`. It contains no events. Modern kubectl hides `managedFields` unless you pass `--show-managed-fields`. Which to use: `describe` first because it is faster to read and carries events; `-o yaml` when you need precision, when you suspect describe is hiding something, when comparing two objects (a diff of two yaml dumps is a strong technique), or when scripting with `-o jsonpath`. ## The status fields that matter In `-o yaml`, the parts worth knowing by name: `status.phase` (Pending, Running, Succeeded, Failed, Unknown — coarse and often less useful than people assume); `status.conditions` with `PodScheduled`, `Initialized`, `ContainersReady`, `Ready`, each carrying a reason and message; `status.containerStatuses[]` with `ready`, `restartCount`, `image`, `imageID`, `state` (running, waiting or terminated with a reason) and `lastState` for the previous run. `metadata.ownerReferences` names the controlling ReplicaSet or Job, and `metadata.creationTimestamp` distinguishes a fresh pod from a long-lived one. ## Events are objects, not a log An Event is an API resource in a namespace, with `involvedObject` pointing at the thing it describes, plus `reason`, `message`, `source` (which component emitted it: scheduler, kubelet, controller-manager), `type` (Normal or Warning), `firstTimestamp`, `lastTimestamp` and `count`. Practical implications: - **They expire.** The API server's event TTL defaults to about one hour. An hour after an incident the Events section can be empty even though the pod is still broken — absence of events is not evidence of absence of trouble. - **They are aggregated.** Repeated identical events are collapsed into one object with an incremented `count`, so "1 event" may represent 300 occurrences over 40 minutes. Read `count` together with the first and last timestamps. - **They are unordered by default.** `kubectl get events` returns them in no useful order; always add `--sort-by=.lastTimestamp` (or `.metadata.creationTimestamp`). Newer kubectl offers `kubectl events --for pod/mypod --watch`, which is clearer. - **They are per-object.** `describe pod` shows only events whose involvedObject is that pod. A rollout stuck because the ReplicaSet cannot create pods (quota, Pod Security rejection) emits events on the **ReplicaSet**; eviction and pressure events land on the **node**; scaling decisions land on the **HorizontalPodAutoscaler**. Checking only the pod hides all of these — hence `kubectl get events -n <ns> --sort-by=.lastTimestamp` as a wide net, or `--field-selector involvedObject.name=<name>` to focus. - **They are best-effort.** Emitting components rate-limit events, so a storm can be dropped. Events are a strong hint, never a guarantee of completeness. ## Putting them together A sound reading order for a broken pod: `describe` for the summary plus its events; if the events are empty or stale, `kubectl get events -n ns --sort-by=.lastTimestamp` to see what happened around it, including on the node and controller; then `-o yaml` for the exact state to quote and preserve. Saving both the yaml and the sorted event list at the start of an incident gives you an evidence pack that survives the pod being replaced.

  • A pod has been broken for two hours and describe shows no Events at all. What do you conclude?
    Nothing about the cause — events expire after roughly an hour by default, so the ones explaining the original failure are simply gone. Fall back to the pod's status fields, which persist, to the controller's status, and to any exported events or monitoring history. If events matter operationally they should be shipped to a durable store rather than read from the API server after the fact.
  • A Deployment's rollout is stuck and no new pods appear at all. Where are the relevant events?
    On the ReplicaSet, not on a pod, because no pod was ever created — typical reasons are resource quota rejection, a Pod Security admission denial, or a failing admission webhook. Run kubectl describe rs on the newest ReplicaSet, or list namespace events sorted by time, to see the creation failures.

saying these in an interview costs you the question

  • Thinking describe shows the complete object rather than a rendered subset
  • Reading kubectl get events without sorting and assuming the output is chronological
  • Treating an empty Events section as proof nothing happened
  • Looking only at pod events when the failure is on the ReplicaSet, node, or HPA
  • Ignoring the count field and treating an aggregated event as a single occurrence

context