Events & the event stream
What the cluster is telling you, in order.
A Pod sits in Pending for ten minutes and nothing you look at explains it. The Deployment is healthy. The image name is spelled right. Then you run kubectl describe on the Pod, scroll to the very bottom, and one line settles it: 0/6 nodes are available: 6 Insufficient memory. The cluster knew all along. It had been telling you through Events the whole time, and you weren't reading them. (A Pod is the smallest thing Kubernetes runs, one or more containers scheduled together as a unit.)
A dispatcher's radio channel runs all shift. Units call in what they're doing and what went wrong. Can't reach the address. Engine won't turn over. Handing this one to unit four. It's spoken out loud, useful in the moment, and nobody keeps a recording. Kubernetes runs on the same idea. As each component does its work, it narrates. The scheduler, the part that picks which machine a Pod runs on, says why it couldn't place one. The kubelet, the agent sitting on every worker node, reports that an image failed to pull or a health probe came back sick. Controllers call out scale-ups and rollouts. Every one of those announcements is an Event.
An Event is a real object, not a log line
This is the part people skip past. An Event isn't text buried in a file somewhere. It's a first-class API object, kind: Event, stored in etcd (the cluster's key-value database) right alongside your Pods and Services, and it lives inside a namespace. Components don't print events. They POST them to the API server through a small helper called an EventRecorder. That's the whole reason kubectl get events works: you're listing stored objects, exactly the way you'd list Pods. You can list them, watch them, select them by field, even pipe them through jq, because underneath they really are just objects. Every Event carries the same handful of fields, and once you know them the output stops looking like noise.
$ kubectl get event -n prod checkout-7d9c8-x2fjq.17f0a9b2c3d4e5f6 -o yamlapiVersion: v1kind: Eventmetadata:name: checkout-7d9c8-x2fjq.17f0a9b2c3d4e5f6namespace: prodtype: Warningreason: FailedSchedulingmessage: '0/6 nodes are available: 6 Insufficient memory. preemption: 0/6 nodes are available.'involvedObject:kind: Podname: checkout-7d9c8-x2fjqnamespace: prodsource:component: default-schedulercount: 9firstTimestamp: '2026-07-16T09:14:02Z'lastTimestamp: '2026-07-16T09:31:40Z'
Look at count: 9. The scheduler did not create nine Event objects. It created one. Every time the same failure repeated, the EventRecorder found that existing Event (same object, same reason, same message) and bumped the count while moving lastTimestamp forward. That's why describe prints it as (x9 over 18m). The folding is deliberate, and it happens before anything reaches storage. A crash-looping Pod could otherwise fire an event every second and bury etcd, so the same recorder machinery also rate-limits how many events it will send for one object. Worth keeping in mind during an incident: under a real storm the recorder drops some events instead of sending them all, so an empty result doesn't always mean nothing happened.
If you go digging you'll find events actually live under two API groups. There's the original core/v1 Event, and a newer events.k8s.io/v1 that went stable in v1.19, built to aggregate high-frequency events more cheaply (it groups repeats into a series instead of one rolling count). kubectl bridges both, so kubectl get events and kubectl events show you everything regardless of which group produced it. You rarely pick a group by hand. It matters mostly when you write a controller that emits its own events.
Reading the stream three ways
describe is the fast path when you already know the object. It pulls that object's recent events and prints them at the bottom, which is where nine out of ten 'why is this stuck' answers live. When you don't know the object yet, or several things are failing at once, you want the whole namespace's stream, sorted so the sequence makes sense. And recent kubectl versions ship a dedicated command, kubectl events, built for exactly this instead of bending kubectl get to the job.
$ kubectl get events -n prod --sort-by=.lastTimestampLAST SEEN TYPE REASON OBJECT MESSAGE6m Normal Scheduled pod/checkout-7d9c8-k4p2j Successfully assigned prod/checkout-7d9c8-k4p2j to node-25m Normal Pulled pod/checkout-7d9c8-k4p2j Container image "reg/checkout:1.8" already present on machine90s Warning FailedScheduling pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.40s Warning BackOff pod/payments-api-77-9mnpq Back-off restarting failed container$ kubectl get events -n prod --field-selector type=Warning,reason=FailedSchedulingLAST SEEN TYPE REASON OBJECT MESSAGE90s Warning FailedScheduling pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.
The reason field is the real signal, and a small vocabulary covers most of what you'll meet. FailedScheduling means no node fit: not enough resources, or a taint or an affinity rule ruled every node out. Failed usually means the kubelet couldn't pull the image (a bad tag, a private registry, a missing pull secret), and you'll watch the Pod's status flip from ErrImagePull to ImagePullBackOff as it keeps retrying. Unhealthy means a liveness or readiness probe came back bad. BackOff means the container keeps crashing and the kubelet is now waiting longer between restarts. FailedMount means a volume wouldn't mount. Each word points straight at one layer, so it usually tells you where to look next.
$ kubectl events --for pod/checkout-7d9c8-x2fjq -n prod --types=WarningLAST SEEN TYPE REASON OBJECT MESSAGE2m (x9 over 18m) Warning FailedScheduling Pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.# after you raise the memory limit and the Pod finally schedules, watch it clear:$ kubectl events --for pod/checkout-7d9c8-x2fjq -n prod --watchLAST SEEN TYPE REASON OBJECT MESSAGE0s Normal Scheduled Pod/checkout-7d9c8-x2fjq Successfully assigned prod/checkout-7d9c8-x2fjq to node-40s Normal Pulled Pod/checkout-7d9c8-x2fjq Container image "reg/checkout:1.8" already present on machine0s Normal Started Pod/checkout-7d9c8-x2fjq Started container checkout
Events expire, so capture them
One thing the output won't warn you about: events don't last. The API server expires them on a timer set by --event-ttl, one hour by default, so the events explaining this morning's outage may be gone by lunch. This isn't the normal garbage collector doing it. The API server prunes events itself. Two consequences follow. Read them while the problem is fresh, not tomorrow. And if you need history for a post-incident review, ship events off the cluster (a tool like eventrouter or the Kubernetes event-exporter that streams them to your logging stack) so the 'why' survives past the hour.
Normal versus Warning is a hint, not a severity system. A flood of Normal Scheduled can still hide one Warning FailedScheduling.
Events are not an audit log. Prefer API audit for who did what.
Spammy controllers can drown useful events. Filter by involvedObject when the stream is noisy.
Try this
Sort events by lastTimestamp in a namespace with a failing pod. Read the newest warnings and map them to scheduling, pulling, or probing.
$ kubectl get event -n prod checkout-7d9c8-x2fjq.17f0a9b2c3d4e5f6 -o yamlapiVersion: v1kind: Eventmetadata:name: checkout-7d9c8-x2fjq.17f0a9b2c3d4e5f6namespace: prodtype: Warningreason: FailedSchedulingmessage: '0/6 nodes are available: 6 Insufficient memory. preemption: 0/6 nodes are available.'involvedObject:kind: Podname: checkout-7d9c8-x2fjqnamespace: prodsource:component: default-schedulercount: 9firstTimestamp: '2026-07-16T09:14:02Z'lastTimestamp: '2026-07-16T09:31:40Z'$ kubectl get events -n prod --sort-by=.lastTimestampLAST SEEN TYPE REASON OBJECT MESSAGE6m Normal Scheduled pod/checkout-7d9c8-k4p2j Successfully assigned prod/checkout-7d9c8-k4p2j to node-25m Normal Pulled pod/checkout-7d9c8-k4p2j Container image "reg/checkout:1.8" already present on machine90s Warning FailedScheduling pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.40s Warning BackOff pod/payments-api-77-9mnpq Back-off restarting failed container$ kubectl get events -n prod --field-selector type=Warning,reason=FailedSchedulingLAST SEEN TYPE REASON OBJECT MESSAGE90s Warning FailedScheduling pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.$ kubectl events --for pod/checkout-7d9c8-x2fjq -n prod --types=WarningLAST SEEN TYPE REASON OBJECT MESSAGE2m (x9 over 18m) Warning FailedScheduling Pod/checkout-7d9c8-x2fjq 0/6 nodes are available: 6 Insufficient memory.# after you raise the memory limit and the Pod finally schedules, watch it clear:$ kubectl events --for pod/checkout-7d9c8-x2fjq -n prod --watchLAST SEEN TYPE REASON OBJECT MESSAGE0s Normal Scheduled Pod/checkout-7d9c8-x2fjq Successfully assigned prod/checkout-7d9c8-x2fjq to node-40s Normal Pulled Pod/checkout-7d9c8-x2fjq Container image "reg/checkout:1.8" already present on machine0s Normal Started Pod/checkout-7d9c8-x2fjq Started container checkout
Takeaway
Events are the cluster narration. They expire. Read them while debugging, not a day later.