Foundations workloads

Pods — The Atomic Unit

● Beginner ⏱ 12 min read workloads

A Pod is the smallest deployable unit in Kubernetes. It represents a single instance of a running process in your cluster. A pod encapsulates one or more containers, shared storage (volumes), a unique network IP, and options that govern how the containers run. Understanding pods is the foundation for everything else in Kubernetes.

📚
Official Reference

Based on kubernetes.io/docs/concepts/workloads/pods/.

What Is a Pod?

While Docker containers are the unit you build and ship, pods are the unit Kubernetes schedules and manages. A pod always runs on a single node. All containers in a pod:

In practice, most pods contain a single container. Multi-container pods are used for tightly coupled helper processes (sidecar, ambassador, adapter patterns).

Pod Spec

Here is a minimal pod manifest:

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

Key fields:

FieldDescription
apiVersion: v1Pods are in the core API group
metadata.nameUnique name within a namespace
metadata.labelsKey-value pairs used by selectors
spec.containers[].imageContainer image (always pin a specific tag)
spec.containers[].resourcesCPU/memory requests and limits
⚠️
Don't run raw Pods in production

Pods created directly have no self-healing. If the node fails, the pod is lost. Always use a higher-level workload object: Deployment for stateless apps, StatefulSet for stateful apps, DaemonSet for per-node agents, or Job for batch tasks.

Multi-Container Patterns

When multiple containers in a pod are tightly coupled, they should live together. Three classical patterns:

Sidecar

A helper container that extends or enhances the main container without modifying it. Example: a Fluent Bit container that reads logs from a shared volume and ships them to Elasticsearch, alongside an nginx container.

Ambassador

A proxy container that handles outbound connections on behalf of the main container. Example: a proxy container that automatically retries failed API calls or handles service discovery, so the main app just talks to localhost:8080.

Adapter

Transforms output from the main container into a format expected by external systems. Example: a container that converts the main app's metrics from a custom format into Prometheus format.

Pod Lifecycle

A pod's status.phase field represents where it is in its lifecycle:

PhaseMeaning
PendingPod accepted by the cluster but not yet running. Containers are being scheduled or image is being pulled.
RunningPod bound to a node and at least one container is running (or starting/restarting).
SucceededAll containers in the pod have terminated successfully and will not be restarted.
FailedAll containers have terminated, and at least one exited with a non-zero code or was killed by the system.
UnknownPod state cannot be determined, usually due to a communication error with the node.

Check pod status with:

kubectl get pod nginx-pod
kubectl describe pod nginx-pod   # full status + events

Restart Policies

The spec.restartPolicy field controls what happens when a container in the pod exits. Options:

PolicyBehaviourUse case
AlwaysRestart the container whenever it exits (default)Long-running services (web servers, APIs)
OnFailureRestart only if container exits with non-zero codeBatch jobs that should retry on failure
NeverNever restart regardless of exit codeOne-shot tasks, debugging

Restart attempts use exponential back-off: 10s, 20s, 40s, ... up to 5 minutes, then reset after 10 minutes of success. A pod that keeps restarting shows CrashLoopBackOff.

Resource Requests & Limits

Every container should declare its resource requirements. Kubernetes uses these values for two distinct purposes:

resources:
  requests:
    memory: "128Mi"
    cpu: "250m"    # 250 millicores = 0.25 of one CPU core
  limits:
    memory: "256Mi"
    cpu: "500m"
💡
CPU vs Memory limits behaviour

CPU is compressible: exceeding the limit throttles the container but doesn't kill it. Memory is incompressible: exceeding the limit kills the container with OOMKilled. Set memory limits carefully — too low and your app gets killed unexpectedly; too high and you waste cluster resources.