DEV Community

Ramer Labs
Ramer Labs

Posted on

The Ultimate Checklist for Zero‑Downtime Deploys with Docker & Nginx

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"]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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

  1. Deploy the new Docker image to a green swarm/cluster.
  2. Verify health endpoints (/healthz) return 200.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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"}}"
Enter fullscreen mode Exit fullscreen mode

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:

  1. Removes the X-Deploy-Color: green header.
  2. 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
Enter fullscreen mode Exit fullscreen mode

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)