| |

LFCE 139 ๐Ÿง Kubernetes โ€” Deployments

โฑ๏ธ Estimated read: 11 min | |

A Deployment is the standard way to run a stateless application on Kubernetes. It does not manage Pods directly. It manages a ReplicaSet, and the ReplicaSet manages the Pods. The Deployment declares the desired stateโ€”the container image, the number of replicas, the resource limits, and the update strategyโ€”and the Deployment controller continuously reconciles the actual state to match it . When the image changes, the Deployment creates a new ReplicaSet and gradually shifts traffic from the old Pods to the new ones, without downtime. When something goes wrong, it can roll back to the previous ReplicaSet in seconds.

This chapter covers the Deployment abstraction: the hierarchy of Deployment โ†’ ReplicaSet โ†’ Pod, the rolling update strategy and its two parameters (maxSurge and maxUnavailable), the rollback mechanism that makes a bad release recoverable, and the scaling and self-healing behavior that makes a Deployment the production default for stateless workloads.

Key point: Never create a bare Pod in production. A Pod that is deleted or whose node fails is not recreated. A Deployment-managed Pod is replaced automatically by the controller, and the Deployment provides rolling updates and rollbacks that a bare Pod cannot.


Why Deployments exist

The bare Pod problem. A Pod is ephemeral. If it is deleted, if its node fails, or if the container crashes in a way that the kubelet cannot restart, the Pod is gone and nothing brings it back . A Deployment creates a ReplicaSet, and the ReplicaSet watches the Pod count. When a Pod disappears, the ReplicaSet creates a replacement. The Deployment adds the update strategy on top: when the desired state changes, the transition from the old state to the new state is controlled, observable, and reversible.

The update problem. Replacing a running application with a new version requires stopping the old containers and starting the new ones. If all old containers are stopped first, the application is unavailable during the transition. A Deployment’s default strategy is RollingUpdate, which starts new Pods before stopping old ones, keeps a minimum number available, and only retires an old Pod when its replacement passes the readiness probe . The result is zero-downtime deployment.

The recovery problem. A new version can be broken. A container can crash-loop, a readiness probe can fail, or a configuration change can cause errors. The Deployment retains a history of previous ReplicaSets, and kubectl rollout undo scales the previous ReplicaSet back up while scaling the current one down. The rollback is near-instant because the previous ReplicaSet and its Pod template already exist .


a. The Deployment hierarchy and the manifest

The hierarchy is Deployment โ†’ ReplicaSet โ†’ Pod. The Deployment owns one or more ReplicaSets, and each ReplicaSet owns a set of Pods. During a rolling update, two ReplicaSets coexist: the old one being scaled down and the new one being scaled up .

A minimal Deployment manifest declares the image, the replica count, and the selector that matches the Pod template’s labels.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-backend
  template:
    metadata:
      labels:
        app: api-backend
    spec:
      containers:
        - name: api
          image: my-registry/api:v1.2.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5

The selector.matchLabels must match template.metadata.labels. If the selector does not match the Pod template, the Deployment will not manage the Pods, and the failure is silent . The readiness probe is what makes a rolling update safe: the Deployment does not count a new Pod as available until the probe succeeds, so traffic is not routed to a container that is not ready .


b. The rolling update strategy

The default strategy is RollingUpdate. It is controlled by two parameters that define the update’s concurrency and availability floor.

maxUnavailable is the maximum number of Pods that can be unavailable during the update. It defaults to 25%. maxSurge is the maximum number of additional Pods that can be created above the desired replica count. It also defaults to 25% .

For a Deployment with four replicas and the default 25%, maxUnavailable rounds down to 1, and maxSurge rounds up to 1. The controller ensures at least three Pods are available while allowing up to five Pods to exist during the transition . The update proceeds by scaling the new ReplicaSet up and the old ReplicaSet down, one Pod at a time within those bounds. An old Pod is not terminated until its replacement is ready.

The alternative strategy is Recreate, which terminates all existing Pods before creating new ones. It is appropriate only when the application cannot run two versions simultaneously, such as a schema migration that is not backward-compatible. It causes downtime .

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1

c. Rollback, history, and scaling

The Deployment retains a configurable number of old ReplicaSets, controlled by revisionHistoryLimit (default 10) . Each ReplicaSet corresponds to a revision in kubectl rollout history. The CHANGE-CAUSE column is populated only if the kubernetes.io/change-cause annotation is set; the old --record flag is deprecated .

kubectl rollout undo reverts to the previous revision. --to-revision=N reverts to a specific revision. The rollback scales the old ReplicaSet back up and the current one down. It is near-instant because the old ReplicaSet already exists . The rollback restores the Pod template only. It does not revert ConfigMaps, Secrets, database schema changes, or external state .

Scaling is done through the Deployment, not the ReplicaSet. kubectl scale deployment/api-backend --replicas=5 changes the replica count, and the Deployment passes the change to the ReplicaSet . Changing spec.replicas in the manifest and reapplying it is the declarative approach, which is preferred for version control . A HorizontalPodAutoscaler can adjust the replica count automatically based on CPU or memory utilization, but it requires resource requests to be defined in the manifest .


