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: 15

The 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-app

Performing 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-app
Tip

Add 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=4

For 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-app

Deployment strategies compared

Kubernetes supports two deployment strategies natively:

StrategyHow it worksDowntimeWhen to use
RollingUpdateReplace pods incrementally, new alongside oldNone (with maxUnavailable: 0)Most production workloads
RecreateKill all old pods, then start all new podsYes — gap between old and newWhen 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 again

Common mistakes

  1. 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).
  2. 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.
  3. 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.
  4. 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.
  5. 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.

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.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.