Pods and Deployments¶
Assumes: the reconciliation model from the Overview, and a cluster you can run commands against - see Get a Cluster to Practice On.
A Pod is the smallest thing Kubernetes runs - one or more containers scheduled together on one node. A Deployment is what keeps a set of identical Pods running and updates them safely. In the vocabulary of the Overview: Pods are the runtime unit, Deployments are the lifecycle controller.
If you remember one rule, remember this: in production, you almost never run standalone pods. You run pods through a controller, usually a Deployment.
Pod fundamentals¶
A pod is one or more containers that share:
- one network namespace - meaning all containers in the pod share a single IP address and can reach each other on
localhost, exactly like processes on one machine - declared storage volumes
- one scheduling decision - the whole pod lands on one node, all or nothing
Most pods should contain one main application container. Add sidecars (extra helper containers that run alongside your app in the same pod) only when they provide clear value, such as logging, proxying, or telemetry.
Pod lifecycle¶
A pod moves through several phases during its lifetime:
stateDiagram-v2
[*] --> Pending : created
Pending --> Running : scheduled and containers start
Running --> Succeeded : all containers exit 0, no more restarts
Running --> Failed : terminated in failure, no more restarts
Running --> Unknown : node communication lost
- Pending: accepted by the API server but not yet scheduled, or images not yet pulled.
- Running: at least one container is running or starting.
- Succeeded: all containers exited successfully and will not be restarted (common for Jobs).
- Failed: all containers terminated and at least one failed, with no further restarts (only possible when
restartPolicyisNeverorOnFailureretries are exhausted). - Unknown: the node cannot be reached.
Running does not mean healthy
With restartPolicy: Always (the default, and what Deployments use), a crashing container never moves the pod to Failed. The pod phase stays Running while the kubelet restarts the container with exponential backoff - that's what CrashLoopBackOff is. It appears in the container state, not the pod phase. This is why kubectl get pods can show Running for a pod that is actually broken; check the READY column and container statuses, not just the phase.
Phase is deliberately coarse. The finer-grained truth lives in pod conditions (PodScheduled, Initialized, ContainersReady, Ready) and per-container states (Waiting, Running, Terminated), all visible in kubectl describe pod.
Why pods alone are not enough¶
Pods are disposable. They can disappear during node failure, eviction, rescheduling, or rollout. A naked pod does not self-heal.
That is why controllers exist.
What Deployments do¶
A Deployment manages a ReplicaSet, and the ReplicaSet manages pods.
Deployment responsibilities:
- keep the desired replica count running
- perform rolling updates
- support rollback to prior revisions
- expose rollout status and history
graph TD
A[Apply Deployment] --> B[Deployment controller]
B --> C[ReplicaSet]
C --> D[Pods]
D --> E[Node failure or pod crash]
E --> C
Example Deployment¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: ghcr.io/example/web:v1.2.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
Rolling update behavior¶
Here is what actually happens during an update. A Deployment never modifies pods in place - it creates a new ReplicaSet for the new pod template and scales the two ReplicaSets in opposite directions:
graph LR
D[Deployment v2] --> RS1[ReplicaSet v1<br/>3 → 2 → 1 → 0]
D --> RS2[ReplicaSet v2<br/>0 → 1 → 2 → 3]
Each step waits for new pods to become ready (readiness probe passing) before continuing. The old ReplicaSet is kept at zero replicas afterward - that retained history is what makes kubectl rollout undo instant: rollback is just scaling the previous ReplicaSet back up. revisionHistoryLimit (default 10) controls how many old ReplicaSets are kept.
Key controls:
maxUnavailable: how many old pods can be unavailable during rolloutmaxSurge: how many extra new pods can be created temporarily
With maxSurge: 1, maxUnavailable: 0, capacity never drops below the desired count - the safest setting for latency-critical services, at the cost of needing headroom for one extra pod.
Two consequences worth knowing:
- A rollout with a failing readiness probe stalls rather than fails. The Deployment waits (
progressDeadlineSeconds, default 600s) before marking the rollout as not progressing. Old pods keep serving - this is the safety net working as designed. - Only changes to
spec.templatetrigger a rollout. Scaling replicas or editing labels on the Deployment itself does not create a new ReplicaSet. Notably, changing a ConfigMap that pods reference does not restart them - usekubectl rollout restartor a checksum annotation for that.
Standalone pod use cases¶
Standalone pods are still useful for short-lived debugging:
Do not use this pattern for long-running applications.
Graceful shutdown¶
When a pod is deleted, Kubernetes sends SIGTERM to containers and waits terminationGracePeriodSeconds (default 30s) before sending SIGKILL. Use the preStop lifecycle hook to drain connections cleanly before the signal arrives, especially for servers that need time to finish in-flight requests.
If your application needs more than 30 seconds to shut down, increase terminationGracePeriodSeconds.
Common mistakes¶
- Deploying applications with
kubectl runand no controller - Missing
resources.requests, which hurts scheduling quality - Missing readiness probes, which can route traffic to not-ready pods
- Using mutable image tags like
latestin production - Setting
imagePullPolicy: Alwayswithout understanding it pulls on every pod start, even for immutable tags - Not setting
terminationGracePeriodSecondslong enough for slow-draining services
Quick operations checklist¶
kubectl get deploy,rs,pods -l app=web
kubectl rollout status deploy/web
kubectl rollout history deploy/web
kubectl rollout undo deploy/web
Certification notes¶
- CKAD expects fast Deployment creation:
kubectl create deployment web --image=nginx --replicas=3 --dry-run=client -o yamlgives you a skeleton to edit. - Know the rollout command family cold:
rollout status,rollout history,rollout undo [--to-revision=N],rollout restart. - Understand why a pod shows
Runningbut0/1 READY- readiness probe failure is the most common scenario-question answer.
Summary¶
Pods execute containers. Deployments enforce application availability and safe change management through ReplicaSet versioning. For production services, Deployment should be the default starting point.
Check Yourself¶
kubectl get pods shows Running and 0/1 in the READY column. What is happening, and what would you look at?
The container process is up, but the readiness probe is failing, so the pod is deliberately kept out of Service endpoints - no traffic reaches it. Phase says nothing about health. Look at kubectl describe pod for probe failure events and container state, then at the app's own logs.
You edit a ConfigMap that your Deployment's pods mount as environment variables. Why do the pods keep serving the old values?
Only changes to spec.template trigger a rollout. Editing a separate ConfigMap object does not touch the Deployment's pod template, so no new ReplicaSet is created and no pod is replaced. Environment variables are read once at container start. Use kubectl rollout restart or a checksum annotation on the pod template to force the replacement.
A rollout with a broken readiness probe has been sitting at 1 of 3 new pods for five minutes. Is the old version still serving traffic?
Yes, and that is the design working. The Deployment scales the new ReplicaSet up only as new pods become ready, so the old ReplicaSet is still up and in the Service. The rollout stalls rather than fails, and after progressDeadlineSeconds (default 600s) it is marked as not progressing. kubectl rollout undo scales the old ReplicaSet back to full.
Why is kubectl rollout undo almost instantaneous when a fresh deploy takes a minute?
Because rollback is not a deploy. The previous ReplicaSet object still exists at zero replicas (revisionHistoryLimit, default 10, controls how many are kept), so undo just scales it back up - the pod template, and usually the image, are already known and cached on the nodes.
You run kubectl run debug --image=busybox and the container exits. Why is that different from a pod created by a Deployment?
Nothing is watching it. A standalone pod has no controller, so when it dies or its node fails there is no reconciliation loop noticing the gap and recreating it. Deployments exist precisely to close that gap, which is why standalone pods belong to debugging only.
Related Concepts¶
Read first:
- Learning Paths - where this page sits in your track
- Kubernetes Overview - the reconciliation model behind Deployments
- Get a Cluster to Practice On - so you can run everything above
Go deeper:
- Health Probes for readiness and liveness, the next page in the Beginner track
- Init Containers for startup dependencies
- Jobs and CronJobs for batch and scheduled workloads
- StatefulSets for stateful applications
- Horizontal Pod Autoscaling for dynamic scaling
- Scheduling and Placement for where pods land
Further Reading¶
Beginner track - step 3 of 12. Next: Health Probes. Back to the track overview.