Blue/Green Deployment #

Guaranteeing 100% service availability when launching major system changes is one of the highest achievements in DevOps practice. Although the built-in Rolling Update strategy is very resource-efficient, it forces two application versions to run simultaneously in the same pool and cuts traffic gradually. If a critical bug appears in the new version, the release cancellation (rollback) process takes minutes to reverse Pod replicas.

The Blue/Green Deployment strategy offers a different approach. By providing two identical parallel environments — the Blue environment as the currently active stable version (v1) and the Green environment as the new release candidate version (v2) — we can fully test the new application in the real production environment without affecting active users at all. After testing passes, we instantly (sub-second) move all user traffic to the Green environment. If anomalies appear post-release, the rollback process is just a one-command affair done within seconds.


Blue/Green Concepts and Network Architecture #

The basic philosophy of Blue/Green is the physical separation between the active environment and the standby environment inside the Kubernetes cluster. We don’t mix v1 and v2 Pods under the same Service label selector during the update process.

flowchart TD
    subgraph Before["BEFORE SWITCH (BLUE ACTIVE)"]
        User1["Real Users"] --> ProdService1["Service: api-prod-service\n(Selector: version=blue)"]
        ProdService1 --> PodBlue1a["v1 Pod (Blue)"]
        ProdService1 --> PodBlue1b["v1 Pod (Blue)"]

        QA1["QA Team / CI Pipeline"] --> TestService1["Service: api-test-service\n(Selector: version=green)"]
        TestService1 --> PodGreen1a["v2 Pod (Green)"]
        TestService1 --> PodGreen1b["v2 Pod (Green)"]
    end

    subgraph After["AFTER SWITCH (GREEN ACTIVE)"]
        User2["Real Users"] --> ProdService2["Service: api-prod-service\n(Selector: version=green)"]
        ProdService2 --> PodGreen2a["v2 Pod (Green)"]
        ProdService2 --> PodGreen2b["v2 Pod (Green)"]

        PodBlue2a["v1 Pod (Blue)\n(Standby - Rollback Ready)"]
        PodBlue2b["v1 Pod (Blue)\n(Standby - Rollback Ready)"]
    end

The traffic cutover from Blue to Green is achieved by changing the label selector reference on the main production Service. Because this API object manipulation is atomic inside Kubernetes etcd, the API Server updates the IP address list (EndpointSlice) instantly. Kube-proxy on every worker node updates the iptables/IPVS rules within milliseconds, redirecting new data packets to Green Pods without cutting running TCP connections.


Implementation Tactic A: Service Selector (Layer 4 Routing) #

This approach is the simplest and most native way in Kubernetes. We define two independent Deployment objects (one for Blue, one for Green) and control traffic routing using the label selector configuration on the Service object.

1. Blue Deployment Manifest (Current Active Version / v1) #

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service-blue
  namespace: e-commerce
  labels:
    app: api-service
    color: blue
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-service
      color: blue
  template:
    metadata:
      labels:
        app: api-service
        color: blue # Unique environment-distinguishing label
    spec:
      containers:
      - name: api
        image: company/api-service:v1.10.0
        ports:
        - containerPort: 8080

2. Green Deployment Manifest (New Version / v2) #

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service-green
  namespace: e-commerce
  labels:
    app: api-service
    color: green
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-service
      color: green
  template:
    metadata:
      labels:
        app: api-service
        color: green # Unique environment-distinguishing label
    spec:
      containers:
      - name: api
        image: company/api-service:v1.11.0 # New version
        ports:
        - containerPort: 8080

3. Main Production Service Manifest #

apiVersion: v1
kind: Service
metadata:
  name: api-prod-service
  namespace: e-commerce
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: api-service
    color: blue # ← Routes active traffic to the Blue version (v1)

Implementation Tactic B: Ingress Controller (Layer 7 Routing) #

If our application needs more complex routing features (like redirecting traffic based on HTTP headers, user session cookies, or TLS termination), we must move routing control to the Ingress Controller level. In this pattern, we create two separate Service objects (one for Blue, one for Green) and point the Ingress to the desired Service.

# Supporting Service for the Blue environment
apiVersion: v1
kind: Service
metadata:
  name: api-service-blue
  namespace: e-commerce
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: api-service
    color: blue
---
# Supporting Service for the Green environment
apiVersion: v1
kind: Service
metadata:
  name: api-service-green
  namespace: e-commerce
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: api-service
    color: green
---
# Main Production Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: e-commerce
spec:
  rules:
  - host: api.company.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service-blue # ← Change this value to 'api-service-green' for cutover
            port:
              number: 80

Step-by-Step Guide (Production Guide) #

Safely applying Blue/Green in production requires disciplined orchestration. Here’s the complete process flow we usually run through the CI/CD pipeline:

flowchart TD
    Start["1. Release New Code (v2)"] --> Step1["2. Deploy the 'Green' Deployment with full replicas"]
    Step1 --> Step2["3. Run isolated Smoke Tests against Green"]
    Step2 --> Dec1{"Did the Smoke Test<br>pass 100%?"}
    
    Dec1 -- "No" --> RollbackEarly["4a. Delete the Green Deployment<br/>'(Release cancelled, Blue stays safe)'"]
    Dec1 -- "Yes" --> Step3["4b. Do the Traffic Cutover via Service / Ingress Patch"]
    
    Step3 --> Step4["5. Monitor Production Metrics (SLA, HTTP Error Rate)"]
    Step5["6. Decommission: Scale Down the Blue Deployment to 0"] <-- "After the Stabilization Period Ends (N Hours)" --> Step4
    
    style RollbackEarly stroke:#d32f2f,stroke-width:2px
    style Step3 stroke:#0288d1,stroke-width:2px
    style Step5 stroke:#388e3c,stroke-width:2px

Step 1: Deploy the Green Version #

Send the Green Deployment manifest (api-service-green) to the cluster. Make sure the replicas are set to the full count (identical to Blue’s capacity).

Step 2: Run Isolated Smoke Testing #

Before directing real user traffic, we must do an independent verification of the Green environment. We can use a testing Service (api-test-service) specifically pointing to color: green.

# Example of running an automated smoke test via a Kubernetes Job
kubectl apply -f - <<EOF
apiVersion: batch/v1
kind: Job
metadata:
  name: smoke-test-green-v11
  namespace: e-commerce
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: test-runner
        image: company/smoke-tester:latest
        env:
        - name: TARGET_URL
          value: "http://api-service-green.e-commerce.svc.cluster.local/healthz"
EOF

# Wait until the Job completes successfully
kubectl wait --for=condition=complete job/smoke-test-green-v11 -n e-commerce --timeout=5m

Step 3: Execute the Traffic Cutover #

If the smoke test succeeds, run an atomic patch command on the main production Service to move traffic routing to the Green environment.

# Atomic Service selector patch command using kubectl
kubectl patch service api-prod-service -n e-commerce \
  -p '{"spec":{"selector":{"app":"api-service","color":"green"}}}'

Step 4: Monitor Post-Release Performance #

Monitor the monitoring dashboard (like Grafana) focusing on HTTP 5xx error rate metrics and transaction latency. If critical anomalies are detected, immediately cancel the release with an instant rollback:

# Instant rollback to Blue if anomalies occur post-cutover
kubectl patch service api-prod-service -n e-commerce \
  -p '{"spec":{"selector":{"app":"api-service","color":"blue"}}}'

Step 5: Decommission the Old Environment #

After the stabilization monitoring period safely passes (e.g. 1-2 hours under real traffic load), we can shut down (scale down) the Blue Deployment to 0 replicas to free cluster resource capacity.

# Scale down the Blue Deployment to zero replicas to save costs
kubectl scale deployment api-service-blue --replicas=0 -n e-commerce

State Management and Shared Resources #

One of the biggest challenges of the Blue/Green strategy (and the reason this strategy isn’t always instantly applicable) is managing State and Shared Resources like Databases and User Session Caches.

1. Database Backward Compatibility #

Both environments (Blue and Green) usually interact with the same physical database. When we deploy the Green version (v2), the database structure may need a schema migration (e.g. adding new columns).

[!CAUTION] Breaking Database Schema Prohibition: Never apply database schema changes that break backward compatibility (non-backward compatible migrations) during Blue/Green. If the Green version changes or removes columns still read by the actively serving Blue version, the Blue version crashes instantly, causing fatal downtime. Always use the Expand-Contract Pattern for database migrations.

2. User Session Affinity (Sticky Sessions) #

If our application stores login status or shopping cart data inside container memory (stateful memory), instantly moving traffic from Blue to Green causes all active users to be mass-logged-out and lose their transaction data.

Best Solution #

  • Externalize Session State: Move session state out of container memory into a distributed cache database like Redis Cluster or Memcached. Both Blue and Green Pods read session data from the same Redis, so when cutover happens, users don’t feel any transition.
  • Ingress Session Draining: If using an L7 Ingress, configure the session draining feature (or graceful connection shedding) that lets active user sessions in the Blue environment finish naturally while directing all new user sessions to the Green environment.

Cluster Cost and Capacity Optimization #

Operating Blue/Green requires at least 100% extra cluster leftover capacity (headroom) during the deployment process. If our cluster is already running above 70% CPU/Memory utilization limits, creating the Green Deployment with full replicas fails due to node scheduling capacity limits (insufficient CPU/memory resources).

Production Cost-Saving Tactics: #

  1. Using Horizontal Pod Autoscaler (HPA): Instead of locking Green replicas fully from the start, enable HPA on the Green Deployment. Start with a minimal replica count (e.g. 1 or 2 Pods). Run the smoke tests. When the traffic cutover happens, HPA detects the load increase on Green containers and automatically scales up as needed for real traffic.
  2. Quick Scale Down to 1 Replica (Not 0): If we worry about needing fast rollback anytime later, instead of scaling Blue down to 0, we can lower it to just 1 replica. This single standby Blue Pod doesn’t consume much cluster resource, but is ready to be instantly scaled up if we must cancel the release.
  3. Cluster Autoscaler: Use the Cluster Autoscaler feature on cloud providers (AWS EKS, GCP GKE). The cluster dynamically rents additional virtual worker nodes during the Blue/Green process, and automatically shuts down those extra nodes (downscale) after the Blue environment is deactivated.

Anti-Patterns vs Best Solutions #

Let’s study common mistakes when applying Blue/Green in production environments along with their fixes.

Anti-Pattern 1: Doing Traffic Cutover Without Cache Warming (Cold Start Anomaly) #

Instantly moving 100% traffic to a Green environment that just finished deploying without doing cache warming or JVM (Java Virtual Machine) warming. When massive traffic load suddenly comes in, Green Pods suffer temporary paralysis (latency spikes) because they must load new database connections and compile memory runtime from a cold state (cold start).

Best Solution #

Run small-scale load tests or pre-warming scripts against the Green testing Service before directing the main production traffic.

# ✓ SOLUTION: Endpoint warming script before cutover
for i in {1..100}; do 
  curl -s -o /dev/null "http://api-service-green.e-commerce.svc.cluster.local/api/bootstrap"
done
echo "✓ Cache warming complete. Safe to do the cutover."

Anti-Pattern 2: Deleting the Blue Environment Right After a Successful Cutover #

Deleting the Blue Deployment immediately after pressing the cutover button on the CI/CD dashboard, to free cluster capacity as fast as possible. If a hidden bug appears after 15 minutes (e.g. a slow memory leak), we lose the instant rollback ability and must redeploy from scratch.

Best Solution #

Apply a Stabilization Period (Graceful Coexistence Period). Keep the Blue environment in full standby condition (intact replicas) for at least 30 to 60 minutes post-cutover. After confirming no error log spikes or user complaints, only then run the automatic cleanup.


Complete Production Blue/Green Manifests #

Here’s a complete declarative manifest visualization safely managing the Blue/Green release cycle in the cluster:

# 1. Deployment for the BLUE environment (Active)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-processor-blue
  namespace: order-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-processor
      color: blue
  template:
    metadata:
      labels:
        app: order-processor
        color: blue
    spec:
      containers:
      - name: processor
        image: company/order-processor:v1.5.0
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
---
# 2. Deployment for the GREEN environment (Standby / New Version)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-processor-green
  namespace: order-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-processor
      color: green
  template:
    metadata:
      labels:
        app: order-processor
        color: green
    spec:
      containers:
      - name: processor
        image: company/order-processor:v1.6.0 # New Version
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
---
# 3. Main Production Service (Routes Real User Traffic)
apiVersion: v1
kind: Service
metadata:
  name: order-processor-prod
  namespace: order-system
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: order-processor
    # To route traffic to the new version, change 'color: blue' to 'color: green'
    color: blue
---
# 4. Testing Service (Specifically for QA Team / CI Pipeline Testing)
apiVersion: v1
kind: Service
metadata:
  name: order-processor-test
  namespace: order-system
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: order-processor
    # Always points to the new release candidate version (green) for smoke testing
    color: green

Blue/Green Release Audit Checklist #

Before doing the instant production traffic movement, make sure our system meets the following readiness checklist:

STATE & DATABASE CONSISTENCY:
  □ The deployed database schema is backward-compatible with the old version (v1) code.
  □ User session data has been moved to an external cache database (Redis).
  □ Database pool connections on the new environment are adjusted so they don't overload the database server's maximum capacity.

TESTING & VALIDATION:
  □ Automated smoke testing against the testing Service (Green) has been run and passes 100%.
  □ Green Pods have gone through the cache warming phase (pre-warming) to avoid cold start anomalies.
  □ HTTP 5xx metric and latency monitoring mechanisms are ready to detect post-cutover anomalies.

RESOURCE & COST MANAGEMENT:
  □ The cluster has at least 100% leftover resource capacity (headroom) to run both Deployments in parallel.
  □ The CI/CD pipeline is configured to lower the old environment (Blue) replicas after the stabilization period ends.
  □ The instant rollback procedure (Service selector patch command) has been tested and works within seconds.

Summary #

  • Use Service selectors for instant cutover — Traffic movement from Blue to Green is achieved by changing the color: blue value to color: green on the main production Service manifest, applying atomically in sub-seconds.
  • Must run smoke testing — Always leverage the internal testing Service (api-test-service) to verify the new version’s (Green) functionality before directing real user traffic.
  • Maintain a stabilization period — Don’t immediately delete the old environment (Blue) after release; keep it for at least 30-60 minutes as an instant rollback guarantee if hidden bugs appear.
  • Design backward-compatible databases — Apply the Expand-Contract pattern on databases so both Blue and Green versions can read data simultaneously without crashing.
  • Move user session state — Secure session state in external Redis so users aren’t suddenly logged out during the traffic movement process.
  • Use HPA for resource efficiency — Enable HPA on the Green environment so replica counts grow elastically following real traffic volume after cutover.

← Previous: Rolling Update   Next: Canary Deployment →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact