Blue-green deployments are a trick to deploy new versions of your application without any downtime, by keeping two identical production environments, one running the current version (green) and one ready for the new version (blue).
Let’s see this in action. Imagine we have a simple Flask app.
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello from version 1!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
We’ll run this on two separate servers or containers, let’s call them green-server and blue-server. Initially, green-server is live, serving traffic. blue-server is idle.
Here’s how we’d typically set them up:
Green Environment (Live):
- Server:
green-server - Application Version:
v1 - Running on Port:
5000 - Load Balancer points to:
green-server:5000
Blue Environment (Staging/New):
- Server:
blue-server - Application Version:
v2(our new deployment) - Running on Port:
5001(important: run on a different port to avoid conflict)
Our load balancer (e.g., Nginx, HAProxy, or a cloud provider’s LB) is configured to send all incoming traffic to green-server:5000.
Now, we want to deploy v2. We deploy v2 to blue-server, running it on port 5001.
# app_v2.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello from version 2!" # Changed message
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001) # Running on a different port
Once v2 is confirmed healthy on blue-server:5001 (perhaps by sending a test request directly to that port), we update the load balancer. We switch its configuration to point to blue-server:5001 instead of green-server:5000.
This switch is almost instantaneous. All new incoming requests now hit blue-server:5001 running v2. The green-server:5000 is still running v1, but it’s no longer receiving live traffic. This is the "switch."
The beauty is that if v2 has a hidden bug, we can immediately switch the load balancer back to green-server:5000 to revert to the stable v1. This rollback is as fast as the initial switch.
Once we’re confident v2 is stable, green-server:5000 becomes the idle environment, and we can spin it down or prepare it for the next deployment cycle where it will become the "blue" environment.
The core problem this solves is the downtime inherent in traditional deployments where you update an application in place. You stop the old version, deploy the new, and start it. That stop-and-start window is downtime. Blue-green eliminates this by always having a fully functional environment ready to serve traffic.
The critical component here is the load balancer. It acts as the traffic director. Its configuration determines which environment is live. The key to a zero-downtime switch is that the load balancer can be reconfigured very quickly, often through API calls or simple configuration reloads.
For instance, if you’re using Nginx, you might have a configuration like this:
# /etc/nginx/sites-available/myapp
upstream app_green {
server green-server:5000;
}
upstream app_blue {
server blue-server:5001;
}
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://app_green; # Initially points to green
}
}
To switch to blue, you’d edit this file to:
# ... (same as above)
location / {
proxy_pass http://app_blue; # Now points to blue
}
# ...
Then, you’d reload Nginx: sudo systemctl reload nginx. This reload is very fast.
The mental model is that you have two identical, independent deployment slots. One is live, the other is a staging area for the next version. You prepare the staging slot, test it, then flip the traffic. The old live slot becomes the new staging area.
A common misconception is that you need two full sets of infrastructure (servers, databases, etc.). While that’s the purest form, you can often achieve blue-green with shared databases (if schema changes are backward-compatible) or by using container orchestration platforms like Kubernetes. In Kubernetes, you’d deploy a new Deployment with the updated image, a new Service that selects pods from the new Deployment, and then update the existing Service’s selector or Ingress resource to point to the new Service. The Service itself abstracts away the specific pods, allowing for seamless transitions.
The next challenge is managing database schema migrations in a way that’s compatible with both the old and new application versions during the transition period.