Understanding Pods

5 minute read

The Pod is the smallest deployable unit in Kubernetes. It’s not a container — it’s a wrapper around one or more containers that share the same network and storage. Every workload in Kubernetes runs inside a Pod.

What is a Pod?

A Pod represents a single instance of a running process in your cluster. It encapsulates:

  • One or more containers (usually one)
  • Shared networking — all containers in a Pod share the same IP address and port space
  • Shared storage — containers can mount the same volumes
  • Lifecycle — containers in a Pod are co-scheduled and co-located

Think of a Pod as a “logical host” — similar to a physical or virtual machine, but lighter.

┌─────────────── Pod ───────────────┐
│                                    │
│  ┌────────────┐  ┌────────────┐  │
│  │ Container  │  │ Container  │  │
│  │  (main)    │  │  (sidecar) │  │
│  └────────────┘  └────────────┘  │
│                                    │
│  Shared Network (localhost)        │
│  Shared Volumes                    │
│  IP: 10.244.0.5                    │
└────────────────────────────────────┘

Why Pods and Not Just Containers?

Kubernetes doesn’t run containers directly for several important reasons:

  1. Co-location — Some containers need to run together (e.g., a web server and a log shipper)
  2. Shared resources — Containers in a Pod can communicate via localhost and share files
  3. Atomic scheduling — K8s schedules the entire Pod as a unit, not individual containers
  4. Abstraction — Pods provide a runtime-agnostic interface (works with containerd, CRI-O, etc.)

Creating Your First Pod

Using kubectl (Imperative)

# Run a single Pod
kubectl run my-nginx --image=nginx --port=80

# Check the Pod
kubectl get pods

# Get detailed info
kubectl describe pod my-nginx

Using YAML (Declarative)

Create a file called pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  labels:
    app: nginx
    environment: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.27
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

Apply it:

kubectl apply -f pod.yaml

Pod YAML Anatomy

Let’s break down the key fields:

apiVersion: v1          # API version for Pods
kind: Pod               # Resource type
metadata:
  name: my-nginx        # Pod name (must be unique in the namespace)
  labels:               # Key-value pairs for organizing and selecting
    app: nginx
spec:
  containers:           # List of containers in this Pod
  - name: nginx         # Container name
    image: nginx:1.27   # Container image
    ports:
    - containerPort: 80 # Port the container listens on
    resources:
      requests:         # Minimum resources guaranteed
        memory: "64Mi"
        cpu: "250m"
      limits:           # Maximum resources allowed
        memory: "128Mi"
        cpu: "500m"

Pod Lifecycle

A Pod goes through several phases during its lifetime:

Phase Description
Pending Pod accepted by the cluster but containers not yet running. Could be waiting for scheduling or image pull.
Running Pod bound to a node. At least one container is running.
Succeeded All containers terminated successfully (exit code 0). Won’t restart.
Failed All containers terminated. At least one exited with a non-zero code.
Unknown Pod status can’t be determined (usually a node communication issue).

Check Pod status:

kubectl get pods
kubectl get pod my-nginx -o wide    # Shows node, IP, etc.

Working with Pods

View Pod logs

# View logs
kubectl logs my-nginx

# Follow logs in real-time
kubectl logs -f my-nginx

# View logs for a specific container (multi-container Pod)
kubectl logs my-nginx -c nginx

Execute commands inside a Pod

# Open a shell
kubectl exec -it my-nginx -- /bin/bash

# Run a single command
kubectl exec my-nginx -- ls /usr/share/nginx/html

# For multi-container Pods, specify the container
kubectl exec -it my-nginx -c nginx -- /bin/bash

Port forwarding

# Forward local port 8080 to Pod port 80
kubectl port-forward my-nginx 8080:80

# Now visit http://localhost:8080 in your browser

Delete a Pod

# Delete by name
kubectl delete pod my-nginx

# Delete using the YAML file
kubectl delete -f pod.yaml

Multi-Container Pods

While most Pods run a single container, there are valid patterns for running multiple containers in one Pod.

Sidecar Pattern

A helper container that enhances the main container:

apiVersion: v1
kind: Pod
metadata:
  name: web-with-logging
spec:
  containers:
  - name: web
    image: nginx:1.27
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  - name: log-shipper
    image: busybox
    command: ["sh", "-c", "tail -f /var/log/nginx/access.log"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  volumes:
  - name: shared-logs
    emptyDir: {}

Common Multi-Container Patterns

Pattern Use Case Example
Sidecar Extend main container functionality Log shipper, monitoring agent
Ambassador Proxy network connections Database proxy, API gateway
Adapter Transform output Log format converter, metric adapter

Init Containers

Init containers run before the main containers start. They’re used for setup tasks.

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
  - name: wait-for-db
    image: busybox
    command: ["sh", "-c", "until nc -z db-service 5432; do echo waiting for db; sleep 2; done"]
  containers:
  - name: app
    image: my-app:1.0

Init containers:

  • Run to completion before any main containers start
  • Run sequentially (one at a time)
  • If an init container fails, K8s restarts the Pod
  • Great for waiting on dependencies, database migrations, or config generation

Labels and Selectors

Labels are key-value pairs attached to Pods (and other objects) for organization and selection.

metadata:
  labels:
    app: nginx
    environment: production
    team: platform
    version: "1.27"

Query Pods using labels:

# Get Pods with a specific label
kubectl get pods -l app=nginx

# Get Pods with multiple labels (AND)
kubectl get pods -l app=nginx,environment=production

# Get Pods where environment is NOT production
kubectl get pods -l 'environment!=production'

# Get Pods with label selector expressions
kubectl get pods -l 'environment in (staging, production)'

Restart Policies

Pods have a restartPolicy that controls what happens when a container exits:

Policy Behavior
Always (default) Always restart the container, regardless of exit code
OnFailure Only restart if the container exits with a non-zero code
Never Never restart the container
spec:
  restartPolicy: OnFailure
  containers:
  - name: my-app
    image: my-app:1.0

Important: Pods Are Not Self-Healing

A critical thing to understand — Pods are ephemeral. If a Pod dies and it was created directly (not via a Deployment/ReplicaSet), it’s gone forever.

# This Pod will NOT be recreated if it fails or is deleted
kubectl run solo-pod --image=nginx

# This is why we use Deployments in production (covered in Chapter 7)
kubectl create deployment managed-nginx --image=nginx

Rule of thumb: Never create standalone Pods in production. Always use a controller (Deployment, StatefulSet, Job) that manages Pods for you.

Summary

  • A Pod is the smallest deployable unit in Kubernetes
  • Pods wrap one or more containers with shared networking and storage
  • Pods are ephemeral — use Deployments to manage them
  • Labels organize and select Pods
  • Init containers handle setup tasks before main containers start
  • Multi-container patterns: sidecar, ambassador, adapter

What’s Next?

Pods by themselves are fragile — if a Pod dies, nothing brings it back. In the next chapter, we’ll learn about ReplicaSets, which ensure a specified number of Pod replicas are running at all times.

Next: ReplicaSets →

Updated: