name: wizard services: wizard_website: build: ./wizard_website restart: unless-stopped deploy: resources: limits: cpus: "${WEBSITE_CPUS:-0.25}" memory: ${WEBSITE_MEMORY:-64M} pids: 64 networks: - wizard_website wizard_server: build: ./wizard_server restart: unless-stopped environment: AI_SHARED_SECRET: ${AI_SHARED_SECRET:?set AI_SHARED_SECRET in .env} ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-} LOG_LEVEL: ${LOG_LEVEL:-INFO} MAX_ACTIVE_GAMES: ${MAX_ACTIVE_GAMES:-50} MAX_CONNECTIONS: ${MAX_CONNECTIONS:-500} MAX_CONNECTIONS_PER_IP: ${MAX_CONNECTIONS_PER_IP:-10} # Hard caps so Wizard can never starve other services on the host. deploy: resources: limits: cpus: "${SERVER_CPUS:-1.0}" memory: ${SERVER_MEMORY:-512M} pids: 256 networks: - wizard_api - wizard_internal wizard_ai: build: context: ./wizard_ai # The AI uses the game logic package of the server (requires docker compose >= 2.17). additional_contexts: game_logic: ./wizard_server/game_logic restart: unless-stopped environment: SERVER_URL: ws://wizard_server:8083/internal/ai AI_SHARED_SECRET: ${AI_SHARED_SECRET:?set AI_SHARED_SECRET in .env} AGENT: ${AI_AGENT:-model} MODEL_PATH: /srv/models/latest LOG_LEVEL: ${LOG_LEVEL:-INFO} AI_WORKERS: ${AI_WORKERS:-1} AI_MAX_QUEUE: ${AI_MAX_QUEUE:-64} AI_DEGRADE_QUEUE: ${AI_DEGRADE_QUEUE:-8} # Numeric libraries (numpy/torch) must not start one thread per host core. OMP_NUM_THREADS: ${AI_WORKERS:-1} MKL_NUM_THREADS: ${AI_WORKERS:-1} deploy: resources: limits: cpus: "${AI_CPUS:-1.0}" memory: ${AI_MEMORY:-1G} pids: 256 volumes: # Trained checkpoints; replace without rebuilding the image. - ./wizard_ai/models:/srv/models:ro depends_on: - wizard_server networks: - wizard_internal networks: # Shared with the existing Caddy reverse proxy. wizard_website: external: true wizard_api: external: true # Only the game server and the AI player; no route to the outside. wizard_internal: name: wizard_internal internal: true