Pods — the basic unit of Kubernetes

Here's a surprise for a lot of people: Kubernetes never schedules a container on its own. It schedules pods. A pod is the atom of the cluster — small, cheap, and meant to be thrown away.

What a pod actually is

A pod is a bundle of one or more containers that share a few things:

  • An IP address — every pod gets its own private cluster IP.
  • A network namespace — all containers in the pod see each other on localhost.
  • Storage volumes — mounted into any container in the pod.
  • A lifecycle — the pod's containers start together, stop together, and get rescheduled together.
Usually one container per pod

Nearly every pod you meet holds exactly one container. You reach for a multi-container pod when you've got a tightly-coupled sidecar — a logging agent, a service-mesh proxy, an init helper that needs to live and die on the same lifecycle as the main app.

Pod with a main container and a sidecar
Pod IP: 10.244.1.7 Main container app :8080 nginx / api / worker Sidecar log-shipper :9090 fluent-bit, envoy… localhost Both containers share the same IP, the same volumes, and can talk over 127.0.0.1.

Every pod gets its own IP

If you remember one networking fact about Kubernetes, make it this one:

The Kubernetes networking contract
  • Every pod has a unique cluster-wide IP.
  • Any pod can reach any other pod's IP directly — no NAT.
  • Pods on the same node reach each other without going through the host's public interface.

So pod-to-pod networking feels a lot like old-school VMs sitting on one flat network. Containers inside the same pod chat over localhost; when one pod talks to another, that traffic rides the cluster network — a CNI overlay like Calico or Cilium.

The trouble with pods that keep vanishing

Pods are cattle, not pets. They die, get rescheduled, and get recreated constantly — every deploy, every scale-down, every node failure, every update. And each time one comes back, it comes back with a brand-new IP. So you can't hardcode a pod's IP anywhere and expect it to hold.

Pod IPs change; other pods lose their connection
web pod 10.244.1.7 db pod v1 ✗ 10.244.2.4 db pod v2 10.244.2.9 (new IP!) stale IP → broken Fix: a Service.
For the support team — "it worked yesterday, now it can't reach the other pod"

When one pod suddenly can't talk to another after a deploy or a restart, nine times out of ten someone pinned a pod IP somewhere — a config file, an env var, a hardcoded address. That IP is gone the moment the pod was recreated. The fix is never to chase the new IP; it's to point that connection at a Service name instead, which stays put no matter how often the pods behind it churn.

And that's exactly the gap the next topic — Services — was built to fill.

A minimal pod manifest

apiVersion: v1
kind: Pod
metadata:
  name: hello
  labels:
    app: hello
spec:
  containers:
    - name: web
      image: ghcr.io/example/hello:1.0
      ports:
        - containerPort: 8080

In real life you'll almost never hand-create a pod like this. You wrap it in a Deployment, StatefulSet, DaemonSet, or Job, and that wrapper quietly brings your pods back whenever they die.

Try it yourself

kubectl run tmp --image=busybox --rm -it --restart=Never -- sh

# See pod IPs
kubectl get pods -o wide

# Exec into a pod
kubectl exec -it <pod> -- sh

# Reach another pod by its cluster IP
wget -qO- 10.244.2.9:8080

Key takeaways

  • A pod is Kubernetes' scheduling unit — usually one container, sometimes with sidecars.
  • Containers in a pod share IP, ports, network namespace, and volumes.
  • Every pod has a unique cluster IP; any pod can reach any other pod's IP.
  • Pod IPs are ephemeral — never hardcode them; wrap pods behind Services.