Skip to content
ThemeNord

Same themes as the MinusPod app

Default

Catppuccin

Dracula

Nord

Gruvbox

Solarized

Rose Pine

Kanagawa

Ayu

Other

Environment Variables

Grouped by how often you’ll touch them. Standard is what a typical deployment sets; Security is the hardening surface; Advanced are tuning knobs for edge cases; Optional are opt-in features.

WHISPER_LANGUAGE, WHISPER_COMPUTE_TYPE, and the VAD_GAP_* variables seed fresh installs only; runtime values live in Settings > Transcription (VAD at GET/PUT /api/v1/settings).

Variable Default Description
ANTHROPIC_API_KEY (none) Claude API key (required when LLM_PROVIDER=anthropic, not needed for Ollama)
LLM_PROVIDER anthropic Default LLM provider: anthropic, openrouter, openai-compatible, ollama, typesafe, or systemone-compatible
OPENROUTER_API_KEY (none) OpenRouter API key (required when LLM_PROVIDER=openrouter)
OPENAI_BASE_URL http://localhost:8000/v1 Base URL for OpenAI-compatible API (only used with non-anthropic providers)
TYPESAFE_API_KEY (none) Provider A TypeSafe API key. Provider B uses its saved slot key.
SYSTEMONE_API_KEY (none) Provider A System One-compatible API key. Provider B uses its saved slot key.
SYSTEMONE_BASE_URL (none) Provider A compatible base URL. Provider B uses its saved slot base URL. Requests use /systemone; model discovery uses /models. Does not inherit OPENAI_BASE_URL.
OPENROUTER_BASE_URL https://openrouter.ai/api/v1 OpenRouter API base URL. Override for private proxies or regional endpoints.
ANTHROPIC_BASE_URL (anthropic default) Anthropic API base URL. Override for private proxies.
OPENAI_API_KEY not-needed API key for OpenAI-compatible endpoint (not required for Ollama or local wrappers)
OPENAI_MODEL (none) Model id that seeds claude_model, verification_model, and chapters_model whenever one of those settings is unset. No shipped default exists: required for Ollama (e.g. qwen3:14b) and needed for OpenAI-compatible/OpenRouter unless you pick a model in Settings > AI models instead. Once a setting has a stored value the env var no longer touches it. Processing fails with a message pointing at Settings > AI models until every model setting is configured. There is no LLM_MODEL variable.
BASE_URL http://localhost:8000 Public URL for generated feed links
UI_BASE_URL (falls back to BASE_URL) Public URL for UI links in webhooks (set if UI is on a different domain than feeds)
WHISPER_MODEL small (GPU), tiny (CPU image) faster-whisper backend only. Model size. tiny, base, small, medium, large-v3, turbo, plus .en variants
WHISPER_DEVICE cuda (GPU image), cpu (CPU image/source) cuda or cpu; applies to both local backends. Set to cpu with the CPU image or remote backend.
WHISPER_BACKEND local local (faster-whisper, in the container), parakeet (NVIDIA Parakeet TDT 0.6B v3, in the container), or openai-api (remote HTTP server). WHISPER_MODEL and WHISPER_COMPUTE_TYPE apply to local only. See Transcription.
HF_HOME /app/data/.cache in both images Persistent model-cache root. Parakeet uses parakeet/nemo-parakeet-tdt-0.6b-v3 beneath it. See model storage and sizing.
WHISPER_API_BASE_URL (none) Base URL for OpenAI-compatible whisper API
WHISPER_API_KEY (none) API key for whisper API
WHISPER_API_MODEL whisper-1 Model name sent to whisper API
WHISPER_LANGUAGE en ISO 639-1 language code, or auto.
WHISPER_COMPUTE_TYPE auto faster-whisper backend only. auto, float16, int8_float16, int8, or float32. auto picks float16 on CUDA and int8 on CPU. See GPU Compute Type for per-GPU recommendations.
VAD_GAP_DETECTION_ENABLED true true or false. Toggles the VAD gap detector, which cuts audio regions Whisper’s VAD dropped (sped-up disclaimers, distorted ad tails) that the transcript-based detectors never see. Advanced tuning, not surfaced in the UI.
VAD_GAP_START_MIN_SECONDS 3.0 Minimum pre-transcript gap (seconds) at episode start that the VAD detector will cut. Anything shorter is left alone.
VAD_GAP_MID_MIN_SECONDS 8.0 Minimum mid-episode untranscribed gap. Standalone mid-gaps still require BOTH signoff-before AND resume-after context to emit; gaps adjacent to a detected ad extend that ad in place regardless of this threshold.
VAD_GAP_TAIL_MIN_SECONDS 3.0 Minimum post-transcript gap at episode end that the VAD detector will cut when no postroll marker already covers it.
OLLAMA_API_KEY (none) Ollama Cloud key. Leave unset for local.
PODCAST_INDEX_API_KEY (none) PodcastIndex.org API key for podcast search
PODCAST_INDEX_API_SECRET (none) PodcastIndex.org API secret
LOG_LEVEL INFO DEBUG, INFO, WARNING, or ERROR
LOG_FORMAT text text or json. JSON output works with log aggregators (Loki, CloudWatch).
DATA_DIR /app/data Data storage directory. Aliases DATA_PATH and MINUSPOD_DATA_DIR are also honored.
TZ UTC Container timezone (e.g. America/New_York). When set to a valid IANA zone, seeds the notification_timezone setting that computes timestamp_local in webhook and email notifications. Override the setting at Settings > Notifications, or GET/PUT /api/v1/settings/notifications/timezone, without touching TZ.
Variable Default Description
MINUSPOD_MASTER_PASSPHRASE (unset) Unlocks encrypted provider-key store. Strongly recommended for production; first boot migrates any plaintext rows to enc:v1:. Losing it makes stored keys unrecoverable (env fallback still works).
MINUSPOD_REQUIRE_AUTH true in Compose Refuse the API until an application password is set. Direct source runs default to false for development compatibility.
MINUSPOD_SETUP_TOKEN (unset) One-time header value for remote first-password setup when required auth is enabled. Send it as X-MinusPod-Setup-Token. Loopback setup does not need a token. Remove it after setup.
MINUSPOD_ALLOW_PUBLIC_PROCESSING false in Compose Allow an unauthenticated podcast request to start processing an unprocessed episode. Authenticated feed credentials can start processing regardless. Direct source runs default to true for compatibility.
SESSION_COOKIE_SECURE auto in Compose Follow BASE_URL. Set true or false only when proxy termination makes that result incorrect. An unset value uses the same automatic behavior.
SESSION_COOKIE_SAMESITE Strict Override to Lax only if a specific integration breaks.
MINUSPOD_ENABLE_HSTS false Set to true once the deployment is HTTPS-only. HSTS traps browsers so don’t flip this on a dual-protocol setup.
MINUSPOD_URL_PREFIX empty Serve the app under a reverse-proxy sub-path such as /minuspod, so the UI is at /minuspod/ui/ and feeds at /minuspod/<slug>. BASE_URL must include the same prefix. The first segment cannot be ui, api, episodes, health, opml or login. See Sub-path hosting.
MINUSPOD_TRUSTED_PROXY_COUNT 0 Reverse-proxy hops to trust when reading X-Forwarded-For; 1 behind Cloudflare, cloudflared, nginx, or Traefik. Leaving it at 0 behind a proxy breaks login lockout and per-IP rate limits. See Client IP for login lockout.
SSRF_IP_PINNING true Pin the resolved IP for outbound fetches so a DNS record that flips between check and connect cannot reach an internal target. Set false (or 0, no, off) only to isolate a fetch failure you suspect the pin causes. Provider SDK traffic is unaffected. See Outbound request safety.
MINUSPOD_ALLOW_PRIVATE_FEED_HOSTS false Allow RSS feed sources on private / loopback / LAN hosts. Off by default: feed URLs are untrusted and validated with the strict SSRF tier (DNS-resolved, private/metadata blocked) so a stored URL can’t be rebound to an internal address on refresh. Set true only if you serve a feed from a private address (e.g. a LAN Audiobookshelf).

Env-backed rows seed the default of the stored setting named in the row. The stored value is editable at runtime in Settings and wins over the env var.

Variable Default Description
PROCESSING_SOFT_TIMEOUT 3600 Seconds before a stuck job is auto-cleared. Seeds fresh installs; runtime value lives in Settings > Transcription.
PROCESSING_HARD_TIMEOUT 7200 Seconds before the processing lock is force-released. Must exceed the soft timeout.
AD_DETECTION_MAX_FAILED_WINDOW_RATIO 0.25 Fraction of a detection or verification pass’s windows that may fail (LLM error, timeout) before the whole pass is treated as failed, instead of accepting a result with those windows unexamined. A failed first pass fails the episode, which the retry ladder then picks up. A failed verification pass leaves the first-pass result and its markers alone and records the second scan as incomplete rather than clean; the episode is not reprocessed on its own. 1.0 accepts the pass unless every window failed. This variable seeds the ad_detection_max_failed_window_ratio setting, which is read on every pass, so editing the stored value retunes the threshold without a restart.
REVIEWER_CALIBRATION_ON_CHANGE true Run the reviewer calibration self-test in a background thread when the reviewer model setting changes. Each run costs 8 LLM calls. Set to false to switch models without paying for the check. Env-backed (reviewer_calibration_on_change).
LOW_AD_YIELD_ACTION nothing Action taken automatically when a pipeline-initiated run removes far less ad time than the feed’s recent average: nothing, redetect (rerun detection from the stored transcript), reprocess, or full. Fires at most once per episode (a failed rerun still counts) and never for a manual reprocess. Env-backed (low_ad_yield_action); each feed can override it.
EPISODE_LOG_RETENTION_DAYS 30 Days to keep per-run pipeline logs, clamped to [0, 365]. 0 turns run log storage off and lets the cleanup sweep delete what is already stored. Env-backed (episode_log_retention_days); each feed can opt in or out.
EPISODE_LOG_LEVEL debug Minimum level kept in a run log: debug or info. A run log can only keep what the server already writes, so there are no debug lines to keep unless LOG_LEVEL is DEBUG. Env-backed (episode_log_level).
DOWNLOAD_USER_AGENT (a current Chrome string) User-Agent sent when fetching audio, artwork, and chapters. Some hosts run bot mitigation that refuses browser identifiers below a rolling version floor, which surfaces as a 403 on download even though the file is there. Env-backed (download_user_agent, Settings > Outbound Requests), so a host that starts refusing ours is fixed without a new image.
FEED_USER_AGENT PodcastAdRemover/1.0 User-Agent sent when fetching RSS. Kept separate from the download UA because some feed hosts do the opposite and answer only a declared podcast client. Env-backed (feed_user_agent, Settings > Outbound Requests).
LOG_DOWNLOAD_QUERY false Include URL query strings in download logs. The requested path and the full redirect chain are logged either way. This adds the query, which on a podcast enclosure regularly carries a signed CDN token or a per-listener tracking id. Turn it on while debugging a refusal, not permanently, since those values then sit in your logs for as long as you keep them. Env-backed (log_download_query, Settings > Outbound Requests).
MAX_AUDIO_DOWNLOAD_MB 500 Per-episode download size cap in MB. Raise it for feeds with very long or high-bitrate episodes (a 260-minute episode at 256kbps is about 500MB). Guards against a broken or malicious enclosure filling the disk, so keep it finite. Env-backed.
MINUSPOD_MAX_ARTWORK_BYTES 26214400 (25 MB) Cap on podcast artwork download size. Clamped to [65536, 52428800]. Env-backed.
MINUSPOD_MAX_RSS_BYTES 209715200 (200 MB) Cap on RSS response body size. Floor is 1 MB. Env-backed.
RATE_LIMIT_STORAGE_URI memory-threadsafe:// Flask-limiter storage backend. Default is per-worker; set to redis://host:6379 + run a Redis sidecar for exact declared limits across workers.
APP_UID 1000 UID gunicorn runs as inside the container. Override to match host volume ownership.
APP_GID 1000 GID counterpart to APP_UID.
GUNICORN_BIND 0.0.0.0:8000 Listen address. Accepts a comma-separated list for multiple sockets. For dual-stack on rootless Podman, use [::]:8000: one IPv6 wildcard also accepts IPv4 when the kernel keeps bindv6only=0 (the default). Do not list both 0.0.0.0:8000 and [::]:8000 on such a kernel; the second bind fails with EADDRINUSE and gunicorn exits.
MINUSPOD_PORT 8000 Port for the default listen address (0.0.0.0:$MINUSPOD_PORT). Handy for network_mode: host or running several instances on one host without a port-mapping conflict. Ignored when GUNICORN_BIND is set, which takes precedence. The container EXPOSE stays at 8000 (build-time metadata only); the actual listen port follows this var.
GUNICORN_WORKERS 2 Worker count. Episode processing always runs in the background leader worker, so extra workers only serve the API, UI and feeds. Lower means single-threaded UI blocking during RSS refresh; higher multiplies per-worker rate-limit counters (when using memory://).
GUNICORN_TIMEOUT 600 Per-request hard timeout.
GUNICORN_GRACEFUL_TIMEOUT 330 Seconds between SIGTERM and SIGKILL on shutdown.
GUNICORN_ACCESS_LOG - (stdout) Where the access log goes. Set it empty to disable it, or to a file path to write there.
GUNICORN_ERROR_LOG - (stderr) Where the server error log goes. Set it empty to disable it, or to a file path to write there.
MINUSPOD_STOP_GRACE_PERIOD 360s Compose container shutdown grace. Keep it longer than GUNICORN_GRACEFUL_TIMEOUT.
MINUSPOD_BIND_ADDRESS 0.0.0.0 Host address used by the Compose port mapping. Set 127.0.0.1 when a local reverse proxy, VPN, or wrapper is the only intended entry point.
SECRET_KEY (auto-generated) Flask session signing key. If unset, a random value is generated and stored in the SQLite settings table. Set it explicitly only when required by a custom deployment. Rotating it invalidates every existing session.
SESSION_LIFETIME_HOURS 24 How long authenticated sessions stay valid, in hours.
OMP_NUM_THREADS (library default) Caps OpenMP threads for local faster-whisper CPU transcription. On hybrid Intel CPUs the default can push work onto the slow E-cores and thrash the cache; set it to your performance-core count (more threads is not faster). No effect with a remote Whisper API or on GPU. See Installation.

Every per-stage LLM control in Settings > Ad Detection has a matching env var: the setting key in uppercase. Set one to pin the control (the UI renders it read-only with a note); unset it to hand control back to the stored value. An unset variable leaves the built-in default in place. See the annotated list in .env.example and the configuration guide.

The stage prefixes are DETECTION_, VERIFICATION_, REVIEWER_, CHAPTER_BOUNDARY_, and CHAPTER_TITLE_. Each takes the same four suffixes:

Suffix Type Range / values
_TEMPERATURE float 0.0 - 2.0
_MAX_TOKENS int 128 - 32768
_REASONING_BUDGET int 1024 - 65536 (Anthropic extended thinking)
_REASONING_LEVEL enum none, low, medium, high (non-Anthropic providers)

So DETECTION_TEMPERATURE, VERIFICATION_MAX_TOKENS, REVIEWER_REASONING_LEVEL, and so on. Two legacy names still resolve: AD_DETECTION_MAX_TOKENS (alias of DETECTION_MAX_TOKENS) and REVIEW_MAX_TOKENS (alias of REVIEWER_MAX_TOKENS).

Variable Default Description
TUNNEL_TOKEN (none) Cloudflare tunnel token. See Before enabling the tunnel profile.
SENTRY_DSN (none) Opt-in Sentry. Requires sentry-sdk installed. Headers, cookies, CSRF tokens, and credential-like query params are scrubbed before send; no performance tracing.
MINUSPOD_RELEASE (none) Optional release tag forwarded to Sentry.
SENTRY_ENVIRONMENT production Environment tag forwarded to Sentry.
Variable Description
RETENTION_PERIOD Legacy minutes-based retention. Auto-converted to days on first startup. Use Settings UI or PUT /api/v1/settings/retention instead.