Kubernetes Deployments: Managing Pod Replicas
A Kubernetes Deployment is how you tell the cluster to run a certain number of copies of your application and keep them running. It handles starting pods, maintaining the desired count, and rolling out new versions without downtime. Almost every production workload in AKS runs under a Deployment.
Why Deployments exist
You could run pods directly, but standalone pods have no self-healing. If the pod crashes or its node fails, it disappears permanently. The Deployment controller continuously monitors the running pod count and creates replacements whenever the actual count falls below the desired count.
Deployments also solve the update problem. When you need to ship a new container image, you want the old version to keep serving traffic until the new one is ready. Deployments do this automatically via rolling updates — replacing pods one group at a time.
Anatomy of a Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 4
selector:
matchLabels:
app: web-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web-app
version: "2.1.0"
spec:
containers:
- name: web-app
image: myregistry.azurecr.io/web-app:2.1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 15The strategy section controls how updates happen:
maxSurge: 1— allow 1 extra pod above the desired 4 during an update (so up to 5 pods temporarily)maxUnavailable: 0— never take a pod down until a replacement is ready. This guarantees zero downtime but requires extra capacity.
Creating and inspecting a Deployment
# Apply the deployment YAML
kubectl apply -f web-app-deployment.yaml
# Watch pods come up
kubectl get pods -l app=web-app --watch
# Check deployment status
kubectl get deployment web-app
# Detailed status showing ready/available counts
kubectl rollout status deployment/web-app
# Waiting for deployment "web-app" rollout to finish: 0 of 4 updated replicas are available...
# deployment "web-app" successfully rolled out
# See the ReplicaSet the Deployment created
kubectl get replicasets -l app=web-appPerforming a rolling update
The most common operation on a Deployment is changing the container image to deploy a new version. You have several ways to do this:
# Option 1: Update the image via kubectl
kubectl set image deployment/web-app web-app=myregistry.azurecr.io/web-app:2.2.0
# Option 2: Edit the Deployment directly (opens in $EDITOR)
kubectl edit deployment web-app
# Option 3: Update the YAML file and re-apply (preferred for GitOps)
# Change image in web-app-deployment.yaml, then:
kubectl apply -f web-app-deployment.yaml
# Watch the rollout in real time
kubectl rollout status deployment/web-app
# See what changed
kubectl get replicasets -l app=web-app
# NAME DESIRED CURRENT READY AGE
# web-app-7d4f9b7c8d 4 4 4 5m ← new
# web-app-6c3e8a6b7c 0 0 0 2h ← old (kept for rollback)After the update, Kubernetes keeps the old ReplicaSet around with 0 replicas. This is intentional — it stores the previous pod template so you can roll back instantly without re-downloading images.
Rolling back a bad deployment
Suppose you deployed version 2.2.0 and it has a bug. Users are reporting errors. Roll back immediately:
# Instant rollback to previous version
kubectl rollout undo deployment/web-app
# Check rollout history to see available revisions
kubectl rollout history deployment/web-app
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 kubectl set image deployment/web-app...
# 3 kubectl set image deployment/web-app...
# Roll back to a specific revision
kubectl rollout undo deployment/web-app --to-revision=1
# Verify the rollback completed
kubectl rollout status deployment/web-app
kubectl get pods -l app=web-appAdd change cause annotations when deploying so your history is readable: kubectl annotate deployment web-app kubernetes.io/change-cause=“Deploy version 2.2.0 with payment fix”. Without this, the CHANGE-CAUSE column shows “none” and history becomes difficult to understand.
Scaling a Deployment
Scale manually to handle a known traffic spike:
# Scale to 10 replicas
kubectl scale deployment web-app --replicas=10
# Watch pods spin up
kubectl get pods -l app=web-app -w
# Scale back down
kubectl scale deployment web-app --replicas=4For automatic scaling based on CPU or memory, the Horizontal Pod Autoscaler (HPA) integrates directly with Deployments. See the HPA page for details.
Pausing and resuming a rollout
When rolling out a complex change, you might want to pause at a partial update to observe the new pods before proceeding. This is a canary-style pattern using native Deployment controls:
# Pause the rollout after starting it
kubectl rollout pause deployment/web-app
# At this point, some pods are on new version, some on old
# Check that the new pods are behaving correctly
kubectl logs -l app=web-app,version=2.2.0
# If all is well, resume
kubectl rollout resume deployment/web-app
# If something is wrong, roll back
kubectl rollout undo deployment/web-appDeployment strategies compared
Kubernetes supports two deployment strategies natively:
| Strategy | How it works | Downtime | When to use |
|---|---|---|---|
| RollingUpdate | Replace pods incrementally, new alongside old | None (with maxUnavailable: 0) | Most production workloads |
| Recreate | Kill all old pods, then start all new pods | Yes — gap between old and new | When old and new cannot coexist (e.g., DB schema changes) |
For true zero-downtime with fine-grained traffic control (e.g., sending 5% of traffic to the new version), you need either an Ingress controller with traffic splitting or a service mesh. Native Deployments do not do traffic-based canary splitting — they do pod-count-based splitting (if 1 of 10 pods is new, roughly 10% of requests hit it).
How readiness probes affect rollouts
Your readiness probe configuration directly controls how a rolling update proceeds. During a rollout, Kubernetes only marks a new pod as Available (and proceeds to replace the next old pod) once the new pod passes its readiness probe.
If the new version has a bug and the readiness probe fails, the rollout halts. Old pods keep running. You can then roll back safely with full traffic on the working version.
This is the automatic safety net. But it only works if your readiness probe actually tests something meaningful — not just checking if the process is running, but checking if it can serve requests.
readinessProbe:
httpGet:
path: /api/health # Should check DB connectivity too
port: 8080
initialDelaySeconds: 10 # Give app time to start
periodSeconds: 5 # Check every 5 seconds
failureThreshold: 3 # 3 failures before marking unready
successThreshold: 1 # 1 success after failure to mark ready againCommon mistakes
- Using the latest tag for container images. If you deploy
image: my-app:latest, Kubernetes cannot detect a change between deployments and may not pull the new image. Always use a specific version tag tied to your CI build (e.g., a git SHA or semantic version). - Setting maxUnavailable to a high number. maxUnavailable: 50% means half your pods can be down simultaneously during an update. For high-traffic services, keep maxUnavailable low (1 or 0) and use maxSurge for extra capacity.
- No readiness probe. Without a readiness probe, Kubernetes considers a pod ready as soon as the container starts. If your app takes time to initialize, it receives traffic before it is ready, causing errors during rollouts.
- Editing pods directly instead of the Deployment. If you change a pod’s spec directly with
kubectl edit pod, the Deployment will overwrite your change at the next reconciliation. Always edit the Deployment spec. - Ignoring rollout history. Kubernetes keeps only 10 revisions by default (controlled by
revisionHistoryLimit). For important deployments, add annotation-based change causes so older revisions are understandable if you need to roll back.
Summary
- Deployments manage pod replicas and add rolling update and rollback capabilities on top of ReplicaSets.
- Use
kubectl set imageor update the YAML and re-apply to trigger a rolling update. Usekubectl rollout undoto roll back. - The strategy settings
maxSurgeandmaxUnavailablecontrol how many pods can be temporarily added or removed during updates. - Readiness probes are the safety net for rolling updates — a failing probe halts the rollout and keeps old pods serving traffic.
- Always use specific image tags, not
:latest, so Kubernetes can detect version changes and so you can trace what is deployed.
Frequently asked questions
What is the difference between a Deployment and a ReplicaSet?
A ReplicaSet ensures a specified number of pod replicas are running. A Deployment manages ReplicaSets and adds versioning — when you update a Deployment, it creates a new ReplicaSet and gradually shifts pods from old to new. Use Deployments, not ReplicaSets directly.
How do I roll back a bad deployment?
Run kubectl rollout undo deployment/my-app. This reverts to the previous ReplicaSet. You can also roll back to a specific revision with --to-revision=2.
How does Kubernetes ensure zero downtime during a rolling update?
The maxUnavailable setting controls how many pods can be down at once. The maxSurge setting controls how many extra pods can be created above the desired count. By default both are 25%, so Kubernetes keeps most replicas running during updates.