Deploy with zero downtime using Traefik’s dynamic routing. Switch traffic between blue and green deployments by updating environment variables, with automatic health checks.

The setup

Traefik automatically discovers services and routes traffic based on labels:

# compose.yml
services:
  traefik:
    image: traefik:v3.0
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
    ports:
      - "80:80"
      - "8080:8080"  # Traefik dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - web

  app-blue:
    image: myapp:${BLUE_VERSION:-v1.0}
    labels:
      - "traefik.enable=${BLUE_ENABLED:-true}"
      - "traefik.http.routers.app-blue.rule=Host(`app.localhost`)"
      - "traefik.http.routers.app-blue.priority=1"
      - "traefik.http.services.app-blue.loadbalancer.server.port=3000"
    networks:
      - web
    environment:
      VERSION: blue

  app-green:
    image: myapp:${GREEN_VERSION:-v2.0}
    labels:
      - "traefik.enable=${GREEN_ENABLED:-false}"  # Start disabled
      - "traefik.http.routers.app-green.rule=Host(`app.localhost`)"
      - "traefik.http.routers.app-green.priority=2"  # Higher priority when enabled
      - "traefik.http.services.app-green.loadbalancer.server.port=3000"
    networks:
      - web
    environment:
      VERSION: green

networks:
  web:
    driver: bridge

Deployment workflow

Switch traffic by recreating containers with updated labels:

# 1. Deploy with blue active
docker compose up -d

# 2. Update green to new version
GREEN_VERSION=v2.0 docker compose up -d app-green

# 3. Switch traffic to green (recreate with new labels)
BLUE_ENABLED=false GREEN_ENABLED=true docker compose up -d

# Traefik detects the change and switches routing instantly!

For this to work, update your compose file:

services:
  app-blue:
    labels:
      - "traefik.enable=${BLUE_ENABLED:-true}"

  app-green:
    labels:
      - "traefik.enable=${GREEN_ENABLED:-false}"

Weighted canary deployment

Gradually shift traffic from blue to green:

services:
  app-blue:
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.app.loadbalancer.server.port=3000"
      - "traefik.http.services.app.loadbalancer.sticky=true"
      - "traefik.http.services.app.loadbalancer.weight=90"  # 90% traffic

  app-green:
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.app.loadbalancer.server.port=3000"
      - "traefik.http.services.app.loadbalancer.weight=10"  # 10% traffic

Adjust weights to gradually migrate:

# Shift to 50/50
BLUE_WEIGHT=50 GREEN_WEIGHT=50 docker compose up -d

# Full migration to green
BLUE_WEIGHT=0 GREEN_WEIGHT=100 docker compose up -d

Update your compose file to use variables:

services:
  app-blue:
    labels:
      - "traefik.http.services.app.loadbalancer.weight=${BLUE_WEIGHT:-90}"
  app-green:
    labels:
      - "traefik.http.services.app.loadbalancer.weight=${GREEN_WEIGHT:-10}"

Health-check based routing

Traefik only routes to healthy services:

services:
  app-green:
    image: myapp:v2.0
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.app-green.loadbalancer.healthcheck.path=/health"
      - "traefik.http.services.app-green.loadbalancer.healthcheck.interval=10s"

Instant rollback

# Revert to blue immediately
BLUE_ENABLED=true GREEN_ENABLED=false docker compose up -d

# Containers recreate quickly and Traefik switches routing!

Pro tip

Monitor deployments via Traefik dashboard at http://localhost:8080. You can see:

  • Active services and their health
  • Current routing rules
  • Real-time traffic distribution
  • Response times and error rates

Further reading