Complete Example Session

# ============================================
# PART 1: THE DEPLOYMENT MANIFEST
# ============================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-backend
  template:
    metadata:
      labels:
        app: api-backend
    spec:
      containers:
        - name: api
          image: my-registry/api:v1.2.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
# ============================================
# PART 2: APPLY AND OBSERVE
# ============================================
kubectl apply -f deployment.yaml
kubectl get deployments
# NAME          READY   UP-TO-DATE   AVAILABLE
# api-backend   3/3     3            3

kubectl get rs
# NAME                     DESIRED   CURRENT   READY
# api-backend-7d8f9c6b5    3         3         3
# ============================================
# PART 3: THE HIERARCHY
# ============================================
kubectl get deploy,rs,pods -l app=api-backend

# Deployment โ†’ ReplicaSet โ†’ Pods
# The Deployment owns the ReplicaSet.
# The ReplicaSet owns the Pods.
# ============================================
# PART 4: TRIGGER A ROLLING UPDATE
# ============================================
kubectl set image deployment/api-backend api=my-registry/api:v1.3.0

# The Deployment creates a new ReplicaSet.
# The old ReplicaSet is scaled down gradually.
# The new ReplicaSet is scaled up gradually.
# ============================================
# PART 5: TRACK THE ROLLOUT
# ============================================
kubectl rollout status deployment/api-backend

# Waiting for deployment "api-backend" rollout to finish:
# 1 out of 3 new replicas have been updated...
# deployment "api-backend" successfully rolled out
# ============================================
# PART 6: ROLLBACK
# ============================================
kubectl rollout undo deployment/api-backend

# The previous ReplicaSet is scaled back up.
# The current ReplicaSet is scaled down.
# ============================================
# PART 7: HISTORY
# ============================================
kubectl rollout history deployment/api-backend

# REVISION  CHANGE-CAUSE
# 1         <none>
# 2         <none>
# 3         <none>
# ============================================
# PART 8: SCALE
# ============================================
kubectl scale deployment/api-backend --replicas=5
kubectl get deployment api-backend
# READY   UP-TO-DATE   AVAILABLE
# 5/5     5            5
# ============================================
# PART 9: PAUSE AND RESUME
# ============================================
kubectl rollout pause deployment/api-backend
kubectl set image deployment/api-backend api=my-registry/api:v1.4.0
kubectl rollout resume deployment/api-backend
# ============================================
# PART 10: THE ROLLOUT COMMANDS
# ============================================
# status   track progress
# history  view revisions
# undo     rollback
# restart  restart Pods without changing the template
# pause    suspend a rollout
# resume   continue a paused rollout

The ten parts covered the manifest, apply and observe, the hierarchy, triggering an update, tracking the rollout, rollback, history, scaling, pause/resume, and the rollout command summary.


Quick Reference

The hierarchy

ObjectResponsibility
DeploymentManages ReplicaSets, provides updates and rollbacks
ReplicaSetMaintains the desired Pod count
PodRuns the containers

Strategy parameters

ParameterDefaultMeaning
maxUnavailable25%Maximum Pods unavailable during update
maxSurge25%Maximum extra Pods above replica count
revisionHistoryLimit10Old ReplicaSets retained for rollback

kubectl rollout commands

CommandPurpose
kubectl rollout status deploy/XTrack rollout progress
kubectl rollout history deploy/XList revisions
kubectl rollout undo deploy/XRollback to previous
kubectl rollout undo deploy/X --to-revision=NRollback to specific revision
kubectl rollout restart deploy/XRestart Pods without changing template
kubectl rollout pause deploy/XSuspend rollout
kubectl rollout resume deploy/XResume paused rollout

Scaling

MethodCommand
Imperativekubectl scale deployment/X --replicas=N
DeclarativeChange spec.replicas and kubectl apply
AutomaticHorizontalPodAutoscaler

Best Practices

โœ… Do This:

# Set a readiness probe so rolling updates wait for readiness
readinessProbe:
  httpGet:
    path: /health
    port: 8080                                              # โœ…
# Set resource requests so the scheduler can place Pods
resources:
  requests: { cpu: 100m, memory: 128Mi }                    # โœ…
# Use the declarative approach for scaling
# Change spec.replicas and kubectl apply                       # โœ…
# Rollback with rollout undo when a release fails
kubectl rollout undo deployment/api-backend                  # โœ…

โŒ Don’t Do This:

# Create a bare Pod for a long-running service
kubectl run nginx --image=nginx                              # โŒ
# Manipulate the ReplicaSet directly
kubectl scale rs/api-backend-7d8f9c6b5 --replicas=5          # โŒ
# Use Recreate for a stateless app that can run two versions
strategy:
  type: Recreate                                            # โŒ (causes downtime)
# Assume rollback reverts everything
# It reverts the Pod template only                             # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Deployment does not manage Podsselector.matchLabels does not match template.labelsAlign the selector and the template labels
Rolling update stallsReadiness probe never succeedsFix the probe or the application
Rollback does not restore the old versionrevisionHistoryLimit is too lowIncrease the limit
Pods stay Pending during updateInsufficient cluster capacity for maxSurgeReduce maxSurge or add capacity
Rollback does not fix the problemThe problem is in a ConfigMap or SecretRoll back those resources separately

