services: redis: image: redis:8.10.2-alpine volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 2s timeout: 1s retries: 10 start_period: 2s trawl: image: ghcr.io/germondai/trawl:latest ports: # API + FlareSolverr-compatible /v1 - "${PORT:-8191}:8191" # MITM forward proxy (:8192) - only used when MITM_ENABLED=true. # The CA cert is served from :8191/proxy-ca.crt and must be installed # in each client's trust store (system keychain, Java cacerts, etc.). - "${MITM_PORT:-8192}:${MITM_PORT:-8192}" shm_size: 1gb environment: SESSION_CACHE_DRIVER: ${SESSION_CACHE_DRIVER:-redis} REDIS_URL: redis://redis:6379 REDIS_SESSION_TTL_SECONDS: ${REDIS_SESSION_TTL_SECONDS:-3600} MEMORY_SESSION_CACHE_MAX_ENTRIES: ${MEMORY_SESSION_CACHE_MAX_ENTRIES:-1000} REDIS_CONNECT_TIMEOUT_MS: ${REDIS_CONNECT_TIMEOUT_MS:-5000} REDIS_RETRY_DELAY_MS: ${REDIS_RETRY_DELAY_MS:-5000} BROWSER_SESSION_MAX_ENTRIES: ${BROWSER_SESSION_MAX_ENTRIES:-4} BROWSER_SESSION_TTL_SECONDS: ${BROWSER_SESSION_TTL_SECONDS:-3600} BROWSER_POOL_SIZE: ${BROWSER_POOL_SIZE:-1} LOG_LEVEL: ${LOG_LEVEL:-info} BROWSER_BLOCK_ADS: ${BROWSER_BLOCK_ADS:-true} BROWSER_IDLE_TIMEOUT_MS: ${BROWSER_IDLE_TIMEOUT_MS:-0} BROWSER_MAX_CONTENT_PROCESSES: ${BROWSER_MAX_CONTENT_PROCESSES:-2} BROWSER_HARDWARE_CONCURRENCY: ${BROWSER_HARDWARE_CONCURRENCY:-} BROWSER_HEADFUL_POOL_SIZE: ${BROWSER_HEADFUL_POOL_SIZE:-0} SCRAPE_MIN_TIER: ${SCRAPE_MIN_TIER:-1} TIER1_TRANSPORT: ${TIER1_TRANSPORT:-bun} SCRAPE_MAX_TIMEOUT_MS: ${SCRAPE_MAX_TIMEOUT_MS:-60000} USER_PREFS: ${USER_PREFS:-} BROWSER_ACQUIRE_TIMEOUT_MS: ${BROWSER_ACQUIRE_TIMEOUT_MS:-15000} BROWSER_RECYCLE_AFTER_CONTEXTS: ${BROWSER_RECYCLE_AFTER_CONTEXTS:-8} BROWSER_STALL_TIMEOUT_MS: ${BROWSER_STALL_TIMEOUT_MS:-180000} BROWSER_CLOSE_TIMEOUT_MS: ${BROWSER_CLOSE_TIMEOUT_MS:-10000} BROWSER_SHUTDOWN_DRAIN_MS: ${BROWSER_SHUTDOWN_DRAIN_MS:-0} BROWSER_LAUNCH_TIMEOUT_MS: ${BROWSER_LAUNCH_TIMEOUT_MS:-90000} SCREENSHOT_SETTLE_MS: ${SCREENSHOT_SETTLE_MS:-3000} SCREENSHOT_TIMEOUT_MS: ${SCREENSHOT_TIMEOUT_MS:-10000} SCREENSHOT_JPEG_QUALITY: ${SCREENSHOT_JPEG_QUALITY:-60} SCREENSHOT_MAX_BYTES: ${SCREENSHOT_MAX_BYTES:-4000000} BLOCKED_EVIDENCE_MAX_HTML_CHARS: ${BLOCKED_EVIDENCE_MAX_HTML_CHARS:-512000} DIAGNOSTICS_MAX_CONSOLE_ENTRIES: ${DIAGNOSTICS_MAX_CONSOLE_ENTRIES:-500} DIAGNOSTICS_MAX_NETWORK_ENTRIES: ${DIAGNOSTICS_MAX_NETWORK_ENTRIES:-1000} DIAGNOSTICS_MAX_STRING_CHARS: ${DIAGNOSTICS_MAX_STRING_CHARS:-2000} DIAGNOSTICS_MAX_TOTAL_CHARS: ${DIAGNOSTICS_MAX_TOTAL_CHARS:-1000000} DIAGNOSTICS_SIZE_TIMEOUT_MS: ${DIAGNOSTICS_SIZE_TIMEOUT_MS:-2000} REDIRECT_MAX_ENTRIES: ${REDIRECT_MAX_ENTRIES:-50} REDIRECT_MAX_URL_CHARS: ${REDIRECT_MAX_URL_CHARS:-2000} REDIRECT_MAX_TOTAL_CHARS: ${REDIRECT_MAX_TOTAL_CHARS:-1000000} CAPTURE_MAX_PATTERNS: ${CAPTURE_MAX_PATTERNS:-10} CAPTURE_MAX_RESPONSES: ${CAPTURE_MAX_RESPONSES:-5} CAPTURE_MAX_BODY_BYTES: ${CAPTURE_MAX_BODY_BYTES:-5242880} CAPTURE_MAX_TOTAL_BYTES: ${CAPTURE_MAX_TOTAL_BYTES:-10485760} CAPTURE_MAX_READ_BYTES: ${CAPTURE_MAX_READ_BYTES:-10485760} CAPTURE_MAX_METADATA_CHARS: ${CAPTURE_MAX_METADATA_CHARS:-2000} CAPTURE_BODY_TIMEOUT_MS: ${CAPTURE_BODY_TIMEOUT_MS:-5000} CAPTURE_SETTLE_MS: ${CAPTURE_SETTLE_MS:-15000} CAPTURE_MAX_SETTLE_MS: ${CAPTURE_MAX_SETTLE_MS:-60000} CAPTURE_IDLE_FLOOR_MS: ${CAPTURE_IDLE_FLOOR_MS:-5000} MHTML_MAX_PARTS: ${MHTML_MAX_PARTS:-200} MHTML_MAX_PART_BYTES: ${MHTML_MAX_PART_BYTES:-2097152} MHTML_MAX_TOTAL_CHARS: ${MHTML_MAX_TOTAL_CHARS:-8388608} MHTML_MAX_INFLIGHT_READS: ${MHTML_MAX_INFLIGHT_READS:-32} MHTML_MAX_OMISSION_RECORDS: ${MHTML_MAX_OMISSION_RECORDS:-100} CAPTCHA_SOLVER: ${CAPTCHA_SOLVER:-none} TWOCAPTCHA_API_KEY: ${TWOCAPTCHA_API_KEY:-} CAPTCHA_SOLVER_MAX_TASKS: ${CAPTCHA_SOLVER_MAX_TASKS:-1} CAPTCHA_SOLVER_LOCAL_TIMEOUT_MS: ${CAPTCHA_SOLVER_LOCAL_TIMEOUT_MS:-10000} CAPTCHA_SOLVER_TIMEOUT_MS: ${CAPTCHA_SOLVER_TIMEOUT_MS:-120000} CAPTCHA_SOLVER_PROFILES: ${CAPTCHA_SOLVER_PROFILES:-} STT_URL: ${STT_URL:-} STT_API_KEY: ${STT_API_KEY:-} FFMPEG_PATH: ${FFMPEG_PATH:-} MCP_ENABLED: ${MCP_ENABLED:-false} MCP_ALLOWED_ORIGINS: ${MCP_ALLOWED_ORIGINS:-} METRICS_DASHBOARD_ENABLED: ${METRICS_DASHBOARD_ENABLED:-false} METRICS_DASHBOARD_TOKEN: ${METRICS_DASHBOARD_TOKEN:-} METRICS_DB_PATH: ${METRICS_DB_PATH:-/data/metrics/trawl.sqlite} SCRAPE_PROXY_SELECTION: ${SCRAPE_PROXY_SELECTION:-failover} PROXY_URL: ${PROXY_URL:-} PROXY_LIST_FILE: ${PROXY_LIST_FILE:-} RESIDENTIAL_PROXY_URL: ${RESIDENTIAL_PROXY_URL:-} RESIDENTIAL_PROXY_LIST_FILE: ${RESIDENTIAL_PROXY_LIST_FILE:-} # MITM proxy (off by default - opt in via .env) MITM_ENABLED: ${MITM_ENABLED:-false} MITM_HOST: ${MITM_HOST:-0.0.0.0} MITM_PORT: ${MITM_PORT:-8192} MITM_CA_DIR: ${MITM_CA_DIR:-/data/proxy-ca} MITM_MAX_TIER: ${MITM_MAX_TIER:-4} MITM_ALWAYS_SCRAPE: ${MITM_ALWAYS_SCRAPE:-false} MITM_ESCALATE_429: ${MITM_ESCALATE_429:-false} MITM_DEBUG: ${MITM_DEBUG:-false} volumes: # Persist the proxy's CA so clients don't need to re-install on every restart. - trawl_proxy_ca:${MITM_CA_DIR:-/data/proxy-ca} - trawl_metrics:/data/metrics depends_on: redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:8191/health"] interval: 30s timeout: 10s retries: 3 start_period: 90s volumes: redis_data: trawl_proxy_ca: trawl_metrics: