Skip to content

Backend environment

All backend configuration is via environment variables. The shipped .env.example is the commented source of truth; this page groups it by purpose. Booleans accept true/false, 1/0, yes/no and on/off; lists are comma-separated.

Variable Default Description
TMDB_API_KEY unset TMDB Read Access Token (v4) or legacy key. The only mandatory secret.
Variable Default Description
DATABASE_URL unset Full PostgreSQL URL; takes precedence over the parts below.
POSTGRES_HOST / POSTGRES_PORT / POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD localhost / 5432 / crimson ×3 Used to build the connection when DATABASE_URL is unset.
DB_POOL_MIN / DB_POOL_MAX 1 / 10 Connection-pool size per replica.
DB_CONNECT_TIMEOUT 30 Seconds to wait at startup for the database.
DB_PREPARE_THRESHOLD unset (off) Server-side prepared statements. Leave off behind a transaction-mode PgBouncer.
Variable Default Description
REQUIRE_LOGIN true Site-wide members-only wall. false opens the whole API.
SIGNUP_INVITE_CODE unset Reusable invite code(s), comma-separated, for both account types. Empty closes shared-code signup (403); single-use invites still work.
ADMIN_EMAILS unset Comma-separated emails of existing accounts to promote to admin on startup.
FRONTEND_BASE_URL https://crimsonhaven.to Origin used in emailed verify and reset links and the Discord bot’s signup hint.
SECURITY_EVENTS_RETENTION_DAYS 90 How long the security ledger (Admin › Security) keeps events before the housekeeping sweep prunes them.
DEMO_MODE false Open signup with no invite code, and wipe every non-admin account nightly.
DEMO_RESET_HOUR 4 Hour (UTC, 0 to 23) of the nightly demo wipe.

See Accounts & the login wall for the full model.

Needed only for email + password accounts and the optional airing notifications. With SMTP_HOST unset no mail is sent: registration still works, but verification and password-reset mails do not go out.

Variable Default Description
SMTP_HOST unset Mail server host; unset disables email.
SMTP_PORT 587 Mail server port.
SMTP_SECURITY starttls starttls | ssl | none.
SMTP_USER / SMTP_PASSWORD unset Credentials.
SMTP_FROM / SMTP_FROM_NAME unset / CrimsonHaven Envelope sender and display name.
Variable Default Description
PROXY_SECRET unset (random per process) HMAC secret shared with crimson-proxy. Signs /sign links, the subtitle, IPTV and music proxies, /local_art and cache tickets. Must be stable, identical on every replica and equal to each proxy’s NITRO_PROXY_SECRET. The random fallback only works for a single dev instance. Generate with openssl rand -hex 32.
SUBTITLES_PROXY_SECRET / CACHE_TICKET_SECRET unset Per-feature secrets, read only when PROXY_SECRET is unset. The other per-feature secrets are listed in their sections below.
CRIMSON_PROXY_BASE unset Comma-separated edge proxy origin(s). Enables the /sign grant. Unset: /sign returns 503 and clients use the extension or the backend.
Variable Default Description
JELLYFIN_URL / JELLYFIN_USERNAME / JELLYFIN_PASSWORD unset Enable the Jellyfin source (your own server, reachable from the backend).
JELLYFIN_EDGE_INJECT false Deliver Jellyfin through the proxy edge (token injected at the edge) instead of the backend proxy. Requires the proxy’s NITRO_JELLYFIN_*.
RUN_CACHE_WORKER true Run the background ffmpeg cache downloader on this replica. Keep it true on one dedicated worker and set false everywhere else.
CACHE_INTERNAL_BASE / CACHE_MAX_CONCURRENT / CACHE_DOWNLOAD_TIMEOUT / CACHE_POLL_INTERVAL / CACHE_MIN_FREE_BYTES see .env.example Cache downloader tuning.
PRIVATE_SOURCES_ENABLED true Kill switch for build-time overlay sources. false disables them without a rebuild.

See Operator-owned sources for setup.

Admin downloads go through an aria2 sidecar into a download-enabled Local source.

Variable Default Description
RUN_DOWNLOAD_WORKER true Run the aria2 poll loop on this replica. Keep it true on one dedicated service and set false elsewhere.
ARIA2_RPC_URL http://aria2:6800/jsonrpc aria2 JSON-RPC endpoint.
ARIA2_RPC_SECRET unset Must equal aria2’s RPC_SECRET. Generate with openssl rand -hex 24.
DOWNLOAD_MAX_ACTIVE / DOWNLOAD_POLL_INTERVAL / DOWNLOAD_MIN_FREE_BYTES 3 / 5 / 2147483648 Concurrent downloads, poll interval in seconds, and free space (bytes) to keep on the target.

The reading surface is on by default and needs no API key: discovery is AniList, and chapters and pages resolve in the viewer’s browser. The first three variables are plain preferences (they name no host); the last two are read only by an optional server-side provider.

Variable Default Description
MANGA_ENABLED true Master switch for the reading surface. false: the manga routes return 503 and the client hides the row, search and reader.
MANGA_LANGUAGES en Preferred chapter language(s), comma-separated; the first is the default. Passed to the browser so its resolution matches.
MANGA_CONTENT_RATING safe,suggestive,erotica Content ratings to include, comma-separated. Add pornographic to include it (off by default).
MANGADEX_APP_NAME CrimsonHaven/1.0 Provider only. Descriptive User-Agent for the optional server-side provider.
MANGA_PROXY_SECRET unset Provider only. Signs /manga_proxy image links, read only when PROXY_SECRET is unset.

