Back to Deployment Strategies
Q1Question 1 of 5

Kubernetes `RollingUpdate` vs `Recreate` deployment strategies: trade-offs and when to choose each

Key Concepts
  • RollingUpdate replaces pods incrementally, keeping old pods serving traffic until new pods pass readiness probes, achieving zero downtime.
  • Recreate terminates every old pod before starting any new pod, guaranteeing a downtime window but ensuring only one version runs at a time.
  • maxSurge controls how many extra pods above the desired count are allowed during a roll; maxUnavailable controls how many may be missing.
  • RollingUpdate requires backward-compatible code and schema because two versions briefly coexist.
  • Recreate suits singletons and incompatible upgrades where two versions must never overlap.
  • Readiness probes, graceful shutdown, and expand-and-contract schema migrations are prerequisites for genuine zero downtime with RollingUpdate.

Definition

RollingUpdate keeps the lights on by overlapping two versions; Recreate turns the lights off so only one version ever runs.
  • RollingUpdate – the default Kubernetes Deployment strategy that gradually replaces old pods with new pods a few at a time, keeping some old pods serving traffic while new pods start, achieving zero downtime at the cost of briefly running two versions and extra pods.
  • Recreate – an alternative strategy that first terminates every old pod and only then starts new pods, guaranteeing a downtime window but ensuring exactly one version runs at any moment.
  • maxSurge – a RollingUpdate parameter that sets how many extra pods above the desired replica count may exist temporarily during a roll.
  • maxUnavailable – a RollingUpdate parameter that sets how many pods may be missing below the desired count during a roll.
  • Readiness probe – the health check that must pass before Kubernetes routes traffic to a new pod, making it the gatekeeper of safe rolling updates.

Imagine National Bank of India needs to replace all the tellers at a branch with newly trained tellers. The rolling approach sends in one new teller while the old ones keep serving customers, then swaps them one by one, so the queue never stops – but for a while there are more tellers than desks. The recreate approach closes the branch, sends everyone home, brings in the new tellers, and reopens – nobody is served during the swap, but there is never any confusion about who is on duty.

Understanding the Concepts

What is a Kubernetes Deployment strategy?

A Kubernetes Deployment manages a set of identical pods through a ReplicaSet. When you change the pod template, for example by pointing at a new container image version, the Deployment must replace the old pods with new ones. The strategy.type field in the Deployment spec decides how that replacement happens. Kubernetes offers exactly two built-in strategies: RollingUpdate and Recreate.

YAML
# deployment.yaml for National Bank Transfer Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: transfer-service
  namespace: banking
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: transfer-service
  template:
    metadata:
      labels:
        app: transfer-service
    spec:
      containers:
        - name: transfer-service
          image: registry.nbi.com/transfer-service:2.5.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10

How does RollingUpdate work?

With RollingUpdate, which is the default, Kubernetes replaces pods incrementally. Two parameters shape the roll. maxSurge says how many pods above the desired replica count may exist temporarily. maxUnavailable says how many pods below the desired count may be missing at once.

The practical effect is that Kubernetes spins up new pods, waits for them to pass their readiness probe, shifts traffic to them, and only then tears down old pods. Because old pods keep serving until new pods are healthy, users experience no interruption.

YAML
# RollingUpdate with zero unavailable pods
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%        # up to 25% extra pods allowed
    maxUnavailable: 0    # all desired pods always available
BASH
# Deploy the new version
kubectl set image deployment/transfer-service \
  transfer-service=registry.nbi.com/transfer-service:2.6.0 \
  -n banking

# Watch the rollout progress
kubectl rollout status deployment/transfer-service -n banking
TEXT
Waiting for deployment "transfer-service" rollout to finish:
  3 of 4 updated replicas are available...
  4 of 4 updated replicas are available...
deployment "transfer-service" successfully rolled out

How does Recreate work?

With Recreate, Kubernetes scales the old ReplicaSet all the way down to zero, waits for every old pod to terminate, and only then scales the new ReplicaSet up. During the gap between the last old pod dying and the first new pod becoming ready, the service has no running pods and requests fail.

YAML
# Recreate strategy for the Settlement Scheduler
apiVersion: apps/v1
kind: Deployment
metadata:
  name: settlement-scheduler
  namespace: banking
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: settlement-scheduler
  template:
    metadata:
      labels:
        app: settlement-scheduler
    spec:
      containers:
        - name: settlement-scheduler
          image: registry.nbi.com/settlement-scheduler:3.1.0
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: production
BASH
# Deploy with Recreate -- the old pod terminates before new one starts
kubectl set image deployment/settlement-scheduler \
  settlement-scheduler=registry.nbi.com/settlement-scheduler:3.2.0 \
  -n banking

kubectl rollout status deployment/settlement-scheduler -n banking
TEXT
Waiting for deployment "settlement-scheduler" rollout to finish:
  0 of 1 updated replicas are available...
  1 of 1 updated replicas are available...
deployment "settlement-scheduler" successfully rolled out

How is RollingUpdate different from Recreate?

| Aspect | RollingUpdate | Recreate |
|---|---|---|
| Downtime | Zero (when probes are correct) | Guaranteed gap |
| Two versions coexist | Yes, during the roll | Never |
| Rollback speed | Slow (pod-by-pod reverse) | Slow (full redeploy) |
| Extra resource cost | maxSurge pods temporarily | None |
| Best for | Stateless, backward-compatible services | Singletons, incompatible upgrades |
| Schema requirement | Expand-and-contract | Any migration is safe |
| Default | Yes | No |

What are the rules and constraints for RollingUpdate?

RollingUpdate only delivers genuine zero downtime if three things are true. First, readiness probes must accurately report when a pod can serve traffic, otherwise Kubernetes shifts traffic to a pod that is not ready and users see errors. Second, the application must shut down gracefully, draining in-flight requests when it receives the termination signal, which requires honoring preStop hooks and the termination grace period. Third, database schema changes must follow the expand-and-contract pattern.

YAML
# Graceful shutdown configuration
spec:
  terminationGracePeriodSeconds: 60
  containers:
    - name: transfer-service
      lifecycle:
        preStop:
          exec:
            command: ["sh", "-c", "sleep 5"]
JAVA
package com.bank.config;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GracefulShutdownConfig {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> graceful() {
        // Spring Boot 2.3+ supports graceful shutdown natively
        // application.yml: server.shutdown=graceful
        return factory -> factory.addConnectorCustomizers(
            connector -> connector.setProperty("relaxedQueryChars", "[]")
        );
    }
}

What is the expand-and-contract pattern for schema changes?

When two versions of the application run simultaneously during a rolling update, database schema changes must be backward compatible. The expand-and-contract pattern splits a breaking change into three steps.

SQL
-- Step 1: EXPAND -- add the new column (both old and new code work)
ALTER TABLE transfers ADD COLUMN beneficiary_iban VARCHAR(34);

-- Step 2: MIGRATE -- new code writes to both columns, backfill old rows
UPDATE transfers
   SET beneficiary_iban = beneficiary_account
 WHERE beneficiary_iban IS NULL;

-- Step 3: CONTRACT -- remove the old column (only after old code is gone)
ALTER TABLE transfers DROP COLUMN beneficiary_account;

Because every intermediate state is readable and writable by whichever versions are live, a rolling update never breaks, and a rollback never lands on a schema the old code cannot handle.

When should you use RollingUpdate?

Choose RollingUpdate for stateless, backward-compatible services where downtime is unacceptable. At National Bank of India, the Transfer API serves customers around the clock and must never go offline for a deployment.

BASH
# Verify zero-downtime during a rolling update
# Run a load test while deploying
kubectl set image deployment/transfer-service \
  transfer-service=registry.nbi.com/transfer-service:2.7.0 \
  -n banking &

# In another terminal, watch for errors
while true; do
  curl -s -o /dev/null -w "%{http_code}" http://transfer.nbi.com/health
  echo
  sleep 0.5
done
TEXT
200
200
200
200
... (no 5xx errors if probes and graceful shutdown are correct)

When should you use Recreate?

Choose Recreate when two versions absolutely cannot coexist, when the service holds an exclusive resource, or when a short maintenance window is acceptable. A singleton like the Settlement Scheduler that sweeps and settles trades once a night would double-settle if two instances ran together.

YAML
# CronJob alternative for batch singletons
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-settlement
  namespace: banking
spec:
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: settlement
              image: registry.nbi.com/settlement:3.2.0
          restartPolicy: OnFailure

How do you roll back a failed deployment?

Kubernetes keeps a history of Deployment revisions. If a new version fails readiness probes or causes errors, you can roll back to the previous revision instantly.

