# Blue-Green Deployment Overlay for MotoVaultPro # Usage: docker compose -f docker-compose.yml -f docker-compose.blue-green.yml up -d # # This overlay defines blue and green stacks that share the same database layer. # Traffic routing is handled by Traefik's weighted load balancer. # # Stack naming: # BLUE: mvp-frontend-blue, mvp-backend-blue # GREEN: mvp-frontend-green, mvp-backend-green # # Shared services (from base compose): # mvp-traefik, mvp-postgres, mvp-redis services: # ======================================== # BLUE Stack - Frontend # ======================================== mvp-frontend-blue: image: ${FRONTEND_IMAGE:-git.motovaultpro.com/egullickson/frontend:latest} container_name: mvp-frontend-blue restart: unless-stopped environment: VITE_API_BASE_URL: /api VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} SECRETS_DIR: /run/secrets volumes: - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro networks: - frontend depends_on: - mvp-backend-blue healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:3000 || exit 1"] interval: 10s timeout: 5s retries: 3 start_period: 10s deploy: resources: limits: memory: 512M labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_frontend" - "com.motovaultpro.stack=blue" - "com.motovaultpro.service=frontend" # ======================================== # BLUE Stack - Backend # ======================================== mvp-backend-blue: image: ${BACKEND_IMAGE:-git.motovaultpro.com/egullickson/backend:latest} container_name: mvp-backend-blue restart: unless-stopped environment: NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets DATABASE_HOST: mvp-postgres REDIS_HOST: mvp-redis STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn volumes: - ./config/app/production.yml:/app/config/production.yml:ro - ./config/shared/production.yml:/app/config/shared.yml:ro - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro - ./secrets/app/auth0-client-secret.txt:/run/secrets/auth0-client-secret:ro - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro - ./secrets/app/resend-api-key.txt:/run/secrets/resend-api-key:ro - ./secrets/app/auth0-management-client-id.txt:/run/secrets/auth0-management-client-id:ro - ./secrets/app/auth0-management-client-secret.txt:/run/secrets/auth0-management-client-secret:ro - ./secrets/app/stripe-secret-key.txt:/run/secrets/stripe-secret-key:ro - ./secrets/app/stripe-webhook-secret.txt:/run/secrets/stripe-webhook-secret:ro - ./data/documents:/app/data/documents - ./data/backups:/app/data/backups networks: - backend - database depends_on: - mvp-postgres - mvp-redis healthcheck: test: - CMD-SHELL - node -e "require('http').get('http://localhost:3001/health', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))" interval: 10s timeout: 5s retries: 3 start_period: 30s deploy: resources: limits: memory: 1G labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_backend" - "com.motovaultpro.stack=blue" - "com.motovaultpro.service=backend" # ======================================== # GREEN Stack - Frontend # ======================================== mvp-frontend-green: image: ${FRONTEND_IMAGE:-git.motovaultpro.com/egullickson/frontend:latest} container_name: mvp-frontend-green restart: unless-stopped environment: VITE_API_BASE_URL: /api VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} SECRETS_DIR: /run/secrets volumes: - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro networks: - frontend depends_on: - mvp-backend-green healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:3000 || exit 1"] interval: 10s timeout: 5s retries: 3 start_period: 10s deploy: resources: limits: memory: 512M labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_frontend" - "com.motovaultpro.stack=green" - "com.motovaultpro.service=frontend" # ======================================== # GREEN Stack - Backend # ======================================== mvp-backend-green: image: ${BACKEND_IMAGE:-git.motovaultpro.com/egullickson/backend:latest} container_name: mvp-backend-green restart: unless-stopped environment: NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets DATABASE_HOST: mvp-postgres REDIS_HOST: mvp-redis STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn volumes: - ./config/app/production.yml:/app/config/production.yml:ro - ./config/shared/production.yml:/app/config/shared.yml:ro - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro - ./secrets/app/auth0-client-secret.txt:/run/secrets/auth0-client-secret:ro - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro - ./secrets/app/resend-api-key.txt:/run/secrets/resend-api-key:ro - ./secrets/app/auth0-management-client-id.txt:/run/secrets/auth0-management-client-id:ro - ./secrets/app/auth0-management-client-secret.txt:/run/secrets/auth0-management-client-secret:ro - ./secrets/app/stripe-secret-key.txt:/run/secrets/stripe-secret-key:ro - ./secrets/app/stripe-webhook-secret.txt:/run/secrets/stripe-webhook-secret:ro - ./data/documents:/app/data/documents - ./data/backups:/app/data/backups networks: - backend - database depends_on: - mvp-postgres - mvp-redis healthcheck: test: - CMD-SHELL - node -e "require('http').get('http://localhost:3001/health', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))" interval: 10s timeout: 5s retries: 3 start_period: 30s deploy: resources: limits: memory: 1G labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_backend" - "com.motovaultpro.stack=green" - "com.motovaultpro.service=backend" # ======================================== # Override Traefik to add dynamic config # ======================================== mvp-traefik: volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./config/traefik/traefik.yml:/etc/traefik/traefik.yml:ro - ./config/traefik/dynamic:/etc/traefik/dynamic:ro - ./certs:/certs:ro - ./data/traefik:/data - ./secrets/app/cloudflare-dns-token.txt:/run/secrets/cloudflare-dns-token:ro