Workloads

Deployments & ReplicaSets

● Intermediate ⏱ 15 min read

A Deployment is the primary workload object for running stateless applications in Kubernetes. It manages a set of identical pods, handles rolling updates without downtime, and gives you rollback in a single command. Under the hood, it does this by orchestrating one or more ReplicaSets. Understanding how these two objects relate is the key to understanding how Kubernetes keeps your app alive during deploys.

What Is a Deployment?

When you create a Deployment, you declare your desired state: "I want 3 replicas of this container image running at all times." The Deployment controller continuously reconciles reality against that declaration. If a pod crashes, it creates a replacement. If you push a new image, it migrates pods to the new version without downtime.

Deployments are designed for stateless workloads — applications that can be replaced, scaled up or down, and restarted in any order without losing data. Web servers, APIs, and background workers are natural fits. If your app writes to local disk or needs a stable hostname, use a StatefulSet instead.

💡
Deployment → ReplicaSet → Pods

You never create pods or ReplicaSets directly when using Deployments. The Deployment creates and manages ReplicaSets; ReplicaSets create and manage Pods. You only interact with the Deployment.

ReplicaSets Under the Hood

A ReplicaSet has one job: ensure exactly N copies of a pod template are running. It watches the cluster and creates or deletes pods until the count matches spec.replicas. ReplicaSets use label selectors to identify the pods they own.

You rarely interact with ReplicaSets directly. The Deployment creates and manages them. During a rolling update, the Deployment creates a new ReplicaSet for the new version and scales it up while scaling down the old one. Kubernetes keeps old ReplicaSets around (up to spec.revisionHistoryLimit, default 10) so you can roll back.

Deployment — nginx (3 replicas)
ReplicaSet — nginx:1.27 (old)
replicas: 0 (scaled down)
ReplicaSet — nginx:1.28 (active)
Pod Pod Pod
Old ReplicaSet kept for rollback. New one owns all running pods.
A Deployment manages multiple ReplicaSets — one active, old ones kept for rollback history

Deployment YAML

A minimal Deployment manifest:

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.28
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "250m"

Key fields:

FieldDescription
spec.replicasNumber of pod copies to maintain. Default is 1 if omitted.
spec.selector.matchLabelsWhich pods this Deployment owns. Must match spec.template.metadata.labels.
spec.templateThe pod template — same structure as a Pod spec, minus apiVersion and kind.
spec.revisionHistoryLimitHow many old ReplicaSets to keep for rollback. Default 10.
⚠️
The selector is immutable

spec.selector cannot be changed after the Deployment is created. If you need to change it, you must delete and recreate the Deployment. This is by design — changing selectors would cause the Deployment to lose track of its existing pods.

Rolling Updates

By default, updating a Deployment image triggers a rolling update: Kubernetes replaces old pods with new ones gradually, ensuring some replicas stay available throughout.

# Update the image — triggers a rolling update
kubectl set image deployment/nginx nginx=nginx:1.29

# Or edit the manifest and apply
kubectl apply -f deployment.yaml

# Watch the rollout in real time
kubectl rollout status deployment/nginx

Two parameters control how the rollout proceeds:

ParameterDefaultMeaning
maxUnavailable25%Max pods that can be unavailable during update. Lower = safer, slower.
maxSurge25%Max extra pods allowed above replicas. Higher = faster rollout but more resources.
deployment.yaml — tuning the rollout
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1    # at most 1 pod down at a time
      maxSurge: 1          # at most 1 extra pod above replicas
Rolling update with replicas=3, maxUnavailable=1, maxSurge=1
t=0 (start)
v1 v1 v1
t=1
v1 v1 v2 v2 ✦
t=2
v1 v2 v2
t=3 (done)
v2 v2 v2
✦ = new pod starting (surge). At most 1 old pod removed at a time.
Rolling update: new pods come up before old ones are terminated — zero downtime

Rollback

If a deployment goes wrong, roll back to the previous revision in one command:

# Roll back to the previous revision
kubectl rollout undo deployment/nginx

# Roll back to a specific revision
kubectl rollout undo deployment/nginx --to-revision=2

# View rollout history
kubectl rollout history deployment/nginx

# Inspect a specific revision
kubectl rollout history deployment/nginx --revision=2

Under the hood, undo scales up the old ReplicaSet and scales down the current one — the same rolling update process in reverse. This is why Kubernetes preserves old ReplicaSets.

💡
Annotate your rollouts

Use --record (deprecated but still useful) or kubectl annotate to leave a change cause in rollout history. Without it, rollout history shows only the revision number and pod template hash, which makes troubleshooting harder.

kubectl annotate deployment/nginx kubernetes.io/change-cause="bump nginx to 1.29"

Scaling

Scale replicas imperatively or declaratively:

# Imperative scale
kubectl scale deployment/nginx --replicas=5

# Declarative: edit the manifest and re-apply
kubectl apply -f deployment.yaml

# Auto-scale based on CPU (requires metrics-server)
kubectl autoscale deployment/nginx --min=2 --max=10 --cpu-percent=70

Scaling down to zero is valid — it stops all pods while keeping the Deployment object and its configuration. Useful for maintenance or cost savings in dev environments.

# Suspend a deployment (scale to 0)
kubectl scale deployment/nginx --replicas=0

# Resume
kubectl scale deployment/nginx --replicas=3

Update Strategies

Deployments support two update strategies:

StrategyBehaviourUse case
RollingUpdate (default)Gradually replaces old pods with new ones. Some old pods serve traffic until new ones are ready.Production workloads requiring zero downtime.
RecreateTerminates all old pods first, then creates new ones. Causes downtime.Apps that cannot run two versions simultaneously (e.g., database schema migrations with breaking changes).
deployment.yaml — Recreate strategy
spec:
  strategy:
    type: Recreate    # no rollingUpdate block needed
⚠️
Recreate causes downtime

With Recreate, there is a gap between all old pods terminating and the first new pod becoming ready. Only use it when you have no choice — for example, when an old and new version of an app cannot coexist against the same database schema.

Essential kubectl Commands

# Apply a deployment
kubectl apply -f deployment.yaml

# Check status
kubectl get deployment nginx
kubectl describe deployment nginx

# Watch pods
kubectl get pods -l app=nginx -w

# Update image
kubectl set image deployment/nginx nginx=nginx:1.29

# Check rollout status
kubectl rollout status deployment/nginx

# Pause a rollout (e.g. to batch changes)
kubectl rollout pause deployment/nginx
kubectl rollout resume deployment/nginx

# Roll back
kubectl rollout undo deployment/nginx

# Scale
kubectl scale deployment/nginx --replicas=5

# Delete
kubectl delete deployment nginx