BASH
# Check rollout history
kubectl rollout history deployment/transfer-service -n banking

# Roll back to the previous version
kubectl rollout undo deployment/transfer-service -n banking

# Roll back to a specific revision
kubectl rollout undo deployment/transfer-service \
  --to-revision=3 -n banking
TEXT
deployment.apps/transfer-service rolled back

What are common mistakes?

The most common mistake is using RollingUpdate without proper readiness probes. If the readiness probe is missing or always returns healthy, Kubernetes routes traffic to pods that are still starting, and users see errors during every deploy.

YAML
# WRONG: no readiness probe -- traffic hits pods before they are ready
spec:
  containers:
    - name: transfer-service
      image: registry.nbi.com/transfer-service:2.6.0
      ports:
        - containerPort: 8080
      # No readinessProbe defined!
YAML
# CORRECT: readiness probe gates traffic
spec:
  containers:
    - name: transfer-service
      image: registry.nbi.com/transfer-service:2.6.0
      ports:
        - containerPort: 8080
      readinessProbe:
        httpGet:
          path: /actuator/health/readiness
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 5
        failureThreshold: 3

What should a senior engineer know?

A senior engineer knows that RollingUpdate is only one piece of zero-downtime deployment. The full picture includes readiness probes that honestly reflect warm-up, graceful shutdown that drains in-flight requests, expand-and-contract for database migrations, and pod disruption budgets that prevent too many pods from going down at once during node maintenance.

YAML
# PodDisruptionBudget -- at least 3 of 4 pods must stay available
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: transfer-service-pdb
  namespace: banking
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: transfer-service

Visual Overview

DIAGRAM
  RollingUpdate (zero downtime)
  ─────────────────────────────────────────────
  Time 0    [OLD-1] [OLD-2] [OLD-3] [OLD-4]
                                         ▼ maxSurge=1
  Time 1    [OLD-1] [OLD-2] [OLD-3] [OLD-4] [NEW-1 starting...]
                                              ▼ readiness passes
  Time 2    [OLD-2] [OLD-3] [OLD-4] [NEW-1] [NEW-2 starting...]
  Time 3    [OLD-3] [OLD-4] [NEW-1] [NEW-2] [NEW-3 starting...]
  Time 4    [OLD-4] [NEW-1] [NEW-2] [NEW-3] [NEW-4 starting...]
  Time 5    [NEW-1] [NEW-2] [NEW-3] [NEW-4]   ← done, zero gap

  Recreate (guaranteed downtime)
  ─────────────────────────────────────────────
  Time 0    [OLD-1] [OLD-2] [OLD-3] [OLD-4]
                 ▼ all terminated
  Time 1    [    ...no pods running...    ]   ← DOWNTIME
                 ▼ new pods start
  Time 2    [NEW-1 starting...] [NEW-2 starting...]
  Time 3    [NEW-1] [NEW-2] [NEW-3] [NEW-4]   ← done

Key Takeaway

Kubernetes gives a Deployment two built-in strategies for replacing old pods with new ones. RollingUpdate, the default, replaces pods incrementally: it brings up new pods, waits for them to pass their readiness probe, shifts traffic, and only then removes old pods. Because old pods keep serving until the new ones are healthy, users see no interruption. The cost is that two versions briefly coexist, and you temporarily consume extra resources for the surge pods. Recreate does the opposite: it terminates every old pod first and only then starts the new ones, guaranteeing a downtime window but ensuring two versions never overlap.

Choosing between them comes down to whether the application can tolerate two versions running together and whether you can tolerate downtime. For a stateless, customer-facing service like the Transfer API at National Bank of India, where downtime is unacceptable, RollingUpdate is the clear choice. For a singleton like the Settlement Scheduler that must never run as two instances, Recreate is the safe choice.

The nuance that separates a senior answer is that RollingUpdate only delivers genuine zero downtime when three things hold: the readiness probe honestly reflects warm-up, the application shuts down gracefully and drains in-flight requests, and any database schema change follows the expand-and-contract pattern so both versions can read and write safely during the overlap.

Remember one sentence: RollingUpdate keeps the service alive by overlapping versions, Recreate keeps the service pure by never overlapping them, and the choice depends on whether two versions can safely coexist.

---

Share
Use ← → arrow keys to navigate