services: # Traefik - Service Discovery and Load Balancing (replaces nginx-proxy) traefik: image: traefik:v3.0 container_name: traefik restart: unless-stopped command: - --configFile=/etc/traefik/traefik.yml ports: - "80:80" - "443:443" - "8080:8080" # Dashboard volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./config/traefik/traefik.yml:/etc/traefik/traefik.yml:ro - ./config/traefik/middleware.yml:/etc/traefik/middleware.yml:ro - ./certs:/certs:ro - traefik_data:/data networks: - frontend - backend deploy: resources: limits: memory: 512m cpus: '0.5' reservations: memory: 256m cpus: '0.25' healthcheck: test: ["CMD", "traefik", "healthcheck"] interval: 30s timeout: 10s retries: 3 start_period: 20s labels: - "traefik.enable=true" - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.motovaultpro.local`)" - "traefik.http.routers.traefik-dashboard.tls=true" - "traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080" - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$foobar" # Platform Services - Landing Page mvp-platform-landing: build: context: ./mvp-platform-services/landing dockerfile: Dockerfile args: VITE_AUTH0_DOMAIN: ${AUTH0_DOMAIN:-motovaultpro.us.auth0.com} VITE_AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_TENANTS_API_URL: http://mvp-platform-tenants:8000 container_name: mvp-platform-landing restart: unless-stopped environment: VITE_AUTH0_DOMAIN: ${AUTH0_DOMAIN:-motovaultpro.us.auth0.com} VITE_AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_TENANTS_API_URL: http://mvp-platform-tenants:8000 networks: - frontend depends_on: - mvp-platform-tenants - traefik deploy: resources: limits: memory: 1g cpus: '1.0' reservations: memory: 512m cpus: '0.5' healthcheck: test: ["CMD-SHELL", "curl -s http://localhost:3000 || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 20s labels: - "traefik.enable=true" - "traefik.http.routers.landing.rule=Host(`motovaultpro.com`)" - "traefik.http.routers.landing.tls=true" # - "traefik.http.routers.landing.middlewares=frontend-chain@file" - "traefik.http.routers.landing.priority=10" - "traefik.http.services.landing.loadbalancer.server.port=3000" - "traefik.http.services.landing.loadbalancer.healthcheck.path=/" - "traefik.http.services.landing.loadbalancer.healthcheck.interval=30s" - "traefik.http.services.landing.loadbalancer.passhostheader=true" # Platform Services - Tenants API mvp-platform-tenants: build: context: ./mvp-platform-services/tenants dockerfile: docker/Dockerfile.api container_name: mvp-platform-tenants restart: unless-stopped environment: # Core configuration loaded from files NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets # Legacy environment variables (transitional) DATABASE_URL: postgresql://platform_user:${PLATFORM_DB_PASSWORD:-platform123}@platform-postgres:5432/platform AUTH0_DOMAIN: ${AUTH0_DOMAIN:-motovaultpro.us.auth0.com} AUTH0_AUDIENCE: ${AUTH0_AUDIENCE:-https://api.motovaultpro.com} SERVICE_NAME: mvp-platform-tenants volumes: # Configuration files (K8s ConfigMap equivalent) - ./config/platform/production.yml:/app/config/production.yml:ro - ./config/shared/production.yml:/app/config/shared.yml:ro # Secrets (K8s Secrets equivalent) - ./secrets/platform/platform-db-password.txt:/run/secrets/postgres-password:ro - ./secrets/platform/tenants-api-key.txt:/run/secrets/api-key:ro - ./secrets/platform/allowed-service-tokens.txt:/run/secrets/allowed-service-tokens:ro networks: - backend - platform depends_on: - platform-postgres - platform-redis deploy: resources: limits: memory: 1g cpus: '1.0' reservations: memory: 512m cpus: '0.5' healthcheck: test: - CMD-SHELL - "python -c \"import urllib.request,sys;\ntry:\n with urllib.request.urlopen('http://localhost:8000/health', timeout=3) as r:\n sys.exit(0 if r.getcode()==200 else 1)\nexcept Exception:\n sys.exit(1)\n\"" interval: 30s timeout: 10s retries: 3 start_period: 30s labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_backend" - "traefik.http.routers.tenants-api.rule=Host(`admin.motovaultpro.com`) && PathPrefix(`/api/platform/tenants`)" - "traefik.http.routers.tenants-api.tls=true" # - "traefik.http.routers.tenants-api.middlewares=platform-chain@file" - "traefik.http.routers.tenants-api.priority=25" - "traefik.http.services.tenants-api.loadbalancer.server.port=8000" - "traefik.http.services.tenants-api.loadbalancer.healthcheck.path=/health" - "traefik.http.services.tenants-api.loadbalancer.healthcheck.interval=30s" - "traefik.http.services.tenants-api.loadbalancer.passhostheader=true" # Platform Services - Vehicles API mvp-platform-vehicles-api: build: context: ./mvp-platform-services/vehicles dockerfile: docker/Dockerfile.api container_name: mvp-platform-vehicles-api restart: unless-stopped environment: # Core configuration loaded from files NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets # Legacy environment variables (transitional) POSTGRES_HOST: mvp-platform-vehicles-db POSTGRES_PORT: 5432 POSTGRES_DATABASE: vehicles POSTGRES_USER: mvp_platform_user REDIS_HOST: mvp-platform-vehicles-redis REDIS_PORT: 6379 DEBUG: false CORS_ORIGINS: '["https://admin.motovaultpro.com", "https://motovaultpro.com"]' SERVICE_NAME: mvp-platform-vehicles-api volumes: # Configuration files (K8s ConfigMap equivalent) - ./config/platform/production.yml:/app/config/production.yml:ro - ./config/shared/production.yml:/app/config/shared.yml:ro # Secrets (K8s Secrets equivalent) - ./secrets/platform/vehicles-db-password.txt:/run/secrets/postgres-password:ro - ./secrets/platform/vehicles-api-key.txt:/run/secrets/api-key:ro - ./secrets/platform/allowed-service-tokens.txt:/run/secrets/allowed-service-tokens:ro networks: - backend - platform depends_on: - mvp-platform-vehicles-db - mvp-platform-vehicles-redis deploy: resources: limits: memory: 2g cpus: '2.0' reservations: memory: 1g cpus: '1.0' healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_backend" - "traefik.http.routers.vehicles-api.rule=Host(`admin.motovaultpro.com`) && PathPrefix(`/api/platform/vehicles`)" # Removed temporary direct routes - admin-backend now handles API gateway - "traefik.http.routers.vehicles-api.tls=true" # - "traefik.http.routers.vehicles-api.middlewares=platform-chain@file" - "traefik.http.routers.vehicles-api.priority=25" - "traefik.http.services.vehicles-api.loadbalancer.server.port=8000" - "traefik.http.services.vehicles-api.loadbalancer.healthcheck.path=/health" - "traefik.http.services.vehicles-api.loadbalancer.healthcheck.interval=30s" - "traefik.http.services.vehicles-api.loadbalancer.passhostheader=true" # Application Services - Backend API admin-backend: build: context: ./backend dockerfile: Dockerfile cache_from: - node:20-alpine container_name: admin-backend restart: unless-stopped environment: # Core environment for application startup NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets # Force database configuration DB_HOST: admin-postgres DB_PORT: 5432 DB_NAME: motovaultpro DB_USER: postgres DB_PASSWORD: localdev123 # Essential environment variables (until file-based config is fully implemented) DATABASE_URL: postgresql://postgres:localdev123@admin-postgres:5432/motovaultpro REDIS_URL: redis://admin-redis:6379 REDIS_HOST: admin-redis REDIS_PORT: 6379 MINIO_ENDPOINT: admin-minio MINIO_PORT: 9000 MINIO_BUCKET: motovaultpro AUTH0_DOMAIN: ${AUTH0_DOMAIN:-motovaultpro.us.auth0.com} AUTH0_AUDIENCE: ${AUTH0_AUDIENCE:-https://api.motovaultpro.com} AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-your-auth0-client-id} AUTH0_CLIENT_SECRET: ${AUTH0_CLIENT_SECRET:-your-auth0-client-secret} GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY:-your-google-maps-api-key} PLATFORM_VEHICLES_API_URL: http://mvp-platform-vehicles-api:8000 PLATFORM_TENANTS_API_URL: http://mvp-platform-tenants:8000 PLATFORM_VEHICLES_API_KEY: mvp-platform-vehicles-secret-key PLATFORM_TENANTS_API_KEY: mvp-platform-tenants-secret-key volumes: # Configuration files (K8s ConfigMap equivalent) - ./config/app/production.yml:/app/config/production.yml:ro - ./config/shared/production.yml:/app/config/shared.yml:ro # Secrets (K8s Secrets equivalent) - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro - ./secrets/app/minio-access-key.txt:/run/secrets/minio-access-key:ro - ./secrets/app/minio-secret-key.txt:/run/secrets/minio-secret-key:ro - ./secrets/app/platform-vehicles-api-key.txt:/run/secrets/platform-vehicles-api-key:ro - ./secrets/app/platform-tenants-api-key.txt:/run/secrets/platform-tenants-api-key:ro - ./secrets/app/service-auth-token.txt:/run/secrets/service-auth-token: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 networks: - backend - database - platform - egress # External connectivity for Auth0 JWT validation depends_on: - admin-postgres - admin-redis - admin-minio - mvp-platform-vehicles-api - mvp-platform-tenants deploy: resources: limits: memory: 2g cpus: '2.0' reservations: memory: 1g cpus: '1.0' 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: 30s timeout: 10s retries: 3 start_period: 40s labels: - "traefik.enable=true" - "traefik.docker.network=motovaultpro_backend" # Main API router for admin tenant (correct multi-tenant architecture) - "traefik.http.routers.admin-api.rule=Host(`admin.motovaultpro.com`) && PathPrefix(`/api`)" - "traefik.http.routers.admin-api.tls=true" # - "traefik.http.routers.admin-api.middlewares=api-chain@file" - "traefik.http.routers.admin-api.priority=20" # Health check router for admin tenant (bypass auth) - "traefik.http.routers.admin-health.rule=Host(`admin.motovaultpro.com`) && Path(`/api/health`)" - "traefik.http.routers.admin-health.tls=true" # - "traefik.http.routers.admin-health.middlewares=health-check-chain@file" - "traefik.http.routers.admin-health.priority=30" # Service configuration - "traefik.http.services.admin-api.loadbalancer.server.port=3001" - "traefik.http.services.admin-api.loadbalancer.healthcheck.path=/health" - "traefik.http.services.admin-api.loadbalancer.healthcheck.interval=30s" - "traefik.http.services.admin-api.loadbalancer.healthcheck.timeout=10s" # Circuit breaker and retries - "traefik.http.services.admin-api.loadbalancer.passhostheader=true" # Application Services - Frontend SPA admin-frontend: build: context: ./frontend dockerfile: Dockerfile cache_from: - node:20-alpine - nginx:alpine args: 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} VITE_API_BASE_URL: ${VITE_API_BASE_URL:-/api} container_name: admin-frontend restart: unless-stopped environment: VITE_TENANT_ID: ${TENANT_ID:-admin} 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} networks: - frontend depends_on: - admin-backend deploy: resources: limits: memory: 1g cpus: '1.0' reservations: memory: 512m cpus: '0.5' healthcheck: test: ["CMD-SHELL", "curl -s http://localhost:3000 || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 20s labels: - "traefik.enable=true" - "traefik.http.routers.admin-app.rule=Host(`admin.motovaultpro.com`) && !PathPrefix(`/api`)" - "traefik.http.routers.admin-app.tls=true" # - "traefik.http.routers.admin-app.middlewares=frontend-chain@file" - "traefik.http.routers.admin-app.priority=10" - "traefik.http.services.admin-app.loadbalancer.server.port=3000" - "traefik.http.services.admin-app.loadbalancer.healthcheck.path=/" - "traefik.http.services.admin-app.loadbalancer.healthcheck.interval=30s" - "traefik.http.services.admin-app.loadbalancer.passhostheader=true" # Database Services - Application PostgreSQL admin-postgres: image: postgres:15-alpine container_name: admin-postgres restart: unless-stopped environment: POSTGRES_DB: motovaultpro POSTGRES_USER: postgres POSTGRES_PASSWORD: localdev123 POSTGRES_INITDB_ARGS: --encoding=UTF8 volumes: - admin_postgres_data:/var/lib/postgresql/data networks: - database ports: - "5432:5432" # Development access only deploy: resources: limits: memory: 2g cpus: '2.0' reservations: memory: 1g cpus: '1.0' healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 start_period: 30s # Database Services - Application Redis admin-redis: image: redis:7-alpine container_name: admin-redis restart: unless-stopped command: redis-server --appendonly yes volumes: - admin_redis_data:/data networks: - database ports: - "6379:6379" # Development access only deploy: resources: limits: memory: 512m cpus: '0.5' reservations: memory: 256m cpus: '0.25' healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # Database Services - Object Storage admin-minio: image: minio/minio:latest container_name: admin-minio restart: unless-stopped command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin123 volumes: - admin_minio_data:/data networks: - database ports: - "9000:9000" # Development access only - "9001:9001" # Console access deploy: resources: limits: memory: 1g cpus: '1.0' reservations: memory: 512m cpus: '0.5' healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 # Platform Infrastructure - PostgreSQL platform-postgres: image: postgres:15-alpine container_name: platform-postgres restart: unless-stopped environment: POSTGRES_DB: platform POSTGRES_USER: platform_user POSTGRES_PASSWORD: ${PLATFORM_DB_PASSWORD:-platform123} POSTGRES_INITDB_ARGS: --encoding=UTF8 volumes: - platform_postgres_data:/var/lib/postgresql/data - ./mvp-platform-services/tenants/sql/schema:/docker-entrypoint-initdb.d networks: - platform ports: - "5434:5432" # Development access only deploy: resources: limits: memory: 2g cpus: '2.0' reservations: memory: 1g cpus: '1.0' healthcheck: test: ["CMD-SHELL", "pg_isready -U platform_user -d platform"] interval: 10s timeout: 5s retries: 5 # Platform Infrastructure - Redis platform-redis: image: redis:7-alpine container_name: platform-redis restart: unless-stopped command: redis-server --appendonly yes volumes: - platform_redis_data:/data networks: - platform ports: - "6381:6379" # Development access only deploy: resources: limits: memory: 512m cpus: '0.5' reservations: memory: 256m cpus: '0.25' healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # Platform Services - Vehicles Database mvp-platform-vehicles-db: image: postgres:15-alpine container_name: mvp-platform-vehicles-db restart: unless-stopped command: 'postgres -c shared_buffers=4GB -c work_mem=256MB -c maintenance_work_mem=1GB -c effective_cache_size=12GB -c max_connections=100 -c checkpoint_completion_target=0.9 -c wal_buffers=256MB -c max_wal_size=8GB -c min_wal_size=2GB -c synchronous_commit=off -c full_page_writes=off -c fsync=off -c random_page_cost=1.1 -c seq_page_cost=1 -c max_worker_processes=8 -c max_parallel_workers=8 -c max_parallel_workers_per_gather=4 -c max_parallel_maintenance_workers=4' environment: POSTGRES_DB: vehicles POSTGRES_USER: mvp_platform_user POSTGRES_PASSWORD: platform123 POSTGRES_INITDB_ARGS: --encoding=UTF8 volumes: - platform_vehicles_data:/var/lib/postgresql/data - ./mvp-platform-services/vehicles/sql/schema:/docker-entrypoint-initdb.d networks: - platform ports: - "5433:5432" # Development access only deploy: resources: limits: memory: 6g cpus: '6.0' reservations: memory: 4g cpus: '4.0' healthcheck: test: ["CMD-SHELL", "pg_isready -U mvp_platform_user -d vehicles"] interval: 10s timeout: 5s retries: 5 # Platform Services - Vehicles Redis mvp-platform-vehicles-redis: image: redis:7-alpine container_name: mvp-platform-vehicles-redis restart: unless-stopped command: redis-server --appendonly yes volumes: - platform_vehicles_redis_data:/data networks: - platform ports: - "6380:6379" # Development access only deploy: resources: limits: memory: 1g cpus: '1.0' reservations: memory: 512m cpus: '0.5' healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # Network Definition - 4-Tier Isolation networks: frontend: driver: bridge internal: false # Only for Traefik public access labels: - "com.motovaultpro.network=frontend" - "com.motovaultpro.purpose=public-traffic-only" backend: driver: bridge internal: true # Complete isolation from host labels: - "com.motovaultpro.network=backend" - "com.motovaultpro.purpose=api-services" database: driver: bridge internal: true # Application data isolation labels: - "com.motovaultpro.network=database" - "com.motovaultpro.purpose=app-data-layer" platform: driver: bridge internal: true # Platform microservices isolation labels: - "com.motovaultpro.network=platform" - "com.motovaultpro.purpose=platform-services" egress: driver: bridge internal: false # External connectivity for Auth0, APIs labels: - "com.motovaultpro.network=egress" - "com.motovaultpro.purpose=external-api-access" # Volume Definitions volumes: traefik_data: null platform_postgres_data: null platform_redis_data: null admin_postgres_data: null admin_redis_data: null admin_minio_data: null platform_vehicles_data: null platform_vehicles_redis_data: null