Understanding Kubernetes: Pods Are Not Containers

Understanding Kubernetes: Pods Are Not Containers

After talking about Kubernetes as an API-driven reconciliation system in the previous article, it’s time to look at one of the most fundamental Kubernetes objects: Pods.

When starting with Kubernetes, it’s very easy to think something like: “A Pod is basically just a container.” And to be fair, Kubernetes tutorials accidentally reinforce this idea all the time.

Most examples look something like this:

containers:
  - name: nginx
    image: nginx

One Pod. One container.

So naturally, Pod and container start feeling interchangeable. But internally, Kubernetes treats them very differently. And once you start looking at topics like:

  • networking
  • sidecars
  • init containers
  • shared storage
  • service meshes

the distinction suddenly becomes extremely important. Because Pods are not containers. Pods are execution environments around containers.

Pods are Kubernetes objects

Just like Deployments or Services, Pods are API objects stored in the Kubernetes API server. For example:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
    - name: nginx
      image: nginx

Again, this is not Kubernetes “running a container”. This is you declaring a desired state object in the API. And Kubernetes then reconciles reality to match it.

One important detail here: Kubernetes never schedules containers directly. It always schedules Pods. That’s why Pods become the smallest deployable unit in Kubernetes.

Containers are not

While Pods are first-calls Kubernetes API objects, containers themselves only exists as part of a Pod specification. This helps a lot when it comes to what containers often need, e.g.:

  • communicate closely
  • share storage
  • share networking
  • start in a certain order
  • behave like one logical unit

A Pod in Kubernetes hence becomes a shared execution environment for one or more containers living in one namespace.

This means containers inside one Pod:

  • share the same IP address
  • share the same network stack
  • communicate via localhost
  • share port space

For example:

Pod
├── nginx container
└── sidecar container

Both containers can reach each other via localhost because Kubernetes places them into the same network namespace. This is also why two containers inside one Pod cannot both bind to the same port.

And importantly: The Pod itself is not:

  • a VM
  • a machine
  • a durable object

It’s simply the environment wrapping the containers.

At this point, many people ask: “If Pods can contain multiple containers, why do most examples only use one?” And the answer is actually pretty simple: Most applications simply do not need tightly coupled helper containers.

So even though Kubernetes always wraps containers inside Pods, many Pods still only contain one container. But multi-container Pods become very useful once containers need to work together closely.

Sidecars: helper containers inside a Pod

One common example is the sidecar pattern. This usually means we have one main application container with one helper container running next to it.

These sidecars could be logging agents, proxies, service mesh sidecars, or synchronization helpers, sharing networking and livecycle behaviour with the main application container. For example:

  • a proxy sidecar can intercept local traffic
  • a logging sidecar can access shared volumes
  • a service mesh sidecar can transparently handle networking

Init containers

Another special Pod concept are init containers. These run before the normal application containers start. Typical use cases include:

  • waiting for dependencies
  • database migrations
  • preparing configuration
  • downloading assets

Only when all init containers successfully finish, Kubernetes will start the main containers.

Demo time

One of the easiest ways to see Pod behavior in practice is running two containers inside one Pod. For example:

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-demo
spec:
  containers:
    - name: nginx
      image: nginx
    - name: toolbox
      image: busybox
      command: ["sleep", "3600"]

After creating the Pod with

kubectl apply -f pod.yaml

we can open a shell inside the busybox container:

kubectl exec -it multi-container-demo -c toolbox -- sh

and from there run:

wget -qO- localhost

Even though nginx runs in a different container, it is reachable via localhost because both containers share the same Pod network namespace.

Pods are designed to be disposable

Another very important mindset shift: Pods are intentionally ephemeral objects.

Kubernetes does not treat Pods like durable machines.. Instead, Pods are expected to:

  • disappear
  • be recreated
  • move between nodes
  • receive new IP addresses

all the time.

And this directly connects back to the reconciliation model from the previous article. If a Pod disappears, a controller simply creates a new Pod object matching the desired state.

And that distinction matters a lot, because it changes how applications need to behave in Kubernetes:

  • state should usually live outside Pods
  • workloads should tolerate restarts
  • applications should handle rescheduling gracefully

Pod lifecycle

Pods move through different phases during their lifecycle:

  • Pending
  • Running
  • Succeeded
  • Failed

You can inspect this with kubectl get pods

Useful Pod debugging commands

When starting with Kubernetes, these commands become incredibly useful:

kubectl get pods
kubectl get pods -o wide
kubectl describe pod <name>
kubectl logs <name>
kubectl exec -it <name> -- sh

especially when debugging workloads.

Summing up

Pods are a Kubernetes abstraction around containers. They provide shared networking, shared storage, shared lifecycle behavior, and shared execution context.

Understanding this distinction becomes especially important once you start working with networking, observability, service meshes, scheduling, and security boundaries.

This was the second part of my Kubernetes series. Next up: Deployments and why Pods almost never run alone :)