# WebVideo2NAS - Docker environment example # # Copy to .env and adjust values. # # ===================== # Required # ===================== # API authentication (must NOT be change-this-key) API_KEY=change-this-to-a-very-long-secure-key-minimum-32-chars # PostgreSQL password (used by db container and API/worker connection string) DB_PASSWORD=ChangeThisPassword123! # ===================== # Image version (optional) # ===================== # Leave unset to track :latest. Pin to a specific release for reproducibility: # IMAGE_TAG=1.9.2 #IMAGE_TAG=latest # ===================== # Common # ===================== LOG_LEVEL=INFO # CORS (API) # Example for Chrome extension: # ALLOWED_ORIGINS=chrome-extension:// ALLOWED_ORIGINS=chrome-extension://* # Optional: allow cookies/credentials (requires explicit origins; wildcard will be rejected) CORS_ALLOW_CREDENTIALS=false # ===================== # Performance # ===================== # Per-worker segment/part download concurrency. Default 10 matches the # compose fallback and the in-code fallback. Higher = faster but more likely # to trip per-IP CDN throttling (see the pacing section below); tune per CDN # via HOST_CONCURRENCY_OVERRIDES rather than cranking this globally. MAX_DOWNLOAD_WORKERS=10 MAX_RETRY_ATTEMPTS=3 FFMPEG_THREADS=2 # ===================== # Reliability # ===================== # How often (seconds) the worker re-sweeps abandoned browser-job staging dirs # (heartbeat-protected, so a live job is never touched). The zombie-job reaper # runs at startup only, not on this timer. Both still run once at startup; this # sweep bounds how long stale browser staging leaks disk between restarts. # 0 = startup-only. REAPER_INTERVAL_SECONDS=900 # Free-space safety factor for the browser-finalize preflight. The two-track # DASH flow transiently needs staged segments + per-track concat blobs + the # mux output (~2-3x the staged size), so finalize fails fast if free space on # the staging or output volume is below staged_bytes x this factor. 0 = off. MIN_FREE_DISK_MULTIPLIER=1.3 # ===================== # Pacing architecture (v2.3.11+) # ===================== # We have TWO LAYERS of per-host pacing, and they're meant to be used # together for production multi-worker deployments: # # 1. host_throttle (cross-process, Redis-coordinated) # Caps in-flight connections per-host across ALL worker containers. # Configured via HOST_CONCURRENCY_CAP / HOST_CONCURRENCY_OVERRIDES # below. Necessary for multi-worker deployments to respect a CDN's # per-IP throttle threshold (which sees ALL workers as one IP). # # 2. _adaptive_delay (per-process, in-memory) # Backs off automatically on transport failures, decays on success. # Always on — no env knob. Operates within each worker process, so # with N worker containers, you get N independent learners. Useful # as a fast-path refinement but does NOT replace cross-process cap. # # Recommended for ANY multi-worker deployment: # set HOST_CONCURRENCY_OVERRIDES for any CDN known to throttle. # Cross-process per-host concurrency cap (layer 1, v2.3.11+). # When multiple worker containers run in parallel, they each open # MAX_DOWNLOAD_WORKERS connections — a single CDN host can see N_workers × # MAX_DOWNLOAD_WORKERS concurrent connections, which trips per-IP throttling # (curl 28 partial-body timeouts, curl 35 RST clusters, no segments lost # but slow + flaky). This cap is enforced via Redis across ALL workers. # Unset or 0 = no global cap (but per-host overrides below still apply). #HOST_CONCURRENCY_CAP=16 # Slot TTL in seconds. A crashed worker's slots auto-reclaim after this. #HOST_CONCURRENCY_TTL=300 # Per-host override of the cap above (v2.3.13+). Only the listed hosts get # the matching cap; unlisted hosts fall back to HOST_CONCURRENCY_CAP (or # pass through unthrottled if that's also unset). This is the recommended # way to throttle ONE specific CDN that's known to rate-limit aggressively # (e.g. phncdn) without slowing every other download. # # Format: ``host:cap;host:cap`` (semicolon between entries) # Match : exact OR suffix — ``phncdn.com`` matches ``ev-h.phncdn.com``, # ``hv-h.phncdn.com``, etc. Most-specific (longest) suffix wins. # Values: cap must be a positive integer. Bad entries are logged and skipped. # # Examples: # Only throttle phncdn at 6, leave everything else unthrottled: # HOST_CONCURRENCY_CAP= # HOST_CONCURRENCY_OVERRIDES=phncdn.com:6 # # Default 16 globally, phncdn restricted to 6, mycdn allowed up to 32: # HOST_CONCURRENCY_CAP=16 # HOST_CONCURRENCY_OVERRIDES=phncdn.com:6;mycdn.example.com:32 # # Why 6 specifically? It matches the typical browser per-origin # connection limit, and reliable browser-side downloaders for the same # content use maxThread=6 by default and drop to 1 in restricted mode. # Browsers also benefit from H2 multiplexing (multiple "streams" share # 1 TCP connection), which we can't easily replicate from curl_cffi — # so if 6 still hits throttle from this worker, drop further (4, 2, 1). # Sequential (1) is slow but guaranteed to mimic browser-paced playback. #HOST_CONCURRENCY_OVERRIDES=phncdn.com:6 # Per-host header overrides (v2.3.17+). # Path to a JSON file that maps hostname → header dict. Headers in this # file are applied LAST in the request pipeline — they beat both default # captured headers and per-attempt referer-strategy modifications. # # Format (host match: exact OR suffix, longest wins): # { # "phncdn.com": {"User-Agent": "...", "X-Custom": "..."}, # "cdn.example.org": {"Authorization": "Bearer ABC123"} # } # # Loaded lazily on first segment download; restart the worker to pick up # file changes. Missing/unreadable/malformed file = no overrides + warning # in the worker log (never fails the worker). # # Use cases: # - Force a known-good User-Agent for one specific picky CDN # - Inject an Authorization token that captures the user's session # - Add a CDN-specific anti-bot header that the extension doesn't capture #HOST_HEADERS_FILE=/etc/wv2nas/host_headers.json # HTTP/1.1 forcing (v2.3.14+). Default off. # Real Chrome speaks HTTP/2 to modern CDNs via ALPN, and curl_cffi's # chrome impersonation includes an H2 fingerprint. Forcing H1.1 (the old # v1.x default) breaks that impersonation: a request with chrome TLS # fingerprint over HTTP/1.1 doesn't match any real browser, which is # itself a bot signal that anti-bot systems like phncdn pick up. # # Set this to true ONLY if you hit a CDN that returns invalid HTTP/2 # responses (e.g. unfiltered 'keep-alive' header, illegal in H2). #FORCE_HTTP1_1=false # DB cleanup (db_cleanup service) # How often to prune finished jobs (seconds). Default: 3600 (1 hour) # Examples: # CLEANUP_INTERVAL_SECONDS=600 # every 10 minutes # CLEANUP_INTERVAL_SECONDS=86400 # every day #CLEANUP_INTERVAL_SECONDS=3600 # ===================== # Security (recommended if exposed to public internet) # ===================== # Per-client rate limit for write endpoints like /api/download (0 disables). # Bumped from 10 to 60 in v2.1.17 — the old default rejected legitimate # bulk-send bursts (10+ videos in one minute → 429 from the 11th onwards). # Set higher (e.g. 300) for a private NAS with no public exposure; lower # (10–30) if you've port-forwarded the API to the internet. RATE_LIMIT_PER_MINUTE=60 # Restrict who can call the API (comma-separated CIDRs) # Examples: # ALLOWED_CLIENT_CIDRS=203.0.113.8/32 # ALLOWED_CLIENT_CIDRS=203.0.113.8/32,198.51.100.0/24 ALLOWED_CLIENT_CIDRS= # SSRF guard: blocks private/loopback/link-local/reserved destinations. When # enabled it now validates not just the top-level job URL but every URL derived # from an untrusted manifest (HLS variant/segment/AES-key, DASH init/media) and # re-validates the host on EVERY redirect hop, so a redirect can't smuggle a # request to an internal host. Off = pass-through (no validation, no overhead). # Set true for public deployments. Keep false only if you intentionally # download from LAN hosts. (Recommended default: true — see docs/THREAT_MODEL.md.) SSRF_GUARD=false