Pods, a little deeper
What is really inside a pod.
You started a pod in the last lesson. Now let's slow down and look at what a pod really is, because every other Kubernetes idea sits on top of it.
Start with a shipping container. It packs everything a product needs into one box, so the same box moves onto a truck, a ship, or a train without anyone repacking it. A software container does that for a program: it bundles your app together with the exact files and settings it needs, so the app runs the same way on any machine. A pod is the small crate Kubernetes puts that container into before handing it to a machine to run.
What's actually inside a pod
A pod is one or more containers that run together on the same node and share a single network address. A node is just one machine in your cluster, and a cluster is the whole set of machines Kubernetes has to run your apps on. A node can be a physical computer or a virtual one. Most pods hold exactly one container, which is your app. A few hold two, when the second container is a small helper that has to sit right next to the app, like one that collects the app's logs and ships them off.
The containers in a pod are like roommates sharing one small apartment. They live behind the same front door, which is a single IP address (the numeric address computers use to find each other on a network). Because they share it, they can talk to each other over localhost, a shortcut that means 'the same machine I'm on', instead of routing a message across the network. They can share storage too, the way roommates share a fridge.
Here's the part beginners often miss. The pod, not the container inside it, is the smallest unit Kubernetes manages. When Kubernetes decides where to run something, moves it, or counts how many copies exist, it works in whole pods. So a pod is really a group of containers that live and die together, sharing an address and sometimes some files.
Below is a complete pod written as YAML. YAML is a plain-text format for describing settings that people can read without much effort. Save it as pod.yaml:
apiVersion: v1kind: Podmetadata:name: hellolabels:app: hellospec:containers:- name: webimage: nginxports:- containerPort: 80
Read it top to bottom. apiVersion and kind together tell Kubernetes 'this is a Pod'. metadata.name gives the pod a name you'll use to refer to it. The label just under it, app: hello, is a sticker you can search for later. spec is the actual request: run one container named web from the nginx image. An image is a ready-made package a container is built from, and nginx is a popular free web server. The last line says the container listens on port 80, a numbered doorway where a program waits for incoming requests.
Send it to the cluster with apply, which means 'make the cluster match this file':
kubectl apply -f pod.yaml
pod/hello created
Check on it:
kubectl get pods
NAME READY STATUS RESTARTS AGEhello 1/1 Running 0 12s
Add -o wide to see which node it landed on and the IP address it was given:
kubectl get pod hello -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATEShello 1/1 Running 0 30s 10.244.1.7 worker-1 <none> <none>
When a pod keeps crashing
Not every pod turns Running. Sometimes the container starts, runs into a problem, and quits within seconds. Here's a pod built to fail on purpose, so you can see what that looks like before it happens to a real app. Save it as crasher.yaml:
apiVersion: v1kind: Podmetadata:name: crasherspec:containers:- name: appimage: busybox:1.36command: ["sh", "-c", "echo starting; sleep 2; echo 'config missing, giving up'; exit 1"]
The container prints a line, waits two seconds, complains that some config is missing, then exits with code 1, the number a program uses to say 'I failed'. Apply it:
kubectl apply -f crasher.yaml
pod/crasher created
Give it a minute for a few restarts to pile up, then list your pods:
kubectl get pods
NAME READY STATUS RESTARTS AGEcrasher 0/1 CrashLoopBackOff 3 (38s ago) 2m
Three columns tell the whole story. READY says 0/1: none of the pod's one container is up. RESTARTS is climbing, and the (38s ago) means the last restart was moments ago, so something keeps dying. STATUS is CrashLoopBackOff: Kubernetes is restarting the container, but waiting a little longer before each attempt (backing off) so a broken pod can't hammer the node at full speed. It is not stuck; it is looping.
To learn why it quit, read its logs. The container that failed has already been swept away and replaced by the one now waiting to start, so ask for the previous one with --previous:
kubectl logs crasher --previous
startingconfig missing, giving up
There it is, printed by the container just before it died. Real apps crash for real reasons too: a missing environment variable, a database they can't reach, a typo in the start command. When the logs aren't enough on their own, kubectl describe pod crasher adds the events Kubernetes recorded, including every restart and, if the image name itself is wrong, an ImagePullBackOff instead of a crash.
Clean up the crasher before moving on:
kubectl delete pod crasher
pod "crasher" deleted
Pods come and go
The most important thing to know about pods is that they're ephemeral, which just means short-lived and replaceable. A pod gets an IP address when it starts and loses it when it stops. Kubernetes never picks a running pod up and carries it to another machine. If a pod needs to run somewhere else, say its node just died, Kubernetes deletes the old pod and creates a brand-new one in its place, with a brand-new IP.
There's a common way to describe this: pods are cattle, not pets. You give a pet a name and take it to the vet; on a farm, you replace what breaks and move on. Kubernetes keeps your app healthy by replacing pods, not by nursing any single one. That's also why you rarely create pods by hand. Normally a controller does it for you. A controller is a background helper that watches your pods and keeps reality matching what you asked for, so when a pod disappears it starts a replacement automatically. Deployments and ReplicaSets are controllers, and they're next.
You can feel the disposability for yourself. Delete the pod you made by hand:
kubectl delete pod hello
pod "hello" deleted
Now list the pods again:
kubectl get pods
No resources found in default namespace.
Nothing came back. You created that pod directly, so no controller was watching it, and Kubernetes had no standing instruction to keep one running. That's exactly the difference a controller makes, and it's the reason you'll almost never run a bare pod in real life.
A Pod is the scheduling unit: one or more containers that share network namespace and volumes, always scheduled together on one node. Sidecars exist because of that shared fate — a log shipper next to an app shares localhost and can mount the same volume.
Pods are mortal. Controllers recreate them. Prefer designing apps that tolerate replacement: store state outside the container filesystem, use readiness probes before taking traffic, and do not rely on a stable Pod name or IP.
In an incident, kubectl get pods -o wide tells you which node hosts the problem. That is how you decide whether to drain a node or chase an app bug instead of versus treating every CrashLoop as a cluster failure.
Try this
Inspect a running Pod's IP, node, and container list. Exec into it to confirm shared network namespace basics.
$ kubectl run p1 --image=nginx:1.27 --restart=Neverpod/p1 created$ kubectl get pod p1 -o wideNAME READY STATUS RESTARTS AGE IP NODEp1 1/1 Running 0 6s 10.244.1.21 kind-worker$ kubectl get pod p1 -o jsonpath='{.spec.containers[*].name}{"\n"}{.status.podIP}{"\n"}'web10.244.1.21$ kubectl exec p1 -- nginx -vnginx version: nginx/1.27.0$ kubectl delete pod p1pod "p1" deleted
Takeaway
Pods co-locate containers with a shared network and fate, but Pods themselves are disposable. Design for replacement, and use -o wide plus describe when you need to know which node and IP are in play.