See The reading surface (manga) for the full picture.

There is nothing to switch on: the browsable local library follows the Local source (enable one in the admin dashboard). The only related variable is optional:

Variable Default Description
LOCAL_PROXY_SECRET unset Signs the public /local_art poster links, read only when PROXY_SECRET is unset. Set PROXY_SECRET instead.

See The local media library for the full picture, and Operator-owned sources for the Local source itself.

The Live TV surface is on by default and needs no API key. The catalogue is the iptv-org public index, fetched twice daily into memory (no database table). Playback is direct first; the signed /iptv_proxy carries only what the browser’s own rules will not allow.

Variable Default Description
IPTV_ENABLED true Master switch for Live TV. false: the IPTV routes return 503 and the client hides the nav entry and routes.
IPTV_REFRESH_HOURS 12 Hours between catalogue refreshes (upstream publishes daily). Minimum 1.
IPTV_INCLUDE_NSFW false Include NSFW-flagged channels in the catalogue.
IPTV_PROXY_SECRET unset Signs /iptv_proxy stream links, read only when PROXY_SECRET is unset. Set PROXY_SECRET instead.

See The Live TV surface (IPTV) for the full picture.

Off until a share is mounted. Spotify is not configured here: each member connects their own Spotify app from the Music page, and access is granted per member on Admin › Users.

Variable Default Description
MUSIC_ROOT unset In-container path of the music share, e.g. /crimson/music. Unset hides the Music surface. Mount it on api and music-worker alike.
RUN_MUSIC_WORKER true Run the music download and playlist sync loop. true on the music-worker service only.
MUSIC_LINK_SECRET unset Signs /music_stream and /music_art links, read only when PROXY_SECRET is unset. Set PROXY_SECRET instead.
MUSIC_CDN_URL unset Base URL of the music-cdn Worker, e.g. https://cdn.example.com. With MUSIC_CDN_SECRET, songs are copied to R2 and streamed from there. Set on api and music-worker.
MUSIC_CDN_SECRET unset Shared with the Worker’s CDN_SECRET: signs CDN links and authorises uploads.

Downloads also need a music provider baked into the image (MUSIC_REPO at build time). See The music library for the full picture.

The weekly calendar and per-title follows are always on and need nothing here. These two gate only the job that emails a member when a followed episode airs. It also needs SMTP_* above and runs only on the RUN_DB_SYNC replica.

Variable Default Description
AIRING_NOTIFY_ENABLED false Send the “a new episode aired” mail. Off by default because a sent mail is the one thing a redeploy cannot take back.
AIRING_NOTIFY_DRY_RUN false Rehearsal: claims each notice for real and logs who would be mailed, without opening an SMTP connection.

Only accounts with a verified email are mailed, so mnemonic accounts are skipped (the API reports this via email_notifications rather than failing the follow).

See The airing calendar & follows for the full picture, including why to rehearse with the dry run first.

Optional and off by default. Only the provider keys are environment variables. The master switch, provider, model, budgets and per-account grants are operator state in PostgreSQL, managed from Admin › Lumi and Admin › Users without a redeploy. Keys stay out of the database so a database dump never carries billable credentials.

Variable Default Description
ANTHROPIC_API_KEY unset Claude. The recommended provider (best persona fidelity and tool accuracy).
GEMINI_API_KEY unset Google AI Studio. GOOGLE_API_KEY is accepted as an alias.

Setting a key does not turn the feature on, and turning it on does not give anyone access: both are separate, audited decisions in the dashboard.

See Lumi, the chatbot for the full picture.

Variable Default Description
RUN_DB_SYNC true Run the periodic Fribb mapping resync, the nightly metadata jobs and the airing refresh and notify jobs. Exactly one replica should have this true.
ALLOWED_ORIGINS https://crimsonhaven.to,https://www.crimsonhaven.to Comma-separated CORS origins. Set your own in production.
RATE_LIMIT_STORAGE_URI memory:// Rate-limit backend. In-memory limits are per replica; use redis://… to share them.
METADATA_REFRESH_BUCKETS / METADATA_REFRESH_HOUR 14 / 4 Nightly TMDB refresh: re-pull the oldest 1/buckets of the TMDB tables at this hour. Sync replica only.
RUN_METADATA_BACKFILL / METADATA_BACKFILL_PAGES false / 100 Pre-populate from TMDB discover at startup. Also available from the dashboard.

Optional integrations (each self-disables when unset)

Section titled “Optional integrations (each self-disables when unset)”
Variable(s) Feature
DISCORD_BOT_TOKEN / DISCORD_OWNER_ID / DISCORD_COMMAND_PREFIX The Discord invite bot (python -m discord_bot).
KOFI_VERIFICATION_TOKEN / KOFI_ACTIVE_WINDOW_DAYS / KOFI_LIST_CACHE_TTL Ko-fi supporters list (/supporters).
GITHUB_TOKEN / GITHUB_REPO / CHANGELOG_* Public changelog from GitHub Releases (/changelog).
OPENSUBTITLES_API_KEY / OPENSUBTITLES_APP_NAME / SUBTITLES_SEARCH_TTL OpenSubtitles player tracks (/subtitles).
HEALTH_CANARY_* The title the admin Source Health view probes with.
METRICS_TOKEN Lets a Prometheus scrape reach /metrics. Unset: admin sessions only.
PROMETHEUS_URL History charts on the dashboard’s Metrics tab.
LOG_FORMAT plain (default) or json.
DEBUG When truthy, includes exception detail in 500 responses. Leave unset in production.