Back to Blog
DeploymentDevOpsBlue-GreenZero Downtime

Blue-Green Deployments: Zero-Downtime Releases

Implement blue-green deployments for zero-downtime releases. Learn patterns, rollback strategies, and best practices.

B
Bootspring Team
Engineering
February 27, 2026
3 min read

Blue-green deployments eliminate downtime by running two identical production environments.

How It Works#

Before deployment: ┌─────────────┐ ┌─────────────┐ │ Router │────▶│ Blue │ (Live) └─────────────┘ │ (v1.0.0) │ └─────────────┘ ┌─────────────┐ │ Green │ (Idle) │ (v1.0.0) │ └─────────────┘ After deployment: ┌─────────────┐ ┌─────────────┐ │ Router │ │ Blue │ (Idle) └─────────────┘ │ (v1.0.0) │ │ └─────────────┘ │ ┌─────────────┐ └──────────▶│ Green │ (Live) │ (v2.0.0) │ └─────────────┘

Kubernetes Implementation#

1# blue-deployment.yaml 2apiVersion: apps/v1 3kind: Deployment 4metadata: 5 name: app-blue 6 labels: 7 app: myapp 8 version: blue 9spec: 10 replicas: 3 11 selector: 12 matchLabels: 13 app: myapp 14 version: blue 15 template: 16 metadata: 17 labels: 18 app: myapp 19 version: blue 20 spec: 21 containers: 22 - name: app 23 image: myapp:1.0.0 24 ports: 25 - containerPort: 8080 26--- 27# green-deployment.yaml 28apiVersion: apps/v1 29kind: Deployment 30metadata: 31 name: app-green 32 labels: 33 app: myapp 34 version: green 35spec: 36 replicas: 3 37 selector: 38 matchLabels: 39 app: myapp 40 version: green 41 template: 42 metadata: 43 labels: 44 app: myapp 45 version: green 46 spec: 47 containers: 48 - name: app 49 image: myapp:2.0.0 50 ports: 51 - containerPort: 8080 52--- 53# service.yaml - Switch by changing selector 54apiVersion: v1 55kind: Service 56metadata: 57 name: app-service 58spec: 59 selector: 60 app: myapp 61 version: blue # Change to 'green' to switch 62 ports: 63 - port: 80 64 targetPort: 8080

Deployment Script#

1#!/bin/bash 2set -e 3 4NEW_VERSION=$1 5CURRENT_ENV=$(kubectl get svc app-service -o jsonpath='{.spec.selector.version}') 6 7if [ "$CURRENT_ENV" = "blue" ]; then 8 TARGET_ENV="green" 9else 10 TARGET_ENV="blue" 11fi 12 13echo "Current: $CURRENT_ENV, Deploying to: $TARGET_ENV" 14 15# Update target deployment with new image 16kubectl set image deployment/app-$TARGET_ENV app=myapp:$NEW_VERSION 17 18# Wait for rollout 19kubectl rollout status deployment/app-$TARGET_ENV 20 21# Run smoke tests 22./run-smoke-tests.sh app-$TARGET_ENV 23 24# Switch traffic 25kubectl patch svc app-service -p "{\"spec\":{\"selector\":{\"version\":\"$TARGET_ENV\"}}}" 26 27echo "Switched to $TARGET_ENV"

AWS ALB Implementation#

1import { 2 ElasticLoadBalancingV2Client, 3 ModifyListenerCommand, 4} from '@aws-sdk/client-elastic-load-balancing-v2'; 5 6const client = new ElasticLoadBalancingV2Client({}); 7 8async function switchTraffic( 9 listenerArn: string, 10 targetGroupArn: string 11) { 12 await client.send(new ModifyListenerCommand({ 13 ListenerArn: listenerArn, 14 DefaultActions: [{ 15 Type: 'forward', 16 TargetGroupArn: targetGroupArn, 17 }], 18 })); 19} 20 21// Gradual traffic shift 22async function gradualSwitch( 23 listenerArn: string, 24 blueTargetGroup: string, 25 greenTargetGroup: string 26) { 27 const stages = [90, 70, 50, 25, 0]; // Blue percentages 28 29 for (const blueWeight of stages) { 30 await client.send(new ModifyListenerCommand({ 31 ListenerArn: listenerArn, 32 DefaultActions: [{ 33 Type: 'forward', 34 ForwardConfig: { 35 TargetGroups: [ 36 { TargetGroupArn: blueTargetGroup, Weight: blueWeight }, 37 { TargetGroupArn: greenTargetGroup, Weight: 100 - blueWeight }, 38 ], 39 }, 40 }], 41 })); 42 43 console.log(`Traffic: Blue ${blueWeight}%, Green ${100 - blueWeight}%`); 44 await sleep(60000); // Wait 1 minute between stages 45 } 46}

Database Considerations#

1// Use backward-compatible migrations 2// Phase 1: Add new column (nullable) 3await db.query(`ALTER TABLE users ADD COLUMN new_field VARCHAR(255)`); 4 5// Phase 2: Deploy app that writes to both old and new columns 6// Phase 3: Backfill data 7await db.query(`UPDATE users SET new_field = old_field WHERE new_field IS NULL`); 8 9// Phase 4: Deploy app that only uses new column 10// Phase 5: Drop old column (after rollback window) 11await db.query(`ALTER TABLE users DROP COLUMN old_field`);

Health Checks#

1// Comprehensive health check endpoint 2app.get('/health', async (req, res) => { 3 const checks = { 4 database: await checkDatabase(), 5 redis: await checkRedis(), 6 externalApi: await checkExternalApi(), 7 }; 8 9 const healthy = Object.values(checks).every(c => c.status === 'healthy'); 10 11 res.status(healthy ? 200 : 503).json({ 12 status: healthy ? 'healthy' : 'unhealthy', 13 version: process.env.APP_VERSION, 14 checks, 15 }); 16}); 17 18// Readiness check for traffic 19app.get('/ready', async (req, res) => { 20 const ready = await isAppReady(); 21 res.status(ready ? 200 : 503).json({ ready }); 22});

Rollback Strategy#

1#!/bin/bash 2# Quick rollback - just switch selector back 3 4CURRENT_ENV=$(kubectl get svc app-service -o jsonpath='{.spec.selector.version}') 5 6if [ "$CURRENT_ENV" = "blue" ]; then 7 ROLLBACK_ENV="green" 8else 9 ROLLBACK_ENV="blue" 10fi 11 12echo "Rolling back from $CURRENT_ENV to $ROLLBACK_ENV" 13 14kubectl patch svc app-service -p "{\"spec\":{\"selector\":{\"version\":\"$ROLLBACK_ENV\"}}}" 15 16echo "Rollback complete"

Blue-green deployments provide instant rollback capability and zero-downtime releases.

Share this article

Help spread the word about Bootspring