version: '3.7' services: # ========================================== # Nginx (Frontend Production) # ========================================== nginx: build: context: ./frontend target: production-stage args: # Defaults to /api/v1 which works with the proxy config in nginx.conf - VITE_API_BASE_URL=/api/v1 ports: - "80:80" # - "443:443" # Uncomment if you configure SSL in nginx.conf depends_on: - backend restart: always # ========================================== # Backend (FastAPI) # ========================================== backend: build: context: ./backend environment: - TZ=Asia/Shanghai - MYSQL_SERVER=db - MYSQL_PORT=3306 - MYSQL_USER=uap_user - MYSQL_PASSWORD=uap_pass - MYSQL_DB=uap_db - REDIS_HOST=redis - REDIS_PORT=6379 - HYDRA_ADMIN_URL=http://hydra:4445 # CORS: Add your production domain here - BACKEND_CORS_ORIGINS=["http://localhost", "http://127.0.0.1", "http://YOUR_DOMAIN_OR_IP"] depends_on: db: condition: service_healthy redis: condition: service_healthy hydra: condition: service_started volumes: - ./backend/logs:/app/logs # Persist logs # - ./backend:/app # Remove hot reload in production for stability restart: always # ========================================== # Database (MySQL) # ========================================== db: image: mysql:8.0 command: --default-authentication-plugin=mysql_native_password restart: always environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: root_password # CHANGE THIS IN PRODUCTION MYSQL_DATABASE: uap_db MYSQL_USER: uap_user MYSQL_PASSWORD: uap_pass # CHANGE THIS IN PRODUCTION volumes: - db_data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "uap_user", "-p$$MYSQL_PASSWORD"] interval: 10s timeout: 5s retries: 10 start_period: 10s # ========================================== # Redis # ========================================== redis: image: redis:alpine restart: always volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # ========================================== # Ory Hydra Services # ========================================== hydra-migrate: image: oryd/hydra:v2.2.0 environment: - TZ=Asia/Shanghai - DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 command: migrate sql -e --yes depends_on: postgresd: condition: service_healthy restart: on-failure hydra: image: oryd/hydra:v2.2.0 depends_on: hydra-migrate: condition: service_completed_successfully ports: - "4444:4444" # Public port - "4445:4445" # Admin port - "5555:5555" # Token port? (Usually not needed if 4444 is used) # Production command: Remove --dev, remove -c if config file missing # Using --dangerous-force-http because we assume SSL is terminated by an external Nginx/LoadBalancer # If exposing directly without SSL, this is INSECURE but necessary to start. command: serve all --dangerous-force-http environment: - TZ=Asia/Shanghai - DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 # IMPORTANT: Change URLs to your production domain - URLS_SELF_ISSUER=http://192.168.254.105:4444 - URLS_CONSENT=http://192.168.254.105/consent - URLS_LOGIN=http://192.168.254.105/login - URLS_LOGOUT=http://192.168.254.105/login # IMPORTANT: Change these secrets! - SECRETS_SYSTEM=youReallyNeedToChangeThis - OIDC_SUBJECT_IDENTIFIERS_SUPPORTED_TYPES=public,pairwise - OIDC_SUBJECT_IDENTIFIERS_PAIRWISE_SALT=youReallyNeedToChangeThis # CORS - SERVE_PUBLIC_CORS_ENABLED=true - SERVE_PUBLIC_CORS_ALLOWED_ORIGINS=* - SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE,PATCH,OPTIONS - SERVE_PUBLIC_CORS_ALLOWED_HEADERS=Authorization,Content-Type - SERVE_ADMIN_CORS_ENABLED=true - SERVE_ADMIN_CORS_ALLOWED_ORIGINS=* postgresd: image: postgres:15 environment: - TZ=Asia/Shanghai - POSTGRES_USER=hydra - POSTGRES_PASSWORD=secret # CHANGE THIS - POSTGRES_DB=hydra volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U hydra"] interval: 10s timeout: 5s retries: 5 start_period: 10s volumes: db_data: postgres_data: redis_data: