Skip to content

Pods

Kubernetes does not schedule containers. It schedules Pods, and a Pod is one or more containers that are guaranteed to land on the same node and share a network namespace.

That guarantee is the entire reason Pods exist. Two containers in a Pod reach each other over localhost, share an IP address and a hostname, and can share volumes — which is exactly what a sidecar, a log shipper or a proxy needs. Containers in different Pods can make no such assumption.

Reference

Terminology

  • Pod — The smallest deployable unit: a group of containers with shared network and storage, scheduled together on one node.
  • Sandbox (pause) container — Every Pod has one, and it does nothing but hold the network namespace open. The containers you care about join it. This is why a Pod keeps its IP even when all its application containers restart.
  • Init container — Runs to completion, in order, before the main containers start. Used for setup that must finish first: waiting for a dependency, cloning a repository, fixing file ownership on a volume.
  • Sidecar — A container that runs alongside the main one for the Pod’s whole lifetime. Log shippers, service-mesh proxies and config reloaders are sidecars.
  • restartPolicyAlways (the default, for long-running workloads), OnFailure, or Never. It applies to the whole Pod, and it is what distinguishes a Deployment from a Job.
  • Static Pod — A Pod managed directly by the kubelet from a file on the node rather than by the API server. The control plane’s own components are static Pods, which is worth knowing on Day 5.

The Shape of a Pod

apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  restartPolicy: Always
  containers:
    - name: web
      image: docker.io/library/nginx:1.27
      ports:
        - containerPort: 80
      volumeMounts:
        - name: shared
          mountPath: /usr/share/nginx/html
  volumes:
    - name: shared
      emptyDir: {}

Note what is not there: no replicas, no update strategy, no rollout. A bare Pod is a single, unmanaged thing. If its node dies, nobody replaces it.

The Lifecycle

PhaseMeans
PendingAccepted, but not yet running — usually scheduling, or pulling an image
RunningAt least one container is running
SucceededEvery container exited 0, and the Pod will not restart
FailedA container exited non-zero and the restart policy gave up

Phases are coarse. When you need detail, look at the container states instead:

kubectl get pod web -o jsonpath='{.status.containerStatuses[*].state}{"\n"}'

Waiting with reason ImagePullBackOff or CrashLoopBackOff is where most beginners’ problems live, and the reason string tells you which.

Exercises

  1. Create a Pod and inspect it:

    kubectl run web --image=docker.io/library/nginx:1.27 --port=80
    kubectl get pod web -o wide
    kubectl get pod web -o jsonpath='{.status.podIP}{"\n"}'
  2. Two containers, one network namespace. Write a Pod with a web container and a sidecar container, both from docker.io/library/busybox, with the sidecar running sleep 3600:

    kubectl exec -it web -c sidecar -- sh

    From inside the sidecar, wget -qO- localhost:80 reaches the web container on localhost. Now check hostname — both containers report the same one.

  3. Init containers run first, and run again on every Pod start. Add one that writes a file into a shared emptyDir and have the main container read it:

    initContainers:
      - name: setup
        image: docker.io/library/busybox
        command: ["sh", "-c", "echo prepared > /work/state.txt"]
        volumeMounts:
          - name: work
            mountPath: /work

    If the init container fails, the Pod restarts it and the main containers never start. That is the point: it is a gate.

  4. Delete the Pod and watch it not come back. Then compare with a Deployment in the next module, where it does.

What to Take Away

  • A Pod is a co-scheduling guarantee, not a container wrapper. That is why it exists.
  • Containers in a Pod share an IP, a hostname and localhost; containers in different Pods do not.
  • A bare Pod is unmanaged — nothing recreates it. Real workloads are owned by a Deployment, StatefulSet, DaemonSet or Job.
  • When something is wrong, the container state and the events tell you more than the phase does.