A deploy order that never returns 502
Zero downtime does not mean "no risk" — it means "no interruption visible to the user." That is achieved by bringing up and checking the new version next to the old one, then switching over with a single atomic operation that is easy to undo.
- Deploy the new version of the application on a separate port or in a separate container, without touching the running one.
- Wait until the new version passes its health check and fully initializes (warms its cache, connects to the database).
- Check the new version directly, bypassing the main domain — through the direct port or a service header.
- Switch the load balancer or proxy to the new version with a single command.
- Keep the old version running for a while longer, in case an instant rollback is needed.
Being honest: truly zero downtime never happens even with this order — a fraction of a second remains for switching connections at the moment of reload. In practice that means 0-2 seconds of visible delay for some requests, not a complete absence of any effect.
Blue-Green deployment
Keep two identical environments: Blue (the current live one) and Green (the new version). After testing Green, switch the traffic over.
# Nginx upstream — switching between blue and green
upstream app {
server 127.0.0.1:8080; # blue
# server 127.0.0.1:8081; # green (uncomment when switching)
}
server {
location / {
proxy_pass http://app;
}
}
Blue-Green with Docker Compose
# Start the green environment
docker-compose -f docker-compose.green.yml up -d
# Check green
curl http://localhost:8081/health
# Switch Nginx to green
sed -i 's/8080/8081/' /etc/nginx/conf.d/app.conf
nginx -s reload
# Stop blue
docker-compose -f docker-compose.blue.yml down
Rolling deployment
Gradual instance replacement: update them one by one without stopping the service.
# Example with Docker Swarm
docker service update --image myapp:v2 --update-parallelism 1 --update-delay 30s myapp_service
Comparing deployment strategies
| Strategy | Rollback | Resources | Complexity |
|---|---|---|---|
| Blue-Green | Instant | 2x | Medium |
| Rolling | Gradual | 1x + overhead | Low |
| Canary | Gradual | 1x | High |
Checking the new version before switching traffic
Do not rely on a visual look — check the health-check endpoint and a real application flow directly on the new port, before switching the proxy over:
curl -s http://127.0.0.1:8081/health | grep -q '"status":"ok"' && echo "Green is ready"
A healthy endpoint should check not just "the process is running" but real dependencies: the database connection, cache availability, and job queue readiness.
Common errors during the switch
| Symptom | Cause and fix |
|---|---|
| 502 Bad Gateway right after reload | The new upstream is not yet accepting connections — add a health check before switching Nginx |
| Some requests still hit the old version after the switch | Keep-alive connections to Nginx workers were not dropped — use nginx -s reload, not restart |
| Database schema errors after deploying the new version | The database migration is incompatible with the old code version still running in parallel |
Rollback after a failed deploy
- Do not stop the old version (blue) until the new one has run under real traffic for at least 15-30 minutes.
- If problems arise, point the upstream back at the old port — the command is identical to the switch, just in reverse.
- Investigate the failure on the green environment separately, without disturbing live traffic.
When this does not fit
Blue-Green requires twice the resources during deployment — for small projects on a tight budget that can be hard to justify. If the application keeps state in process memory (sessions, WebSocket connections) with no external store, that data is lost during the switch — you need a separate scheme with sticky sessions or an external state store.
⚠️ With a blue-green deploy, make sure both environments run against the same database and that the database schema is compatible with both versions of the application.
Checking the result after switching
- Monitor the 5xx error rate in the logs for a few minutes after the switch — a spike points to a problem.
- Check that existing open user sessions were not dropped.
- Make sure background jobs (queues, cron) do not run twice on the old and new version at the same time.
Configuring servers with Ansible ahead of regular deploys is worth automating — see the article Ansible: automating server setup, and a safe full migration rollback is described in migration rollback plan.