Skip to main content

Pod

Any running application in Kubernetes is called a workload. We run these workloads (simple or complex) inside pod(s).

The textbook definition of a pod is:

In Kubernetes, a Pod is the smallest and simplest unit of deployment.

A pod can contain one or more containers that share the same network namespace and storage volumes, and a specification for how to run the containers.

I do not understand anything from this. So, let us try to dig a bit and understand what it means actually by comparing it with Docker.

Understanding Pods - Beyond Containers

Before diving into Pod lifecycle, let's build an intuition about what a Pod actually is and why Kubernetes created this concept. To do this, we need to understand containers first, and then see why Kubernetes chose a different approach.

The Container Problem

When Docker introduced containers, it popularized the philosophy of "one process per container." The idea is simple:

  • Each container runs a single service or process
  • Containers are isolated from each other
  • Each container has its own filesystem, network stack, and process ID space
  • To communicate, containers need to use the network or volume mounts

This works great for many scenarios, but it has a limitation. In the real world, applications are rarely that simple. Consider a web application:

  • You need a main web server (nginx)
  • You need a logging service that collects logs
  • You might need a monitoring agent
  • You might need a configuration helper

With Docker containers, each of these would be a separate container. Communicating between them becomes complex:

  • They need to find each other over the network (service discovery)
  • They need to map ports and manage network configurations
  • They can't easily share memory or other IPC mechanisms
  • Managing their lifecycle together becomes cumbersome

Why Kubernetes Introduced Pods

Kubernetes took a different approach. Instead of just wrapping a single process, Kubernetes created Pods - which are groups of containers that work together as a unit.

Think of a Pod like this: A Pod is a lightweight virtual machine that can run multiple related containers together.

Why is this useful? Consider our web server scenario again:

  • With containers: You'd need to run nginx in one container, logging in another, and orchestrate them separately
  • With Pods: You run nginx and the logging service in the same Pod, and Kubernetes treats them as a single unit

What Happens Under the Hood

This is where it gets interesting. Kubernetes doesn't just randomly group containers together. Here's what actually happens:

  1. The Pause Container: When Kubernetes creates a Pod, it first creates a special "sandbox" container called the pause container. This pause container's only job is to do nothing - it just sits there and holds some network resources.

vagrant@vagrant:~$ ps auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ... root 2809 0.4 0.2 1235096 10988 ? Sl 16:25 0:02 /usr/bin/containerd-shim-runc-v2 -namespace moby -id root 2833 1.5 0.2 20196 11764 ? Ss 16:25 0:10 _ /sbin/init root 7916 1.1 0.2 1233840 11004 ? Sl 16:35 0:00 _ /usr/bin/containerd-shim-runc-v2 -namespace m 65535 7956 1.1 0.0 1020 728 ? Ss 16:35 0:00 | _ /pause root 8112 1.6 0.2 1233840 11460 ? Sl 16:35 0:00 _ /usr/bin/containerd-shim-runc-v2 -namespace m root 8136 2.5 0.2 14884 8876 ? Ss 16:35 0:01 _ nginx: master process nginx -g daemon off message+ 8208 0.0 0.0 15344 3804 ? S 16:35 0:00 _ nginx: worker process message+ 8209 0.0 0.0 15344 3804 ? S 16:35 0:00 _ nginx: worker process message+ 8210 0.0 0.0 15344 3728 ? S 16:35 0:00 _ nginx: worker process

Based on the start time and the parent process, we can say that the 'pause' container is created within 'nginx' pod.

  1. Shared Namespaces: The pause container creates and maintains three critical namespaces that all other containers in the Pod share:

    • Network namespace (net): All containers see the same IP address and can talk to each other via localhost
    • IPC namespace: Containers can communicate using shared memory and message queues (Inter-Process Communication)
    • UTS namespace: All containers share the same hostname

    Let's check the pause container's namespace (from the output above, PID of pause container is 7956):

    vagrant@vagrant:~$ ls -l /proc/7956/ns total 0 lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 cgroup -> 'cgroup:[4026532402]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 ipc -> 'ipc:[4026532400]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 mnt -> 'mnt:[4026532395]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 net -> 'net:[4026532403]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 pid -> 'pid:[4026532401]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:47 pid_for_children -> 'pid:[4026532401]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 time -> 'time:[4026532276]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:47 time_for_children -> 'time:[4026532276]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 user -> 'user:[4026531837]' lrwxrwxrwx 1 65535 65535 0 Jun 4 16:44 uts -> 'uts:[4026532399]'

    The nginx container's namespaces (PID 8136):

    vagrant@vagrant:~$ sudo ls -l /proc/8136/ns total 0 lrwxrwxrwx 1 root root 0 Jun 4 16:44 cgroup -> 'cgroup:[4026532463]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 ipc -> 'ipc:[4026532400]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 mnt -> 'mnt:[4026532460]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 net -> 'net:[4026532403]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 pid -> 'pid:[4026532462]' lrwxrwxrwx 1 root root 0 Jun 4 16:49 pid_for_children -> 'pid:[4026532462]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 time -> 'time:[4026532276]' lrwxrwxrwx 1 root root 0 Jun 4 16:49 time_for_children -> 'time:[4026532276]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 user -> 'user:[4026531837]' lrwxrwxrwx 1 root root 0 Jun 4 16:44 uts -> 'uts:[4026532461]'

    From the above outputs, we can see that the both pause and nginx containers are sharing the ipc (4026532400), net (4026532403), time (4026532276), and user (4026532276) namespaces

  2. Isolated Namespaces: Each container still gets its own:

    • Mount namespace (mnt): Isolated filesystems
    • PID namespace: Each container sees only its own processes

    It is evident from the above outputs that the mnt and the pid namespaces are different for pause and nginx containers.

  3. Resource Sharing: All containers in a Pod are scheduled on the same node (machine) and share the same cgroups hierarchy for resource limits.

