Understanding Pods
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:
- Co-location — Some containers need to run together (e.g., a web server and a log shipper)
- Shared resources — Containers in a Pod can communicate via
localhostand share files - Atomic scheduling — K8s schedules the entire Pod as a unit, not individual containers
- 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.