Headless environment configuration1.x
Authoritative, opt-in configuration of every user-owned Settings (ConfigItems) value via NZBDAV_CONFIG__... environment variables. Designed for infrastructure-as-code / Docker Compose deployments where the Settings UI should not own those values.
What is in scope
Section titled “What is in scope”- Every public key in Settings that persists as a
ConfigItemsrow (scalars and JSON blobs such asusenet.providers,arr.instances,indexers.instances,profiles.instances). - Read-only Settings UI for ENV-managed keys, with the exact variable name shown.
- Startup validation that fails fast on unknown / invalid prefixed variables.
What is out of scope (v0.9)
Section titled “What is out of scope (v0.9)”These remain separate persistence / operational domains — they are not driven by NZBDAV_CONFIG__...:
- Frontend admin account bootstrap (first-run username/password)
- Warden source lists and GitHub backup state (
warden.db) - Database restore / one-off maintenance task actions
- Process variables such as
CONFIG_PATH,PORTandLOG_LEVEL(Environment variables)
Naming algorithm
Section titled “Naming algorithm”Take the Settings config key (documented on each configuration page), then:
- Replace every
-with_ - Replace every
.with__ - Uppercase
- Prefix with
NZBDAV_CONFIG__
Examples:
| Config key | Environment variable |
|---|---|
api.categories |
NZBDAV_CONFIG__API__CATEGORIES |
usenet.segment-cache.enabled |
NZBDAV_CONFIG__USENET__SEGMENT_CACHE__ENABLED |
api.addurl-trusted-hosts |
NZBDAV_CONFIG__API__ADDURL_TRUSTED_HOSTS |
usenet.providers |
NZBDAV_CONFIG__USENET__PROVIDERS |
Structured settings use the same JSON shapes as the Settings API / UI (PascalCase property names — see JSON and Compose notes below). Per-feature pages list the config keys; apply the mapping rule above for the ENV name.
Internal runtime keys are excluded from the public map. Supplying their mapped names
(e.g. NZBDAV_CONFIG__SEARCH__EXCLUDE_SYNC_CACHE) fails startup as an unknown
configuration variable:
search.exclude(prefix constant — not a persisted setting)search.exclude-sync-cache(runtime cache state)prowlarr.sync-status(runtime sync status)
Persisted exclude settings such as search.exclude-patterns → NZBDAV_CONFIG__SEARCH__EXCLUDE_PATTERNS are supported.
Precedence
Section titled “Precedence”NZBDAV_CONFIG__... > SQLite / Settings UI > legacy fallbacks (WEBDAV_*, MOUNT_DIR, …) > built-in defaults- ENV values are not written into SQLite.
- Removing an ENV variable and restarting restores the prior SQLite value (if any) and re-enables editing in the UI.
- With no
NZBDAV_CONFIG__...variables set, behavior is identical to previous releases.
Settings UI
Section titled “Settings UI”When a key is ENV-managed:
- The control is shown read-only (disabled fieldset) with a lock badge naming the variable
- Save payloads omit managed keys (backend still rejects writes as the authority)
- A page banner notes that managed settings require changing container configuration and restarting
Validation and restart
Section titled “Validation and restart”- Unknown
NZBDAV_CONFIG__...names, or invalid values for keys with schema validation (booleans, integers, enums such asrepair.healthcheck-depth, and JSON blobs), abort startup with a single-line error that names the variable and never prints its value - Unknown or miscased properties inside a structured JSON value (
usenet.providers,arr.instances,indexers.instances,profiles.instances) also abort startup, reporting the JSON path of the offending property (property names only, never values) so a typo is not silently dropped - Structured values are therefore not forward compatible: a Compose file cannot carry a property intended for a newer image, and rolling back to an image that predates a property fails startup rather than ignoring it
- Free-form string keys (categories, paths, import strategy labels, some mode strings) are not fully schema-checked at ENV load — use the allowed values from the matching Settings reference page
- Empty ENV values are ignored (same as omitting the variable); they do not clear a SQLite value
- Changing ENV configuration requires a container restart or recreate (including after
.envchanges) - Logs list managed key names only (not secret values)
Secrets
Section titled “Secrets”Headless stacks still need FRONTEND_BACKEND_API_KEY for frontend↔backend authentication. Set NZBDAV_CONFIG__API__KEY to the same value so Arr, SABnzbd clients and the Settings UI use one download-client API key.
NZBDAV_CONFIG__WEBDAV__PASS accepts plaintext; infinidysk hashes it at overlay load (same as the Settings UI). WEBDAV_PASS in the example is a Compose-only placeholder, not an infinidysk variable. Other secrets in JSON (Usenet Pass, indexer / Arr API keys, profile tokens) remain in the ENV JSON as supplied.
Fully hydrated Compose example
Section titled “Fully hydrated Compose example”Copy-ready skeleton. Secrets use ${VAR:?message} so Compose fails closed if they are missing — put real values in an uncommitted .env (or your orchestrator’s secret store), not in git.
services: nzbdav: image: ghcr.io/infinidysk/infinidysk:latest container_name: nzbdav restart: unless-stopped healthcheck: test: - CMD-SHELL - curl -fsSL http://localhost:3000/healthz > /dev/null || exit 1 interval: 30s retries: 3 start_period: 60s timeout: 5s ports: - "3000:3000" environment: # --- Process variables (see the Environment variables page) --- PUID: "1000" PGID: "1000" TZ: America/New_York TRUST_PROXY: "1" SECURE_COOKIES: "true" FRONTEND_BACKEND_API_KEY: ${FRONTEND_BACKEND_API_KEY:?missing}
# --- Authoritative ConfigItems overlay (NZBDAV_CONFIG__...) --- # Same value as FRONTEND_BACKEND_API_KEY: one key for proxy and Arr/SAB. NZBDAV_CONFIG__API__KEY: ${FRONTEND_BACKEND_API_KEY:?missing} NZBDAV_CONFIG__API__CATEGORIES: "tv,movies" NZBDAV_CONFIG__API__MANUAL_CATEGORY: "uncategorized" NZBDAV_CONFIG__API__IMPORT_STRATEGY: "symlinks" NZBDAV_CONFIG__API__ENSURE_IMPORTABLE_VIDEO: "true" NZBDAV_CONFIG__API__IGNORE_HISTORY_LIMIT: "true" # NZBDAV_CONFIG__API__ADDURL_TRUSTED_HOSTS: "prowlarr,indexer" # NZBDAV_CONFIG__GENERAL__BASE_URL: "https://nzbdav.example.com"
NZBDAV_CONFIG__WEBDAV__USER: "webdav" NZBDAV_CONFIG__WEBDAV__PASS: ${WEBDAV_PASS:?set WEBDAV_PASS} NZBDAV_CONFIG__WEBDAV__ENFORCE_READONLY: "true"
NZBDAV_CONFIG__RCLONE__MOUNT_DIR: "/mnt/remote/nzbdav" NZBDAV_CONFIG__MEDIA__LIBRARY_DIR: "/mnt/media"
NZBDAV_CONFIG__USENET__MAX_DOWNLOAD_CONNECTIONS: "0" NZBDAV_CONFIG__USENET__STREAMING_PRIORITY: "80" NZBDAV_CONFIG__USENET__PIPELINING__ENABLED: "false" NZBDAV_CONFIG__USENET__CASCADE__ENABLED: "true" NZBDAV_CONFIG__USENET__PROVIDERS: >- {"Providers":[{"Type":1,"Host":"news.example.com","Port":563, "UseSsl":true, "User":"${USENET_USER:?set USENET_USER}", "Pass":"${USENET_PASS:?set USENET_PASS}", "MaxConnections":50,"MaxTransferConnections":20, "Nickname":"primary"}]}
NZBDAV_CONFIG__ARR__INSTANCES: >- {"RadarrInstances":[{"Host":"http://radarr:7878", "ApiKey":"${RADARR_API_KEY:?set RADARR_API_KEY}"}], "SonarrInstances":[{"Host":"http://sonarr:8989", "ApiKey":"${SONARR_API_KEY:?set SONARR_API_KEY}"}], "QueueRules":[]} # Optional. Default on. Set false to keep Arr queue rules # without Overview health polling. # NZBDAV_CONFIG__ARR__HEALTH_ENABLED: "true" # Optional Name/Enabled on each instance (since 1.2.0) are not # forward compatible with older images; see Validation and restart.
NZBDAV_CONFIG__INDEXERS__INSTANCES: >- {"Indexers":[{"Name":"Example", "Url":"https://indexer.example/api", "ApiKey":"${INDEXER_API_KEY:?set INDEXER_API_KEY}", "Enabled":true}]}
NZBDAV_CONFIG__PROFILES__INSTANCES: >- {"Profiles":[{"Name":"Default", "Token":"${PROFILE_TOKEN:?set PROFILE_TOKEN}"}]}
NZBDAV_CONFIG__REPAIR__ENABLE: "true" NZBDAV_CONFIG__REPAIR__HEALTHCHECK_CONCURRENCY: "50" NZBDAV_CONFIG__REPAIR__HEALTHCHECK_WORKERS: "1" NZBDAV_CONFIG__REPAIR__HEALTHCHECK_DEPTH: "standard" # NZBDAV_CONFIG__REPAIR__DEGRADED_TOLERANCE_ENABLED: "true" # NZBDAV_CONFIG__REPAIR__DEGRADED_MAX_CONSECUTIVE_MISSING: "2" # NZBDAV_CONFIG__REPAIR__DEGRADED_MAX_TOTAL_MISSING: "5" # NZBDAV_CONFIG__REPAIR__DEGRADED_MAX_MISSING_BYTE_PERCENT: "1.0"
NZBDAV_CONFIG__MAINTENANCE__REMOVE_ORPHANED_SCHEDULE_ENABLED: "true" # Minutes from midnight in TZ (180 = 03:00) NZBDAV_CONFIG__MAINTENANCE__REMOVE_ORPHANED_SCHEDULE_TIME: "180"
NZBDAV_CONFIG__BACKUP__SCHEDULE_ENABLED: "true" # Minutes from midnight in TZ (120 = 02:00) NZBDAV_CONFIG__BACKUP__SCHEDULE_TIME: "120" NZBDAV_CONFIG__BACKUP__RETENTION_COUNT: "7"
NZBDAV_CONFIG__PREFLIGHT__MODE: "off" NZBDAV_CONFIG__PLAY__WATCHDOG_ENABLED: "true" volumes: - ./config:/config - /mnt:/mntAfter start
Section titled “After start”- Open Settings. ENV-managed controls show a lock badge such as
Managed by NZBDAV_CONFIG__WEBDAV__USER. - Attempting to save a managed key is rejected by the API.
- To hand a key back to SQLite/UI: remove that
NZBDAV_CONFIG__...line, restart or recreate the container, and the prior SQLite value (if any) becomes editable again.
Minimal .env (do not commit)
Section titled “Minimal .env (do not commit)”FRONTEND_BACKEND_API_KEY=generate-a-long-random-stringWEBDAV_PASS=USENET_USER=USENET_PASS=RADARR_API_KEY=SONARR_API_KEY=INDEXER_API_KEY=PROFILE_TOKEN=Legacy compatibility
Section titled “Legacy compatibility”Legacy fallbacks such as WEBDAV_USER and MOUNT_DIR still apply only when neither the overlay nor SQLite supplies a value; see Settings fallbacks. Prefer NZBDAV_CONFIG__... for new headless deployments so Settings shows the authoritative source and rejects conflicting UI edits.
Related pages
Section titled “Related pages”- Environment variables — process + legacy fallbacks
- Docker — Compose basics
- First run — admin account (still UI / out of ENV scope)
- Feature Settings references: Usenet · Indexers · Profiles · Queue · SABnzbd · Streaming · WebDAV · Preflight · Watchdog · Watchtower · Warden · Arr Apps · Rclone · Health & repairs · Maintenance · Backup