services: db: image: mariadb:11 restart: unless-stopped environment: MARIADB_DATABASE: ${DB_NAME} MARIADB_USER: ${DB_USER} MARIADB_PASSWORD: ${DB_PASSWORD} MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} volumes: - dbdata:/var/lib/mysql - ./server/db/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 10 # No host port published by default — only the app needs the DB, over the # private compose network. Uncomment to inspect from the host: # ports: # - "3306:3306" app: build: . restart: unless-stopped env_file: .env environment: DB_HOST: db UPLOAD_DIR: /app/uploads LOG_DIR: /app/logs depends_on: db: condition: service_healthy volumes: - uploads:/app/uploads # Bind-mount logs to the host so app.log is directly readable at ./logs/ - ./logs:/app/logs # Binds 0.0.0.0 (no 127.0.0.1 prefix) so Pangolin can reach the container. ports: - "3000:3000" bot: build: context: . dockerfile: bot/Dockerfile restart: unless-stopped env_file: .env environment: DB_HOST: db SITE_INTERNAL_URL: http://app:3000/api/v1/internal/bot-config SITE_PUBLIC_URL: http://app:3000/api/v1/public LOG_DIR: /app/bot/logs depends_on: db: condition: service_healthy app: condition: service_started volumes: - ./bot/logs:/app/bot/logs # No published port — the bot's internal API (/internal/*) is reached only # by `app` over the private compose network, and must NEVER be exposed # through Pangolin/the public reverse proxy. volumes: dbdata: uploads: