version: '3.8' services: # PostgreSQL Database xpeditis-db: image: postgres:15-alpine restart: unless-stopped volumes: - xpeditis_db_data:/var/lib/postgresql/data environment: POSTGRES_DB: xpeditis_prod POSTGRES_USER: xpeditis POSTGRES_PASSWORD: xpeditis_prod_password_CHANGE_ME PGDATA: /var/lib/postgresql/data/pgdata networks: - xpeditis_internal healthcheck: test: ["CMD-SHELL", "pg_isready -U xpeditis"] interval: 10s timeout: 5s retries: 5 deploy: placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 5s max_attempts: 3 # Redis Cache xpeditis-redis: image: redis:7-alpine restart: unless-stopped command: redis-server --requirepass xpeditis_redis_password_CHANGE_ME --appendonly yes volumes: - xpeditis_redis_data:/data networks: - xpeditis_internal healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 10s timeout: 3s retries: 5 deploy: placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 5s max_attempts: 3 # MinIO S3 Storage xpeditis-minio: image: minio/minio:latest restart: unless-stopped command: server /data --console-address ":9001" volumes: - xpeditis_minio_data:/data environment: MINIO_ROOT_USER: minioadmin_CHANGE_ME MINIO_ROOT_PASSWORD: minioadmin_password_CHANGE_ME networks: - xpeditis_internal - traefik_network labels: # MinIO API - "traefik.enable=true" - "traefik.http.routers.xpeditis-minio-api.rule=Host(`s3.xpeditis.com`)" - "traefik.http.routers.xpeditis-minio-api.entrypoints=websecure" - "traefik.http.routers.xpeditis-minio-api.tls=true" - "traefik.http.routers.xpeditis-minio-api.tls.certresolver=letsencrypt" - "traefik.http.routers.xpeditis-minio-api.service=xpeditis-minio-api" - "traefik.http.services.xpeditis-minio-api.loadbalancer.server.port=9000" # MinIO Console - "traefik.http.routers.xpeditis-minio-console.rule=Host(`minio.xpeditis.com`)" - "traefik.http.routers.xpeditis-minio-console.entrypoints=websecure" - "traefik.http.routers.xpeditis-minio-console.tls=true" - "traefik.http.routers.xpeditis-minio-console.tls.certresolver=letsencrypt" - "traefik.http.routers.xpeditis-minio-console.service=xpeditis-minio-console" - "traefik.http.services.xpeditis-minio-console.loadbalancer.server.port=9001" - "traefik.docker.network=traefik_network" # HTTP to HTTPS redirect - "traefik.http.routers.xpeditis-minio-http.rule=Host(`s3.xpeditis.com`) || Host(`minio.xpeditis.com`)" - "traefik.http.routers.xpeditis-minio-http.entrypoints=web" - "traefik.http.routers.xpeditis-minio-http.middlewares=xpeditis-redirect" - "traefik.http.middlewares.xpeditis-redirect.redirectscheme.scheme=https" - "traefik.http.middlewares.xpeditis-redirect.redirectscheme.permanent=true" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 deploy: placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 5s max_attempts: 3 # Backend API (NestJS) xpeditis-backend: image: xpeditis/backend:latest restart: unless-stopped environment: # Node Environment NODE_ENV: production PORT: 4000 # Database DATABASE_HOST: xpeditis-db DATABASE_PORT: 5432 DATABASE_USER: xpeditis DATABASE_PASSWORD: xpeditis_prod_password_CHANGE_ME DATABASE_NAME: xpeditis_prod DATABASE_SSL: "false" DATABASE_SYNC: "false" DATABASE_LOGGING: "false" # Redis REDIS_HOST: xpeditis-redis REDIS_PORT: 6379 REDIS_PASSWORD: xpeditis_redis_password_CHANGE_ME REDIS_TTL: 900 # JWT JWT_SECRET: your-super-secret-jwt-key-CHANGE_ME-min-32-characters JWT_ACCESS_EXPIRATION: 15m JWT_REFRESH_EXPIRATION: 7d # S3/MinIO AWS_S3_ENDPOINT: http://xpeditis-minio:9000 AWS_REGION: us-east-1 AWS_ACCESS_KEY_ID: minioadmin_CHANGE_ME AWS_SECRET_ACCESS_KEY: minioadmin_password_CHANGE_ME AWS_S3_BUCKET: xpeditis-documents AWS_S3_FORCE_PATH_STYLE: "true" # CORS CORS_ORIGIN: https://app.xpeditis.com,https://www.xpeditis.com # Rate Limiting RATE_LIMIT_TTL: 60 RATE_LIMIT_MAX: 100 # Email (Placeholder - configure based on your email provider) EMAIL_HOST: smtp.example.com EMAIL_PORT: 587 EMAIL_USER: noreply@xpeditis.com EMAIL_PASSWORD: email_password_CHANGE_ME EMAIL_FROM: "Xpeditis " # Sentry (Optional - for error tracking) SENTRY_DSN: "" SENTRY_ENVIRONMENT: production SENTRY_TRACES_SAMPLE_RATE: 0.1 # App URLs FRONTEND_URL: https://app.xpeditis.com API_URL: https://api.xpeditis.com networks: - xpeditis_internal - traefik_network labels: - "traefik.enable=true" # API Routes - "traefik.http.routers.xpeditis-api.rule=Host(`api.xpeditis.com`)" - "traefik.http.routers.xpeditis-api.entrypoints=websecure" - "traefik.http.routers.xpeditis-api.tls=true" - "traefik.http.routers.xpeditis-api.tls.certresolver=letsencrypt" - "traefik.http.routers.xpeditis-api.priority=100" - "traefik.http.services.xpeditis-api.loadbalancer.server.port=4000" - "traefik.http.routers.xpeditis-api.middlewares=xpeditis-api-headers,xpeditis-api-ratelimit" - "traefik.docker.network=traefik_network" # Middleware Headers - "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Forwarded-Proto=https" - "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Forwarded-For=" - "traefik.http.middlewares.xpeditis-api-headers.headers.customRequestHeaders.X-Real-IP=" - "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowOriginList=https://app.xpeditis.com,https://www.xpeditis.com" - "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowMethods=GET,POST,PUT,PATCH,DELETE,OPTIONS" - "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlAllowHeaders=*" - "traefik.http.middlewares.xpeditis-api-headers.headers.accessControlMaxAge=3600" # Rate Limiting - "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.average=100" - "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.burst=200" - "traefik.http.middlewares.xpeditis-api-ratelimit.ratelimit.period=1m" # HTTP to HTTPS redirect - "traefik.http.routers.xpeditis-api-http.rule=Host(`api.xpeditis.com`)" - "traefik.http.routers.xpeditis-api-http.entrypoints=web" - "traefik.http.routers.xpeditis-api-http.priority=100" - "traefik.http.routers.xpeditis-api-http.middlewares=xpeditis-redirect" - "traefik.http.routers.xpeditis-api-http.service=xpeditis-api" depends_on: - xpeditis-db - xpeditis-redis - xpeditis-minio healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s deploy: replicas: 2 placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 10s max_attempts: 3 update_config: parallelism: 1 delay: 10s order: start-first resources: limits: cpus: '1.0' memory: 1G reservations: cpus: '0.5' memory: 512M # Frontend (Next.js) xpeditis-frontend: image: xpeditis/frontend:latest restart: unless-stopped environment: NODE_ENV: production NEXT_PUBLIC_API_URL: https://api.xpeditis.com NEXT_PUBLIC_WS_URL: wss://api.xpeditis.com networks: - traefik_network labels: - "traefik.enable=true" # Frontend Routes - "traefik.http.routers.xpeditis-app.rule=Host(`app.xpeditis.com`) || Host(`www.xpeditis.com`)" - "traefik.http.routers.xpeditis-app.entrypoints=websecure" - "traefik.http.routers.xpeditis-app.tls=true" - "traefik.http.routers.xpeditis-app.tls.certresolver=letsencrypt" - "traefik.http.routers.xpeditis-app.priority=50" - "traefik.http.services.xpeditis-app.loadbalancer.server.port=3000" - "traefik.http.routers.xpeditis-app.middlewares=xpeditis-app-headers" - "traefik.docker.network=traefik_network" # Middleware Headers - "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Forwarded-Proto=https" - "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Forwarded-For=" - "traefik.http.middlewares.xpeditis-app-headers.headers.customRequestHeaders.X-Real-IP=" # Security Headers - "traefik.http.middlewares.xpeditis-app-headers.headers.stsSeconds=31536000" - "traefik.http.middlewares.xpeditis-app-headers.headers.stsIncludeSubdomains=true" - "traefik.http.middlewares.xpeditis-app-headers.headers.stsPreload=true" - "traefik.http.middlewares.xpeditis-app-headers.headers.forceSTSHeader=true" - "traefik.http.middlewares.xpeditis-app-headers.headers.contentTypeNosniff=true" - "traefik.http.middlewares.xpeditis-app-headers.headers.browserXssFilter=true" # HTTP to HTTPS redirect - "traefik.http.routers.xpeditis-app-http.rule=Host(`app.xpeditis.com`) || Host(`www.xpeditis.com`)" - "traefik.http.routers.xpeditis-app-http.entrypoints=web" - "traefik.http.routers.xpeditis-app-http.priority=50" - "traefik.http.routers.xpeditis-app-http.middlewares=xpeditis-redirect" - "traefik.http.routers.xpeditis-app-http.service=xpeditis-app" depends_on: - xpeditis-backend healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"] interval: 30s timeout: 10s retries: 3 start_period: 40s deploy: replicas: 2 placement: constraints: - node.role == manager restart_policy: condition: on-failure delay: 10s max_attempts: 3 update_config: parallelism: 1 delay: 10s order: start-first resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M volumes: xpeditis_db_data: driver: local xpeditis_redis_data: driver: local xpeditis_minio_data: driver: local networks: traefik_network: external: true xpeditis_internal: driver: overlay internal: true