version: "3.9" services: # ─── Browserless ──────────────────────────────────────────────────────────── browserless: image: ghcr.io/browserless/chromium:latest container_name: libnovel-browserless restart: unless-stopped environment: # Set a token to lock down the endpoint; the scraper reads it via # BROWSERLESS_TOKEN below. TOKEN: "${BROWSERLESS_TOKEN:-}" # Allow up to 10 concurrent browser sessions. CONCURRENT: "${BROWSERLESS_CONCURRENT:-10}" # Queue up to 100 requests before returning 429. QUEUED: "${BROWSERLESS_QUEUED:-100}" # Per-session timeout in ms. TIMEOUT: "${BROWSERLESS_TIMEOUT:-60000}" # Optional webhook URL for Browserless error alerts. ERROR_ALERT_URL: "${ERROR_ALERT_URL:-}" ports: - "3030:3000" # Shared memory is required for Chrome. shm_size: "2gb" healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3000/json/version"] interval: 10s timeout: 5s retries: 5 # ─── Kokoro-FastAPI (TTS) ──────────────────────────────────────────────────── # CPU image; swap for ghcr.io/remsky/kokoro-fastapi-gpu:latest on NVIDIA hosts. # Models are baked in — no volume mount required for the default voice set. kokoro: image: ghcr.io/remsky/kokoro-fastapi-cpu:latest container_name: libnovel-kokoro restart: unless-stopped ports: - "8880:8880" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8880/health"] interval: 15s timeout: 5s retries: 5 # ─── Scraper ───────────────────────────────────────────────────────────────── scraper: build: context: ./scraper dockerfile: Dockerfile container_name: libnovel-scraper restart: unless-stopped depends_on: kokoro: condition: service_healthy environment: BROWSERLESS_URL: "http://browserless:3000" BROWSERLESS_TOKEN: "${BROWSERLESS_TOKEN:-}" # content | scrape | cdp | direct — swap to test different strategies. BROWSERLESS_STRATEGY: "${BROWSERLESS_STRATEGY:-direct}" # Strategy for URL retrieval (chapter list). Default: content (browserless) BROWSERLESS_URL_STRATEGY: "${BROWSERLESS_URL_STRATEGY:-content}" # 0 → defaults to NumCPU inside the container. SCRAPER_WORKERS: "${SCRAPER_WORKERS:-0}" SCRAPER_STATIC_ROOT: "/app/static/books" SCRAPER_HTTP_ADDR: ":8080" LOG_LEVEL: "debug" # Kokoro-FastAPI TTS endpoint. KOKORO_URL: "${KOKORO_URL:-http://localhost:8880}" KOKORO_VOICE: "${KOKORO_VOICE:-af_bella}" ports: - "8080:8080" volumes: - static_books:/app/static/books healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"] interval: 15s timeout: 5s retries: 3 volumes: static_books: