services: bot: build: context: . # Or pin a prebuilt image instead of building: # image: ghcr.io/tiennm99/tiennm99bot:latest restart: unless-stopped environment: # --- Required --- TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN} # from @BotFather — SECRET MONGO_URL: ${MONGO_URL} # Atlas SRV string incl. credentials — SECRET MONGO_DATABASE: ${MONGO_DATABASE} # e.g. tiennm99bot # --- Optional (commented out; the code falls back to each default) --- # Coolify injects every dashboard variable into each service through its # generated .env (env_file), so setting one in the Coolify dashboard is # enough — no need to uncomment. Coolify also creates a dashboard entry # for every variable referenced here, and an existing empty entry beats # the compose default, so these stay commented to keep that from # happening. Outside Coolify, uncomment a line to pass the variable in. # MODULES: ${MODULES:-} # CSV of modules; empty = all modules # OWNER_ID: ${OWNER_ID:-} # Telegram user id for owner-only commands; empty = none # ADMIN_IDS: ${ADMIN_IDS:-} # CSV of admin Telegram user ids; empty = none # LOL_PANDASCORE_TOKEN: ${LOL_PANDASCORE_TOKEN:-} # PandaScore token — SECRET; empty = /lol* fetches fail # GOLD_VNAPP_API_KEY: ${GOLD_VNAPP_API_KEY:-} # VNAppMob key — SECRET; empty = fetched and cached in Mongo # BOT_USERNAME: ${BOT_USERNAME:-} # bot username without "@"; empty = getMe at startup # STICKER_PACK_NAME: ${STICKER_PACK_NAME:-} # /addsticker set; empty = stickers_by_ # LOG_LEVEL: ${LOG_LEVEL:-info} # debug|info|warn|error # --- Fixed by this stack (not Coolify settings) --- # The bundled renderer service below draws /wheelofnames, /gacha and # /genshin. Its base URL is fixed to the in-network address so a stale # platform-level value cannot point the bot elsewhere; the bot appends # each /api/ route itself. RENDERER_URL: http://renderer:3000 # --- Deliberately not declared --- # SOURCE_COMMIT: Coolify provides it at runtime via its generated .env; # declaring it here could override that with an empty value. # PORT: the health server defaults to 8080, which the healthcheck uses. # KV_PROVIDER: storage auto-selects mongodb because MONGO_URL is set. # Long polling = no TELEGRAM_WEBHOOK_SECRET, no /webhook, no public domain. # Cron is in-process only — no CRON_MODE, no /cron route, no secret. # No stock/coin/gold URL overrides — modules use their coded default # providers (stock: SSI/VCI/KBS; coin: Binance->Coinbase->CoinGecko; # gold: VNAppMob). # Long polling is outbound-only: nothing inbound to route, so no published # ports and no public domain. `expose` only documents the health server's # port; it publishes nothing on the host. expose: - "8080" # Coolify's UI health check does not apply to Docker Compose apps; it reads # this block instead. GET / returns text/plain "tiennm99bot ok". It proves the # process is serving, not that Mongo is reachable — see # docs/deploy-coolify-selfhosted.md. busybox wget ships in the Alpine image. healthcheck: test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/ || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 20s # Animation renderer (Node + Remotion + headless Chrome) from renderer/. # Internal only: the bot reaches it over the compose network, so it has no # published port, no public domain, and no auth token. Never publish a port # or attach a domain to it — its API is unauthenticated. renderer: build: context: ./renderer restart: unless-stopped environment: # Every renderer setting carries the RENDERER_ prefix so it reads as a # renderer setting and cannot clash with the bot's variables. # --- Optional tuning (commented out; the renderer falls back to each default) --- # Set any of these in the Coolify dashboard; it reaches this service # through Coolify's injected .env. Outside Coolify, uncomment a line. # RENDERER_MAX_CONCURRENT_RENDERS: ${RENDERER_MAX_CONCURRENT_RENDERS:-1} # RENDERER_RENDER_TIMEOUT_MS: ${RENDERER_RENDER_TIMEOUT_MS:-30000} # floor 7000 # RENDERER_MAX_OPTIONS: ${RENDERER_MAX_OPTIONS:-32} # wheel options # RENDERER_MAX_OPTION_CHARS: ${RENDERER_MAX_OPTION_CHARS:-40} # chars per option/label # --- Fixed by this stack (not Coolify settings) --- NODE_ENV: production RENDERER_HOST: 0.0.0.0 RENDERER_PORT: "3000" # keep in sync with the bot's RENDERER_URL and the healthcheck expose: - "3000" healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/api/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""] interval: 30s timeout: 5s retries: 3