Blue/green deployment — two identical environments, flip traffic on success
Intermediate5 min read·eng-18-009
interview
Concept
Blue-green deployment — a release strategy that maintains two identical production environments (Blue and Green). Only one environment serves live traffic at a time. To deploy, update the idle environment, verify it, then switch the load balancer to it.
The blue-green cycle:
- Blue is live (serving all traffic). Green is idle.
- Deploy new code to Green. Run migrations, tests, smoke checks on Green while Blue continues serving.
- Switch the load balancer to Green. Now Green serves all traffic.
- Blue is now idle. Keep it warm for a few minutes.
- If Green has issues → switch load balancer BACK to Blue (instant rollback).
- Next deployment: deploy to Blue (now idle), switch back to Blue.
Benefits:
- Zero downtime: traffic switch is nearly instantaneous.
- Instant rollback: switch load balancer back to the old environment.
- Smoke testing on production infrastructure before going live.
Drawbacks:
- Double the infrastructure cost (two full environments).
- Database migrations are tricky: the new schema must be compatible with BOTH Blue code and Green code (during the period when you might roll back).
- Long-running transactions during the switch need careful handling.
vs Canary deployments: Blue-green is all-or-nothing. Canary starts with 5% of traffic on new code, gradually increases, rolling back if errors spike.
In cloud environments: AWS CodeDeploy supports blue-green. AWS Elastic Beanstalk has built-in blue-green. Laravel Vapor uses blue-green under the hood (AWS Lambda versions).
Code Example
bash
# Blue-green deployment with Nginx
# Current state: Blue (nginx upstream = blue_servers) serving traffic
# 1. Deploy new code to Green environment
ssh app-green-1 "git pull && composer install && php artisan migrate --force && php artisan optimize"
ssh app-green-2 "git pull && composer install && php artisan optimize"
# 2. Smoke test Green (not yet live)
curl -f https://green.internal.example.com/health
curl -f https://green.internal.example.com/api/ping
# 3. Switch load balancer from Blue to Green
# /etc/nginx/upstream.conf
upstream active_servers {
# server blue_1:9000; # blue servers commented out
# server blue_2:9000;
server green_1:9000; # green servers now active
server green_2:9000;
}
nginx -s reload # graceful reload — in-flight requests finish, new ones go to green
# 4. Monitor errors for 5-10 minutes
# 5a. Success — clean up Blue (will be used for next deployment)
echo "Deployment successful. Blue is now standby."
# 5b. Rollback — just switch back to Blue
upstream active_servers {
server blue_1:9000;
server blue_2:9000;
# server green_1:9000;
# server green_2:9000;
}
nginx -s reload # instant rollbackphp
<?php
// DATABASE compatibility requirement during blue-green:
// While Green is live, Blue might need to serve traffic during rollback
// New schema must work with BOTH old and new application code
// Safe: adding a nullable column (both versions can work with it)
Schema::table('orders', function (Blueprint $table) {
$table->string('new_field')->nullable()->default(null);
});
// Unsafe: removing a column (old code (Blue) still reads it)
// Solution: 3-phase removal
// Phase 1: Stop reading the column in code (but don't drop it yet)
// Phase 2: Deploy phase 1, confirm Blue is retired
// Phase 3: Drop the column in a subsequent migration