Skip to content

Headless environment configuration1.x

since 0.9.0

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.

  • Every public key in Settings that persists as a ConfigItems row (scalars and JSON blobs such as usenet.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.

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, PORT and LOG_LEVEL (Environment variables)

Take the Settings config key (documented on each configuration page), then:

  1. Replace every - with _
  2. Replace every . with __
  3. Uppercase
  4. 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.

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.

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
  • Unknown NZBDAV_CONFIG__... names, or invalid values for keys with schema validation (booleans, integers, enums such as repair.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 .env changes)
  • Logs list managed key names only (not secret values)

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.

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:/mnt
  1. Open Settings. ENV-managed controls show a lock badge such as Managed by NZBDAV_CONFIG__WEBDAV__USER.
  2. Attempting to save a managed key is rejected by the API.
  3. 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.
Terminal window
FRONTEND_BACKEND_API_KEY=generate-a-long-random-string
WEBDAV_PASS=
USENET_USER=
USENET_PASS=
RADARR_API_KEY=
SONARR_API_KEY=
INDEXER_API_KEY=
PROFILE_TOKEN=

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.