Introduction
If you’re a DevOps lead tasked with keeping a web service online 24/7, you’ve probably wrestled with the dreaded "service down" alert during a deploy. The good news is that with Docker, Nginx, and a disciplined blue‑green workflow you can push new code without ever dropping a single request. This checklist walks you through the concrete steps, from image build to observability, so you can ship changes confidently.
1. Build Production‑Ready Docker Images
1.1 Use Multi‑Stage Builds
A lean final image reduces attack surface and speeds up pull times. Start with a builder stage that contains all compile‑time dependencies, then copy only the artifacts you need.
# ---- Builder ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Runtime ----
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["node", "dist/index.js"]
1.2 Tag Images with Semantic Versions
Never rely on the latest
tag in production. Tag each build with vMAJOR.MINOR.PATCH
and also push a sha256
digest for immutable references.
docker build -t myapp:v1.4.2 .
docker tag myapp:v1.4.2 myregistry.example.com/myapp:v1.4.2
docker push myregistry.example.com/myapp:v1.4.2
2. Configure Nginx as a Reverse Proxy
Nginx will sit in front of your containers, handling TLS termination, health checks, and graceful reloads.
# /etc/nginx/conf.d/app.conf
upstream app_blue {
server 10.0.1.10:3000;
server 10.0.1.11:3000;
}
upstream app_green {
server 10.0.2.10:3000;
server 10.0.2.11:3000;
}
server {
listen 80;
server_name api.example.com;
# Switch between blue and green via a variable
set $backend app_blue;
if ($http_x_deploy_color = "green") {
set $backend app_green;
}
location / {
proxy_pass http://$backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
- TLS – Use Let’s Encrypt or your internal PKI and terminate at Nginx.
-
Health Checks – Enable
proxy_next_upstream
to skip unhealthy containers.
3. Blue‑Green Deployment Workflow
3.1 Spin Up the New Environment
- Deploy the new Docker image to a green swarm/cluster.
- Verify health endpoints (
/healthz
) return200
. - Run integration smoke tests against the green stack.
3.2 Flip Traffic Atomically
Set a custom header X-Deploy-Color: green
on the load balancer or use a feature flag service. Nginx will start routing new requests to the green upstream while existing connections finish on blue.
# Example using curl to test the green stack before cutover
curl -H "Host: api.example.com" -H "X-Deploy-Color: green" http://nginx.example.com/healthz
3.3 Decommission the Old Stack
Once the green environment has handled a full traffic window (e.g., 5 minutes), gracefully shut down the blue containers:
docker service rm myapp_blue
If something goes wrong, you can instantly revert by removing the X-Deploy-Color
header or setting it back to blue
.
4. Zero‑Downtime Database Migrations
Even with flawless container swaps, schema changes can still cause hiccups. Follow these patterns:
- Add‑only migrations – Introduce new columns with defaults, avoid dropping columns until the old code is retired.
- Backfill in background – Use a worker queue to populate new fields without locking tables.
- Versioned APIs – Keep the old endpoint version alive while the new one rolls out.
Sample Migration (PostgreSQL)
-- 2024-09-23: Add `is_active` flag with default true
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;
-- Populate for legacy rows (if needed)
UPDATE users SET is_active = true WHERE is_active IS NULL;
Run migrations before you switch traffic to the green environment.
5. Observability: Logging & Metrics
A zero‑downtime rollout is only as good as the visibility you have.
5.1 Centralized Logging
- Ship container stdout/stderr to a log aggregator (e.g., Loki, Elasticsearch).
- Tag each log line with
deployment_color
(blue
/green
).
# docker-compose.yml snippet for Loki driver
logging:
driver: "json-file"
options:
tag: "{{.Name}}-{{.Label "deployment_color"}}"
5.2 Metrics & Alerts
- Export Prometheus metrics from both blue and green services.
- Create alerts on error rate spikes, latency regressions, or sudden drop in request count.
5.3 Automated Rollback
If an alert fires within the first 10 minutes of cutover, trigger a CI/CD job that:
- Removes the
X-Deploy-Color: green
header. - Restarts blue services if they were scaled down.
6. CI/CD Pipeline Integration
Tie everything together with a simple GitHub Actions workflow.
name: Deploy Blue‑Green
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: |
TAG=$(git rev-parse --short HEAD)
docker build -t myregistry.example.com/myapp:$TAG .
docker push myregistry.example.com/myapp:$TAG
deploy-green:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to green swarm
run: |
docker service update --image myregistry.example.com/myapp:$TAG myapp_green
- name: Smoke test
run: |
curl -sSf -H "Host: api.example.com" -H "X-Deploy-Color: green" http://nginx.example.com/healthz
- name: Promote traffic
run: |
curl -X POST -H "Authorization: Bearer $TOKEN" https://nginx.example.com/api/switch-green
The workflow builds, pushes, deploys to the green stack, validates health, and finally flips traffic via a small API endpoint you expose on Nginx.
7. Checklist Recap
- [ ] Docker: Multi‑stage, semantic tags, minimal runtime image.
- [ ] Nginx: Upstreams for blue/green, TLS termination, health checks.
- [ ] Blue‑Green Steps: Deploy green, smoke test, switch traffic, retire blue.
- [ ] DB Migrations: Add‑only, backfill, versioned APIs.
- [ ] Observability: Central logs, Prometheus metrics, alert‑driven rollback.
- [ ] CI/CD: Automated build, green deploy, verification, traffic promotion.
Follow this list on every release and you’ll eliminate the dreaded downtime window.
Closing Thoughts
Zero‑downtime deployments aren’t magic; they’re the result of disciplined automation, clear separation of environments, and robust monitoring. By treating blue and green as first‑class citizens in your Docker‑Nginx stack, you gain the ability to roll forward or back with a single header change. If you need help shipping this, the team at https://ramerlabs.com can help.
Top comments (0)