Let me visualize this:

┌─────────────────────────────────────────────────────────┐
│  Pod (Single Network Interface, Single IP)              │
│                                                         │
│  ┌──────────────────────────────────────────────────┐   │
│  │ Pause Container (holds net, ipc, uts namespaces) │   │
│  └──────────────────────────────────────────────────┘   │
│           ↑                               ↑             │
│           │ (shares)                      │ (shares)    │
│           │                               │             │
│  ┌────────▼──────────┐          ┌────────▼──────────┐   │
│  │  Container A      │          │  Container B      │   │
│  │  (nginx)          │          │  (logging)        │   │
│  │                   │          │                   │   │
│  │ Isolated Mount NS │          │ Isolated Mount NS │   │
│  │ Isolated PID NS   │          │ Isolated PID NS   │   │
│  │                   │<────────>│                   │   │
│  │ Can talk via      │ via      │ Can talk via      │   │
│  │ localhost:8080    │localhost │ localhost:8080    │   │
│  └───────────────────┘          └───────────────────┘   │
│                                                         │
└─────────────────────────────────────────────────────────┘

Advantages of the Pod Abstraction

Now you can see why Kubernetes created Pods:

1. Localhost Communication

# Inside Container A (nginx)
curl localhost:8080    # Talks to Container B running on port 8080. No network setup needed!

2. Shared Memory & IPC

Containers can communicate via shared memory segments, message queues, and other IPC mechanisms - just like processes on a single machine would.

3. Unified Lifecycle

Pods are created and destroyed together. If one container crashes, Kubernetes restarts the entire Pod. This is simpler than managing multiple independent containers.

4. Familiar Deployment Patterns

Pods bring back familiar patterns from virtual machine deployments:

  • Sidecar Pattern: A logging container alongside your main container
  • Ambassador Pattern: A proxy container that handles external communication
  • Adapter Pattern: A helper container that transforms data

5. Simplified Networking

  • Each Pod gets a single IP address
  • All containers in a Pod can expose the same ports
  • Service discovery is simpler (you discover Pods, not individual containers)

A Real-World Example

Let's say you're running a web application with logging:

With Containers (Traditional Docker approach):

Container 1: Web Server (nginx)
├─ Runs on port 8080
├─ Writes logs to stdout

Container 2: Log Collector (fluentd)
├─ Needs to somehow read logs from Container 1
├─ Needs its own port
├─ Must communicate over network
├─ Separate lifecycle management

This requires:

  • Network configuration between containers
  • Shared volumes or log forwarding setup
  • Managing both containers independently
  • If Container 2 crashes, Container 1 keeps running (inconsistent state)

With Pods (Kubernetes approach):

Pod
├─ Container 1: Web Server (nginx) - localhost:8080
├─ Container 2: Log Collector (fluentd) - localhost:24224
├─ Single shared network interface
├─ Both containers see the same hostname
├─ Can communicate via localhost
├─ Share the same IP address
├─ Managed as a single unit

This provides:

  • No network setup needed
  • Direct communication via localhost
  • Shared namespace for IPC
  • Unified lifecycle (both start/stop together)
  • Simpler deployment and management

Why You Can't Run Containers Directly in Kubernetes

Now you understand why: Kubernetes needs an abstraction layer that mimics a VM-like environment where multiple processes can run together naturally.

If Kubernetes only supported single containers:

  • Every app would need to be restructured to run a single process per container
  • You'd lose the ability to use sidecar containers and other patterns
  • Every container would need its own IP and port mapping
  • You couldn't efficiently use shared memory or IPC
  • The deployment model would feel completely foreign to teams with VM experience

Pods solve this by providing a familiar deployment unit that acts like a lightweight virtual machine - but with the efficiency of containers.

Key Takeaway

A Pod is not just a group of containers - it's a container runtime concept. Under the hood:

  • A pause container establishes shared namespaces (network, IPC, hostname)
  • Application containers reuse these namespaces
  • Each container still has isolated filesystems and process IDs
  • The result feels like a lightweight VM but runs with container efficiency

This is why Pods are the atomic unit of deployment in Kubernetes, not individual containers.

Pod Lifecycle

A Pod goes through several phases during its lifecycle:

  1. Pending: The Pod has been accepted by the Kubernetes system, but one or more of the containers have not been created. This can happen when the scheduler is still trying to find a suitable node for the Pod.
  2. Running: The Pod has been bound to a node, and all of the containers have been created. At least one container is still running.
  3. Succeeded: All containers in the Pod have terminated successfully, and the Pod will not be restarted.
  4. Failed: All containers in the Pod have terminated, and at least one container has terminated with a failure (non-zero exit code).
  5. Unknown: The state of the Pod cannot be determined, typically due to an error in communicating with the node where the Pod is running.
  6. Terminating: The Pod is in the process of being deleted. It is still visible in the system, but it is not running any containers.
  7. CrashLoopBackOff: This is a common state for Pods that are crashing repeatedly. It indicates that the container is starting, crashing, and then being restarted by Kubernetes.
  8. ImagePullBackOff: This state indicates that Kubernetes is having trouble pulling the container image from the registry. It could be due to an incorrect image name, lack of permissions, or network issues.
  9. Completed: This state indicates that the Pod has completed its execution successfully, and all containers have terminated with a zero exit code.
  10. Error: This state indicates that the Pod has encountered an error during its lifecycle, such as a failure to start or a crash.
  11. Evicted: This state indicates that the Pod has been evicted from its node due to resource constraints, such as insufficient memory or disk space.
  12. Terminated: This state indicates that the Pod has been terminated, either by the user or by Kubernetes, and is no longer running.