Real-World Examples

1. Minimal Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: nginx
          image: nginx:1.27

2. Rolling update parameters

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 2

3. Update the image

kubectl set image deployment/web nginx=nginx:1.28

4. Track the rollout

kubectl rollout status deployment/web

5. Rollback

kubectl rollout undo deployment/web

6. Rollback to a specific revision

kubectl rollout undo deployment/web --to-revision=2

7. History

kubectl rollout history deployment/web

8. Scale

kubectl scale deployment/web --replicas=5

9. Restart without changing the template

kubectl rollout restart deployment/web

10. Pause and resume

kubectl rollout pause deployment/web
kubectl rollout resume deployment/web

Visual

The hierarchy

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DEPLOYMENT                                                  โ”‚
โ”‚    โ””โ”€โ”€ ReplicaSet (current)                                  โ”‚
โ”‚          โ”œโ”€โ”€ Pod 1                                           โ”‚
โ”‚          โ”œโ”€โ”€ Pod 2                                           โ”‚
โ”‚          โ””โ”€โ”€ Pod 3                                           โ”‚
โ”‚    โ””โ”€โ”€ ReplicaSet (previous, 0 replicas)                     โ”‚
โ”‚                                                              โ”‚
โ”‚  The Deployment owns the ReplicaSets.                        โ”‚
โ”‚  The ReplicaSets own the Pods.                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The rolling update

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ROLLING UPDATE                                              โ”‚
โ”‚                                                              โ”‚
โ”‚  Old ReplicaSet: 3 Pods                                      โ”‚
โ”‚  New ReplicaSet: 0 Pods                                      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  New Pod 1 created, waits for readiness                      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  New Pod 1 ready โ”€โ”€โ–บ Old Pod 1 terminated                   โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  New Pod 2 created, waits for readiness                      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  ... until the new ReplicaSet has all replicas               โ”‚
โ”‚                                                              โ”‚
โ”‚  No moment where all Pods are unavailable.                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

maxUnavailable and maxSurge

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  4 replicas, maxUnavailable: 1, maxSurge: 1                  โ”‚
โ”‚                                                              โ”‚
โ”‚  Minimum available: 3                                        โ”‚
โ”‚  Maximum total: 5                                            โ”‚
โ”‚                                                              โ”‚
โ”‚  During update:                                              โ”‚
โ”‚    At least 3 Pods available at all times                    โ”‚
โ”‚    At most 5 Pods exist at any moment                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Rollback

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ROLLBACK                                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  Current ReplicaSet: v1.3.0, 3 replicas                      โ”‚
โ”‚  Previous ReplicaSet: v1.2.0, 0 replicas                     โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  kubectl rollout undo                                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Current ReplicaSet scaled to 0                              โ”‚
โ”‚  Previous ReplicaSet scaled to 3                             โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Near-instant because the ReplicaSet already exists.         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Default strategyRollingUpdate
maxUnavailable default25%
maxSurge default25%
Rollback retentionrevisionHistoryLimit (default 10)
Rollback commandkubectl rollout undo deployment/X
Specific revision--to-revision=N
Scalingkubectl scale deployment/X --replicas=N
Restart without template changekubectl rollout restart deployment/X
Self-healingReplicaSet replaces failed Pods

Key takeaways:

  • A Deployment manages ReplicaSets, and a ReplicaSet manages Pods. The Deployment is the object that declares the desired state and controls updates. The ReplicaSet is the object that maintains the Pod count. Never manipulate the ReplicaSet directly; the Deployment orchestrates the transitions .
  • The default strategy is RollingUpdate. It replaces Pods incrementally, keeps a minimum number available, and does not retire an old Pod until its replacement passes the readiness probe. maxUnavailable and maxSurge control the concurrency and the availability floor .
  • Rollback is near-instant. The previous ReplicaSet and its Pod template already exist. kubectl rollout undo scales the old ReplicaSet back up and the current one down. revisionHistoryLimit controls how many old ReplicaSets are retained .
  • Rollback restores the Pod template only. It does not revert ConfigMaps, Secrets, database schema changes, or external state. Those must be rolled back separately .
  • Scaling is done through the Deployment. kubectl scale deployment/X --replicas=N changes the replica count, and the Deployment passes the change to the ReplicaSet. Changing spec.replicas in the manifest and reapplying is the declarative approach .
  • A bare Pod is not a production workload. A Pod that is deleted or whose node fails is not recreated. A Deployment-managed Pod is replaced automatically. The Deployment is the production default for stateless applications .

Remember: A Deployment is the production wrapper around a ReplicaSet, which is the wrapper around Pods. The Deployment declares the desired state, the ReplicaSet maintains the Pod count, and the Pods run the containers. The rolling update strategy is what makes a new version deployable without downtime, and the rollback is what makes a bad version recoverable. The two parameters that control the update are maxUnavailable and maxSurge. The two commands that control the lifecycle are kubectl rollout status and kubectl rollout undo. Never create a bare Pod for a long-running service. Always use a Deployment.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!