Environment Variables by Container

Centralized self-hosting reference for Docker deployments. For configuration guidance, security hardening, and operational patterns, see CONFIGURATION_AND_SECURITY.md.

Scope:

Primary sources:

Status labels:

Container Map

Container / Service File Purpose
soundspan docker-compose.aio.yml All-in-one image (frontend + backend + postgres + redis internal)
backend docker-compose.yml API service (or combined role)
backend-worker docker-compose.yml Background workers/schedulers
frontend docker-compose.yml Web UI (Next.js)
postgres docker-compose.yml PostgreSQL
redis docker-compose.yml Redis
tidal-streamer docker-compose.yml TIDAL sidecar
ytmusic-streamer docker-compose.yml YouTube Music sidecar
audio-analyzer docker-compose.yml MusicCNN/Essentia analyzer
vibe-provider-dclap docker-compose.yml Default DCLAP ONNX embedding provider
lidarr docker-compose.services.yml Optional external Lidarr
postgres-local docker-compose.local.yml Local host-run Postgres
redis-local docker-compose.local.yml Local host-run Redis
audio-analyzer-local docker-compose.local.yml Local host-run analyzer profile
vibe-provider-dclap-local docker-compose.local.yml Local DCLAP provider profile

Core Runtime Variables

Variable Used In Container(s) Required Default What It Does
DATABASE_URL backend, backend-worker, audio-analyzer Required postgresql://soundspan:changeme@postgres:5432/soundspan (split stack) PostgreSQL connection string.
REDIS_URL backend, backend-worker, audio-analyzer Required redis://redis:6379 (split stack) Redis connection for queues, claims, one-time OIDC state, realtime coordination, and shared security rate limits.
POSTGRES_USER backend, backend-worker, postgres Required soundspan PostgreSQL username (and used to build DATABASE_URL).
POSTGRES_PASSWORD backend, backend-worker, postgres, soundspan (AIO) Required (production) split stack: changeme; AIO: generated when unset PostgreSQL password (and used to build DATABASE_URL). In AIO, an operator value is honored and persisted to /data/secrets/postgres_password; otherwise a strong value is generated once and persisted there.
POSTGRES_DB backend, backend-worker, postgres Required soundspan PostgreSQL database name (and used to build DATABASE_URL).
SESSION_SECRET backend, soundspan (AIO) Required split stack: none (compose refuses to start without it); AIO: operator value honored, else persisted/generated at /data/secrets/session_secret Stable 32+ character JWT signing fallback when JWT_SECRET is unset; also the final fallback for the API-key HMAC pepper. The name is historical; it signs JWTs, not cookie sessions. The backend image fails fast when it is missing, still the old published default, or too short (generate with openssl rand -base64 32). In AIO, an operator value takes precedence and is persisted; otherwise a value is generated and persisted.
SETTINGS_ENCRYPTION_KEY backend, soundspan (AIO) Required split stack: none (compose refuses to start without it); AIO: operator value honored, else persisted/generated at /data/secrets/encryption_key Encrypts stored credentials/settings. The backend image's entrypoint fails fast when it is unset, set to the insecure default, or shorter than 32 characters (generate with openssl rand -base64 32); it must stay stable or encrypted data becomes unreadable. In AIO, an operator value takes precedence and is written through to /data/secrets; otherwise the persisted or a newly generated value is used.
ENCRYPTION_KEY backend, backend-worker Optional (deprecated) unset; used only when SETTINGS_ENCRYPTION_KEY is absent Deprecated compatibility alias for SETTINGS_ENCRYPTION_KEY. The same 32-character minimum and insecure-default rejection apply. Prefer SETTINGS_ENCRYPTION_KEY for new deployments.
API_KEY_PEPPER backend, backend-worker, soundspan (AIO) Optional API_KEY_PEPPERSETTINGS_ENCRYPTION_KEYENCRYPTION_KEYSESSION_SECRET HMAC pepper for API keys stored as hashes. Keep the selected value stable; changing it invalidates existing hashed API keys.
SETTINGS_DECRYPT_FAIL_CLOSED backend, backend-worker Optional false When true, the legacy (pre-GCM, AES-256-CBC / plaintext-passthrough) at-rest decryption path fails closed: a stored value that is not an authenticated v2: envelope throws instead of being returned verbatim. Leave false until GET /api/admin/secrets-status reports settingsCipher.legacy: 0 (v1→v2 migration complete); flipping it before then would make any remaining legacy/plaintext value unreadable. Authenticated v2 ciphertext always fails closed regardless of this flag. See docs/UPGRADING.md.
SECRETS_DB_ONLY backend, backend-worker Optional false When true, integration secrets are read only from encrypted System Settings for exactly these keys: LASTFM_API_KEY, FANART_API_KEY, LIDARR_API_KEY, OPENAI_API_KEY, DEEZER_API_KEY, and AUDIOBOOKSHELF_API_KEY. Their env fallbacks are ignored and settings-driven .env sync omits them. The database/settings layer must be initialized before backend and worker services start; startup fails fast when it is unreadable.
MUSIC_PATH backend, backend-worker, tidal-streamer, ytmusic-streamer, analyzers, vibe-provider-dclap; also mount control in compose Required split stack host mount: ./music; AIO sample: /path/to/your/music; container path: /music Library root path/mount.
PORT backend (runtime), frontend (runtime), soundspan (AIO host publish var) Optional backend: 3006; frontend: 3030; AIO publish: 3030 Service bind/publish port control (context-dependent by container).
SOCKET_KEEPALIVE_DELAY_MS backend, soundspan (AIO) Optional 30000 Initial delay in milliseconds before TCP keepalive probes begin on accepted HTTP sockets so vanished peers are eventually reaped. Non-positive or malformed values use the default.
NODE_ENV backend, backend-worker, frontend Optional production (compose) Runtime mode.
BACKEND_PROCESS_ROLE backend, backend-worker Optional split stack: all; HA override for backend: api; worker: worker Role split for API/worker processes.
WORKER_HEALTH_PORT backend-worker Optional 3010 Worker health endpoint port (/health/live, /health/ready).
METRICS_TOKEN backend, backend-worker, soundspan (AIO) Optional unset Bearer token required by GET /metrics. When unset, the endpoint remains fail-closed with 401 unless METRICS_PUBLIC=true. Use a long random secret and rotate it with scraper configuration.
METRICS_PUBLIC backend, backend-worker, soundspan (AIO) Optional false When literal true, disables authentication for GET /metrics. This is unsafe outside an isolated private network because process, queue, cache, and route metrics reveal operational details.
WORKER_EVENT_LOOP_WARN_MS backend-worker Optional 1000 Event-loop stall watchdog: stalls at or above this many milliseconds log a warning naming the active Bull jobs.
WORKER_EVENT_LOOP_SAMPLE_MS backend-worker Optional 5000 Event-loop stall watchdog sampling interval in milliseconds.
GENERATION_ARTIST_WEIGHT_ALPHA backend Optional 0.5 Artist-diversity damping exponent for generated queues/mixes: each artist weighs n^alpha (0 = one share each, 1 = fully proportional to discography size).
GENERATION_ARTIST_SHARE_CEILING backend Optional 0.3 Hard per-artist ceiling for generated queues/mixes as a share of the queue size — no artist can exceed this share regardless of discography size.
LOG_LEVEL backend, backend-worker, python sidecars using shared logger Optional backend-worker compose: warn; otherwise env-dependent defaults Shared logger verbosity (debug, info, warn, error, silent for TS; Python also supports critical).
DATABASE_POOL_SIZE backend, backend-worker Optional role-aware: api=8, worker=4, all=12 Prisma DB pool connection limit.
DATABASE_POOL_TIMEOUT backend, backend-worker Optional 30 Prisma DB pool timeout in seconds.
LOG_QUERIES backend, backend-worker Optional false Enables Prisma query logging in development.
IVFFLAT_PROBES backend, backend-worker Optional 32 pgvector ivfflat.probes for "similar tracks" / vibe ANN queries — how many of the embedding index's 224 inverted lists each query scans, applied per-query on the same connection via a transaction-scoped set_config. Postgres' default of 1 makes recall near-random; the 32 default was benchmark-tuned for recall@10 ≈ 0.96 on the reference corpus. Higher = better recall, more scan cost. Values outside 1..32768 are clamped (Postgres only warns server-side and silently keeps probes=1 otherwise).
REDIS_FLUSH_ON_STARTUP backend, backend-worker, soundspan (AIO) Optional false everywhere (compose files, the Helm chart's config.redisFlushOnStartup, and the backend image's entrypoint fallback) When true, the backend image's entrypoint runs a destructive FLUSHALL against the configured Redis at container start. The safe default is false in every shipped config and in the entrypoint itself, preserving the Redis Streams/consumer-group state the analyzers rely on; opt in explicitly only for a dedicated-Redis cache clear.
TRANSCODE_CACHE_PATH backend Optional /app/cache/transcodes (compose) Directory for transcoding cache files.
TRANSCODE_CACHE_MAX_GB backend Optional 10 Max transcode cache size in GB.
TRANSCODE_CONCURRENCY backend Optional 3 (maximum 32) Maximum concurrent transcoding operations. Invalid or out-of-range values use the bounded default.
TRANSCODE_TIMEOUT_MS backend Optional 300000 (maximum 3600000) Per-transcode timeout in milliseconds. Invalid or out-of-range values use the bounded default.
BROWSE_IMAGE_CACHE_MAX_BYTES backend, soundspan (AIO) Optional 268435456 (256 MiB) Maximum combined bytes for cached browse thumbnails and their metadata sidecars; least-recently-used entries are evicted before writes exceed the bound.
BROWSE_IMAGE_CACHE_MAX_ENTRIES backend, soundspan (AIO) Optional 2048 Maximum cached browse-thumbnail entries; each entry includes an image and optional metadata sidecar.
FFMPEG_PATH backend Optional /usr/bin/ffmpeg Absolute path override for the ffmpeg binary used by transcoding and audio hashing. When unset, /usr/bin/ffmpeg is used; startup fails if it is missing or below the supported version floor.
STREAMING_TRACE_LOGS backend Optional false Enables playback trace logging when set to 1, true, yes, or on (case-insensitive).
ALLOWED_ORIGINS backend Optional unset (production denies cross-origin; development allows all) Allowed CORS origins (comma-separated), e.g. https://app.example.com. When unset, production denies cross-origin browser requests (deny-by-default; same-origin and no-Origin requests unaffected).
CORS_ALLOW_ALL backend Optional false Set true to restore the legacy permissive CORS behavior (reflect any origin with credentials) when no ALLOWED_ORIGINS allowlist is configured. Prefer ALLOWED_ORIGINS. See docs/UPGRADING.md.
LIDARR_WEBHOOK_ALLOW_UNAUTHENTICATED backend Optional false Set true to let POST /api/webhooks/lidarr accept requests when no webhook secret is configured in System Settings (legacy fail-open behavior). Default rejects with 401 (fail closed). See docs/UPGRADING.md.
SECURE_COOKIES backend Optional true when NODE_ENV=production, else false OIDC flow-cookie secure flag. Set true whenever the browser uses HTTPS, including TLS-terminating proxies and tunnels. OIDC then uses the hardened __Host-soundspan_oidc_flow cookie. Set false for plain HTTP, which uses the less-hardened unprefixed cookie.
TRUST_PROXY_HOPS backend Optional unset (trust all hops) Express trust proxy depth. Set to your real reverse-proxy count (usually 1) for spoof-resistant client-IP resolution in rate limiting; unset preserves the legacy trust-all behavior.
DOCS_PUBLIC backend Optional false Allows public API docs in production when true.
ADMIN_RESET_PASSWORD backend Optional unset One-time startup password reset for admin account.
JWT_SECRET backend Optional falls back to SESSION_SECRET Explicit JWT signing secret override.

Startup and Migration Controls

Variable Used In Container(s) Required Default What It Does
PRISMA_MIGRATE_MAX_ATTEMPTS backend, backend-worker Optional 12 Maximum number of prisma migrate deploy attempts during container startup.
PRISMA_MIGRATE_RETRY_DELAY_SECONDS backend, backend-worker Optional 5 Initial retry delay for startup migrations, in seconds.
PRISMA_MIGRATE_MAX_DELAY_SECONDS backend, backend-worker Optional 30 Maximum backoff delay for startup migrations, in seconds.
RUN_DB_MIGRATIONS_ON_STARTUP backend, backend-worker Optional true Runs prisma migrate deploy before the runtime starts. Set false only when migrations are managed separately.
PRISMA_GENERATE_ON_STARTUP backend, backend-worker Optional false Runs Prisma client generation during startup when set to true. Published images already include the generated client.

Authentication Variables

Variable Used In Container(s) Required Default What It Does
LOCAL_LOGIN_ENABLED backend, soundspan (AIO) Optional true Shows and accepts local username/password login. Startup rejects false when OIDC_ENABLED=false to prevent total lockout.
OIDC_ENABLED backend, soundspan (AIO) Optional false Enables OpenID Connect login, account linking, and the public OIDC callback.
OIDC_ISSUER_URL backend, soundspan (AIO) Required when OIDC_ENABLED=true empty OIDC issuer URL used for provider discovery. The value must be a valid HTTP(S) URL.
OIDC_CLIENT_ID backend, soundspan (AIO) Required when OIDC_ENABLED=true empty Confidential OIDC client identifier registered at the identity provider.
OIDC_CLIENT_SECRET backend, soundspan (AIO) Required when OIDC_ENABLED=true empty Confidential OIDC client secret. Keep it in a secret store. The Helm chart reads it from secrets.oidcClientSecret or the OIDC_CLIENT_SECRET key in secrets.existingSecret.
OIDC_REDIRECT_URI backend, soundspan (AIO) Required when OIDC_ENABLED=true empty Exact public callback URL registered at the provider. Use https://<host>/api/auth/oidc/callback. The value must be a valid HTTP(S) URL.
OIDC_WEB_BASE_URL backend, soundspan (AIO) Optional empty Canonical web origin for browser-facing /login and /settings redirects. Set it when the callback uses a sibling API subdomain or a different port. It requires OIDC_ENABLED=true and accepts an HTTP(S) origin only. A trailing slash is stripped. Paths, queries, and fragments stop startup.
OIDC_SCOPES backend, soundspan (AIO) Optional openid profile email Space-separated scopes requested from the provider. Add the provider's groups scope when OIDC role management needs it.
OIDC_AUTO_PROVISION backend, soundspan (AIO) Optional false Creates an account directly for an unknown OIDC identity. Keep this off for public identity providers. When false, unknown identities must redeem a soundspan invite code.
OIDC_MANAGE_ROLES backend, soundspan (AIO) Optional false Makes the IdP groups claim authoritative for linked-user roles. Startup requires OIDC_ENABLED=true and a non-empty OIDC_ADMIN_GROUP when this is true.
OIDC_GROUPS_CLAIM backend, soundspan (AIO) Optional groups Claim that supplies an array of group-name strings when OIDC role management is enabled.
OIDC_ADMIN_GROUP backend, soundspan (AIO) Required when OIDC_MANAGE_ROLES=true empty Group name that maps a linked user to the admin role. Startup fails when role management is enabled and this value is empty.
OIDC_EMAIL_CLAIM backend, soundspan (AIO) Optional email Claim used as the email hint for account linking and verified-email storage. Email never becomes the external identity key.
OIDC_NAME_CLAIM backend, soundspan (AIO) Optional name Claim used as cached display metadata and as a username fallback during provisioning.
OIDC_PROVIDER_NAME backend, soundspan (AIO) Optional SSO Provider label shown on the login and account-linking interfaces.

See the OIDC_SSO.md topology matrix for provider setup, OIDC_WEB_BASE_URL examples, cookie constraints, account linking, role management, and recovery guidance.

Distributed Runtime and Scheduler Controls

Variable Used In Container(s) Required Default What It Does
INTERNAL_API_SECRET backend, backend-worker, soundspan (AIO), vibe-provider-dclap (+ local DCLAP), ytmusic-streamer, tidal-streamer Required (32+ characters) none — docker-compose.yml refuses to start without it (generate with openssl rand -base64 32), and the HTTP sidecars reject the old published soundspan-internal-secret-change-me value as unconfigured; AIO honors an operator value, else generates & persists when unset Auth secret for trusted internal routes and backend→HTTP-sidecar auth (F31). The ytmusic-streamer/tidal-streamer/DCLAP FastAPI sidecars all require the matching x-internal-secret header except on /health, and reject every non-health API request (403) when the secret is unset, empty, or left at the published default. The backend image refuses to start when the value is missing, uses the published default, or is shorter than 32 characters. In AIO, an operator value takes precedence and is persisted to /data/secrets/internal_api_secret; otherwise the persisted or a newly generated value is used.
LISTEN_TOGETHER_REDIS_ADAPTER_ENABLED backend Optional true Enables Redis adapter fanout for cross-replica Socket.IO.
LISTEN_TOGETHER_STATE_SYNC_ENABLED backend Optional true Enables Redis pub/sub state sync for Listen Together. State sync requires LISTEN_TOGETHER_STATE_STORE_ENABLED=true; valid sync/store pairs are true/true, false/true, and false/false. Startup rejects true/false.
LISTEN_TOGETHER_STATE_SYNC_CHANNEL backend Optional listen-together:state-sync Redis pub/sub channel name for Listen Together state sync. Change it only when several soundspan installations share one Redis and must not see each other's events.
LISTEN_TOGETHER_STATE_STORE_ENABLED backend Optional true Enables Redis-backed authoritative group state snapshots. It must remain enabled when state sync is enabled.
LISTEN_TOGETHER_STATE_STORE_TTL_SECONDS backend Optional 21600 TTL for persisted Listen Together state.
LISTEN_TOGETHER_STATE_STORE_KEY_PREFIX backend Optional listen-together:state Redis key prefix for Listen Together snapshots.
LISTEN_TOGETHER_MUTATION_LOCK_ENABLED backend Optional true Enables per-group distributed mutation lock.
LISTEN_TOGETHER_MUTATION_LOCK_TTL_MS backend Optional 3000 Lock TTL for mutation critical sections. The minimum accepted value is 500 ms.
LISTEN_TOGETHER_MUTATION_LOCK_RENEW_INTERVAL_MS backend Optional 1000 Interval for token-guarded mutation-lock lease renewal. It must be positive and no greater than one third of the lock TTL; startup rejects rather than clamps unsafe combinations.
LISTEN_TOGETHER_PUBLICATION_DEADLINE_MS backend Optional 750 Deadline for each Redis, cluster, and socket publication operation performed by Listen Together. It must be positive and lower than the lock TTL; startup rejects unsafe combinations.
LISTEN_TOGETHER_MUTATION_LOCK_PREFIX backend Optional listen-together:mutation-lock Redis key prefix for mutation locks.
LISTEN_TOGETHER_RECONNECT_SLO_MS backend Optional 5000 Reconnect latency warning threshold.
LISTEN_TOGETHER_ALLOW_POLLING backend Optional false Allows polling fallback transport when true.
SCHEDULER_CLAIM_SKIP_WARN_THRESHOLD backend, backend-worker Optional 3 Warn threshold for consecutive skipped scheduler claims.
READINESS_REQUIRE_DEPENDENCIES backend, backend-worker Optional true Makes readiness depend on Redis/Postgres health.
READINESS_DEPENDENCY_CHECK_INTERVAL_MS backend, backend-worker Optional 5000 Min interval between readiness dependency checks.
READINESS_DEPENDENCY_CHECK_TIMEOUT_MS backend, backend-worker Optional 2000 Timeout per readiness dependency probe.
DISCOVER_PROCESSOR_LOCK_TTL_MS backend-worker Optional 2700000 TTL for per-user Discover processor lock claims.
ENRICHMENT_CLAIM_TTL_MS backend-worker Optional 900000 TTL for unified enrichment cycle claim lock.
MOOD_BUCKET_CLAIM_TTL_MS backend-worker Optional 120000 TTL for mood bucket worker cycle claim lock.
SCAN_FILE_CONCURRENCY backend-worker, backend Optional 3 Bounds concurrent per-file metadata parses during library scans to a value from 1 through 10.
TRACK_RECONCILIATION_MAX_ROWS backend, backend-worker Optional 10000 Hard per-run row cap for remote-to-local track mapping reconciliation. The scheduler persists a shared keyset cursor in Redis so later runs continue across larger backlogs. Valid values are 1..100000; invalid values use the default.
TRACK_RECONCILIATION_TIMEOUT_MS backend, backend-worker Optional 600000 Deadline for one remote-to-local track mapping reconciliation run. Valid values are 1..3300000 (55 minutes); invalid values use the default. In-flight Prisma queries finish before cancellation is observed.
TRACK_MAPPING_STALENESS_INTERVAL_MS backend, backend-worker Optional 21600000 (6 hours) Interval between bounded checks for track mappings whose remote targets no longer exist. Invalid or non-positive values use the default.
PROVIDER_TRACK_RETENTION_DAYS backend, backend-worker, soundspan (AIO) Optional 30 Days to retain stale provider-track mappings and recent provider play history before daily garbage collection. Must be an integer from 1 through 3650.
TRACK_REMOVAL_RETENTION_DAYS backend, backend-worker Optional 90 Days to retain soft-removed tracks for automatic revival before the daily purge permanently deletes them. Must be an integer greater than or equal to 0; 0 purges removed tracks on the next purge cycle.
FEDERATION_TOMBSTONE_RETENTION_DAYS backend, backend-worker, soundspan (AIO) Optional 90 Days to retain federation deletion markers for peer delta synchronization. Must be an integer greater than or equal to 3; cleanup runs on the terminal track-purge page.
FEDERATION_SYNC_INTERVAL_MINUTES backend-worker, soundspan (AIO) Optional 15 Minutes between bounded consumer-peer catalog sync scheduling passes. Must be an integer greater than or equal to 1; the worker also runs one health check at startup and hourly thereafter.
FEDERATION_ALLOW_PRIVATE_PEERS backend, backend-worker, soundspan (AIO) Optional false Unsafe administrator opt-in for federation peers using private, loopback, link-local, CGNAT, or benchmark addresses that are blocked by default for both literal and DNS-resolved destinations. HTTPS, credential-free URL, per-attempt DNS resolution, and connection pinning requirements still apply. Use network egress policy as defense in depth.
FEDERATION_ALLOW_PROXY backend, backend-worker, soundspan (AIO) Optional false Administrator opt-in that lets federation egress honor HTTPS_PROXY/ALL_PROXY/NO_PROXY again (federation peers are HTTPS-only, so HTTP_PROXY never applies). Federation still resolves and validates every peer address before each attempt and fails closed on disallowed destinations, but no connection is pinned in this mode: proxied requests are connected by the proxy, and requests exempted by NO_PROXY connect directly without a pin. Only enable this when federation must traverse a mandatory egress proxy; the proxy then becomes part of your trust boundary. The bundled Compose files pass HTTPS_PROXY/ALL_PROXY/NO_PROXY (both cases) through from the host so the opt-in has a proxy to use; Helm deployments set them through the chart's global.env or per-component env values.

Frontend Variables

Variable Used In Container(s) Required Default What It Does
BACKEND_URL frontend Optional (required when default route is not correct) unset: http://127.0.0.1:3006; split stack: http://backend:3006 Server-side URL used by frontend proxy and SSR.
STREAMING_ENGINE_MODE frontend, soundspan (AIO) Optional native Playback engine selection, validated by the container entrypoint: native (default — single native <audio> element; see NATIVE_AUDIO_ENGINE.md) or howler (legacy engine and gated fallback; Android WebView deployments are pinned to it automatically). Removed and unrecognized values are invalid, log a warning, and fall back to native. Container runtime env, not a NEXT_PUBLIC_* build arg.
NEXT_PUBLIC_API_URL frontend (build-time) Optional (build-time only) empty Explicit browser API base URL. Runtime changes on prebuilt images do not affect browser bundle behavior.
NEXT_PUBLIC_API_PATH_MODE frontend (build-time) Optional (build-time only) auto Browser API routing mode: auto, proxy, or direct. Runtime changes on prebuilt images do not affect browser bundle behavior.
NEXT_PUBLIC_LISTEN_TOGETHER_ALLOW_POLLING frontend (build-time) Optional (build-time only) false Browser polling fallback toggle for Listen Together socket client. Runtime changes on prebuilt images do not affect browser bundle behavior.
NEXT_PUBLIC_LOG_LEVEL frontend (build-time) Optional (build-time only) info (dev), warn (prod) Browser-visible frontend logger verbosity (debug, info, warn, error, silent). Uses NEXT_PUBLIC_ because client-side code cannot read non-public env vars.
NEXT_PUBLIC_BUILD_TYPE frontend (build-time) Optional (build-time only) nightly (compose build arg) Marks build channel (nightly/release semantics).
NEXT_PUBLIC_APP_VERSION frontend (build-time) Optional (build-time only) frontend/package.json version Explicit app version override in UI.
ANALYZE frontend (build-time) Optional (build-time only) unset (false) Enables Next.js bundle analyzer when true.
CSP_ENFORCE frontend, soundspan (AIO) Optional (runtime) false Set exactly true to send the nonce-based document policy as Content-Security-Policy. Every other value keeps the default Content-Security-Policy-Report-Only rollout mode. Restart the frontend after changing it.
CSP_REPORT_URI frontend, soundspan (AIO) Optional (runtime) empty Optional root-relative or HTTPS CSP report collector. Adds report-uri, report-to, and Reporting-Endpoints; soundspan has no built-in collector. Unsafe or malformed values are ignored. Reports can contain document and blocked-resource URLs.

Integration and Feature Variables

Variable Used In Container(s) Required Default What It Does
SOUNDSPAN_CALLBACK_URL backend, soundspan (AIO) Optional split stack: http://backend:3006; AIO: http://host.docker.internal:3030 Callback URL used for webhook/integration callbacks (for example Lidarr completion hooks).
AUDIO_ANALYSIS_ENABLED backend, backend-worker Optional true Feature flag for audio analysis queueing/consumption (Essentia + provider-backed vibe embeddings), the mood-bucket worker, and the /api/analysis + /api/vibe routes. Set false to disable; analyzer containers should then also be disabled.
LOUDNESS_TARGET_LUFS backend, soundspan (AIO) Optional -18 Server reference loudness for OpenSubsonic ReplayGain metadata and web-player normalization. Accepts values from -30 through -10 LUFS; invalid values stop backend startup.
AUDIO_ANALYSIS_QUEUE_MAX_DEPTH backend-worker Optional 100 Maximum number of pending MusicCNN jobs admitted to Redis. Additional pending tracks remain in PostgreSQL until capacity is available.
LOUDNESS_BACKFILL_BATCH_SIZE backend-worker Optional 25 Tracks inspected per loudness backfill scheduler page. Values are clamped to 1 through 200; queue admission still obeys AUDIO_ANALYSIS_QUEUE_MAX_DEPTH.
VIBE_ANALYSIS_QUEUE_MAX_DEPTH backend-worker Optional 100 Maximum number of pending provider-backed vibe jobs admitted to Redis. Additional pending tracks remain in PostgreSQL until capacity is available.
VIBE_PROVIDER_URL backend, backend-worker, soundspan (AIO) Optional split-stack Compose: http://vibe-provider-dclap:8092; AIO: http://localhost:8092 Provider base URL for vibe text and audio embeddings. Compose tracks DCLAP_HTTP_PORT when building this URL, and the AIO default targets its embedded DCLAP provider. In the worker, a new (family, checkpointHash) tuple is registered as a migrating space and backfilled automatically.
VIBE_EMBED_CONCURRENCY backend-worker Optional 1 Concurrent provider-backed audio embedding jobs. Accepted range is 1 through 32. Compose and AIO retain the safe application default of 1. The Helm backend-worker defaults to min(32, max(1, 2 × vibeProviderDclap.replicas)); size explicit overrides at approximately two jobs per DCLAP replica unless measured provider capacity indicates otherwise.
VIBE_MAP_WORKER_MEMORY_MB backend Optional 512 Heap ceiling in MB for the vibe map's UMAP projection worker thread. When the worker exceeds it, the map retries with a smaller track sample instead of failing. Accepted range is 128 through 4096.
VIBE_SPACE_CUTOVER_THRESHOLD backend-worker Optional 0.95 Eligible-local-track coverage required before a migrating embedding space is cut over automatically. Accepted range is 0.5 through 1.
VIBE_SPACE_CUTOVER_ALLOW_FAILED backend-worker Optional false Explicitly acknowledges an embedding migration failure tail above the cutover threshold's implied tolerance. Keep false to hold cutover and retry failures through /api/analysis/vibe/retry; set true only after reviewing the failed tracks.
VIBE_SPACE_RETIREMENT_GRACE_DAYS backend-worker Optional 7 Days retired vectors remain available for rollback before bounded cleanup removes them and their partial ANN index. Accepted range is 1 through 90.
ANALYSIS_QUEUE_RESERVATION_TTL_SECONDS backend-worker Optional 3600 TTL for per-track Redis admission reservations that suppress duplicate queue entries. Maximum accepted value is 86400 seconds.
DISCOVERY_ENABLED backend, backend-worker Optional true Feature flag for Discover Weekly (cron + queue processors), the /api/discover + /api/recommendations routes, and the discovery auto-download lifecycle.
AUTO_PLAYLISTS_ENABLED backend, backend-worker Optional true Feature flag for Made For You mixes (on-demand programmatic playlists) and the /api/mixes routes.
FEDERATION_ENABLED backend, backend-worker, soundspan (AIO) Optional false Enables host and consumer federation routes, peer linking, catalog sync and health jobs, identity initialization, tombstones, and federated playback. When disabled, federation prefixes return 404 with code: FEATURE_DISABLED, jobs are not registered, and cleanup writes no tombstones. The peer-visible instance name derives from the container HOSTNAME, with soundspan as the default.
FEATURE_REQUESTS backend, backend-worker Optional true Feature flag for the user music-request queue: the /api/requests routes and the request-fulfillment reconciler job. When disabled, /api/requests returns 404 with code: FEATURE_DISABLED and the reconciler is not registered.
REQUESTS_PER_USER_PER_DAY backend Optional 10 Rolling 24-hour cap on music requests per user. Cancelled and denied requests still count toward the cap.
LIDARR_ENABLED backend, backend-worker Optional false Enables Lidarr integration logic from env fallback paths.
LIDARR_URL backend, backend-worker Required when LIDARR_ENABLED=true unset Lidarr base URL.
LIDARR_API_KEY backend, backend-worker Required when LIDARR_ENABLED=true unset Lidarr API key.
LASTFM_API_KEY backend, backend-worker Optional unset Last.fm metadata/recommendation API key. When unset, Last.fm features stay disabled unless an encrypted system setting supplies a key.
LASTFM_SHARED_SECRET backend, backend-worker Required for Last.fm scrobble forwarding unset Last.fm application shared secret used only for signed per-user auth and scrobble calls. Keep it in runtime secret storage; it is never returned by the API.
FANART_API_KEY backend Optional unset Fanart.tv API key for artist images/backgrounds. A database-stored key (System Settings) takes precedence; this var is the .env fallback used only when no key is stored. By default, the background enrichment cycle's ImageProviderService retains its historical env-only behavior. Under SECRETS_DB_ONLY=true, that enrichment path instead reads the stored System Settings key and ignores this env var.
OPENAI_API_KEY backend, backend-worker Optional unset OpenAI key for AI-assisted recommendation features.
DEEZER_API_KEY backend, backend-worker Optional unset Deezer API key override.
DISCOVERY_MODE backend, backend-worker Optional recommendation Discovery mode (recommendation or deprecated legacy). legacy is accepted and serves the same modern discovery pages; each API/worker process logs one startup deprecation warning (unset the variable to remove it). In the background worker, legacy additionally keeps Discover Weekly's admin-only download-based generation.
CATALOG_PERSISTENCE backend, backend-worker Optional enabled Controls metadata-catalog persistence. Set off, false, or 0 to disable it.
CATALOG_RETENTION_DAYS backend, backend-worker Optional 180 Days to retain persisted metadata-catalog albums before retention sweeps remove them.
AUDIOBOOKSHELF_URL backend, backend-worker Optional unset Audiobookshelf service URL (env fallback path).
AUDIOBOOKSHELF_API_KEY backend, backend-worker Optional (required if using the env fallback) unset Audiobookshelf API key for env-based fallback configuration. Consumed with AUDIOBOOKSHELF_URL via config.audiobookshelf when no database-stored Audiobookshelf settings exist.
TIDAL_SIDECAR_URL backend Optional split stack: http://tidal-streamer:8585; AIO/host-run: http://127.0.0.1:8585 URL for TIDAL sidecar service.
YTMUSIC_STREAMER_URL backend Optional split stack: http://ytmusic-streamer:8586; AIO/host-run: http://127.0.0.1:8586 URL for YouTube Music sidecar service.
ENABLE_ENV_FILE_SYNC backend Optional enabled outside Kubernetes; implicit opt-out in Kubernetes Controls settings-driven .env synchronization. false disables writes. Kubernetes skips sync unless this is explicitly true.
ENV_FILE_PATH backend Optional ../.env relative to the backend process working directory Overrides the file updated by settings-driven .env synchronization. Root-level .env writes require ENABLE_ENV_FILE_SYNC=true.
PLAYLIST_LOG_DIR backend Optional ./logs/playlists relative to the process working directory Overrides the directory for playlist import session and event logs.
YTMUSIC_REGION backend Optional US Region hint passed to the YouTube Music browse/discovery proxies.

ghcr.io/soundspan/soundspan-tidal-downloader is published as an alias of soundspan-tidal-streamer, and the tidal-downloader Compose hostname resolves to tidal-streamer.

Sidecar Variables

Variable Used In Container(s) Required Default What It Does
TIDDL_PATH tidal-streamer Optional /data/.tiddl Sidecar data/config path for tiddl artifacts.
TIDAL_TRACK_DELAY tidal-streamer Optional 3 Delay between TIDAL track downloads (seconds).
DEBUG tidal-streamer, ytmusic-streamer Optional unset Debug logging toggle for sidecar services.
YTMUSIC_DEBUG compose host variable mapping to ytmusic-streamer:DEBUG Optional unset Convenience key in compose for ytmusic debug logging.
DATA_PATH ytmusic-streamer Optional /data Sidecar data/cache path.
YTMUSIC_BATCH_CONCURRENCY ytmusic-streamer Optional 3 Max concurrent batched search requests.
YTMUSIC_BATCH_DELAY_MIN ytmusic-streamer Optional 0.3 Min delay between batched search calls (seconds).
YTMUSIC_BATCH_DELAY_MAX ytmusic-streamer Optional 1.0 Max delay between batched search calls (seconds).
YTMUSIC_EXTRACT_DELAY_MIN ytmusic-streamer Optional 0.5 Min delay between stream extraction calls (seconds).
YTMUSIC_EXTRACT_DELAY_MAX ytmusic-streamer Optional 2.0 Max delay between stream extraction calls (seconds).
YTMUSIC_EXTRACT_TIMEOUT ytmusic-streamer Optional 60 Overall per-request deadline (seconds) for yt-dlp stream-URL extraction; on expiry the request fails fast with HTTP 504 instead of hanging.
YTMUSIC_YTDLP_SOCKET_TIMEOUT ytmusic-streamer Optional 20 yt-dlp socket_timeout (seconds) bounding individual network reads during extraction and downloads, so a stalled worker thread eventually frees.
YTMUSIC_SPOOL_DIR ytmusic-streamer Optional <tmpdir>/soundspan-ytmusic-spool Directory for the YouTube Music HLS spool. yt-dlp downloads complete tracks here; the sidecar serves player Range requests from these local files.
YTMUSIC_SPOOL_MAX_BYTES ytmusic-streamer Optional 268435456 Disk budget (bytes) for completed spool files; least-recently-used tracks are evicted past this bound. Clamped to a 16 MiB minimum.
YTMUSIC_SPOOL_TIMEOUT ytmusic-streamer Optional 110 Per-request deadline (seconds) a client waits for a spool download before HTTP 504. Must stay below the backend's 120 s proxy timeout; the shared download keeps running for later retries.
YTMUSIC_SPOOL_CONCURRENCY ytmusic-streamer Optional 2 Max concurrent yt-dlp spool downloads (clamped to 1–4). Requests for the same track always share one download.
YTMUSIC_SPOOL_DOWNLOAD_TIMEOUT ytmusic-streamer Optional 300 Progress-based deadline (seconds) for one yt-dlp spool download; enforced at each download progress event, with YTMUSIC_YTDLP_SOCKET_TIMEOUT bounding individual stalled reads during extraction and download. Live streams are rejected outright.
YTMUSIC_SPOOL_TRACK_MAX_BYTES ytmusic-streamer Optional 67108864 Per-track downloaded-bytes cap for one spool job (64 MiB default, 1 MiB minimum); the download aborts past this, bounding disk use by any single title.
YTMUSIC_SEARCH_CACHE_TTL ytmusic-streamer Optional 300 Search cache TTL in seconds (0 disables cache).
YTMUSIC_LIBRARY_ERROR_CACHE_SECONDS ytmusic-streamer Optional 600 Per-(user_id, limit, mixes_only) negative-cache TTL in seconds for upstream HTTP 400 failures from /library/playlists (0 disables the cache); cached requests fail fast with HTTP 502 instead of calling YouTube again.
YTMUSIC_SEARCH_CACHE_MAX ytmusic-streamer Optional 1024 Max in-memory search-cache entries; the oldest are evicted past this bound to cap memory.
YTMUSIC_STREAM_CACHE_MAX ytmusic-streamer Optional 1024 Max in-memory stream-URL cache entries; the oldest are evicted past this bound to cap memory.
YTMUSIC_SEARCH_MODE ytmusic-streamer Optional auto Search strategy: auto (native-first, falling back per user to the TV parser when YouTube returns invalid-argument errors), tv (legacy TV parser), or native (ytmusicapi yt.search() only).
YTMUSIC_LANGUAGE ytmusic-streamer Optional en BCP-47 language code forwarded to all YTMusic() client instances; controls the language of shelf titles and content descriptions regardless of the server's geo-IP locale.
YTMUSIC_HOME_FILTERED_SHELVES ytmusic-streamer Optional Quick picks Comma-separated, case-insensitive list of shelf titles to exclude from /home responses.
YT_DOWNLOAD_DIR ytmusic-streamer Optional /music/YouTube Downloads Destination directory for /yt/download audio files. Must live inside the shared /music volume so the backend's library scanner picks up completed downloads; Helm deployments need an RWX music volume in multi-node clusters.
YT_DOWNLOAD_CONCURRENCY ytmusic-streamer Optional 2 Max concurrent YouTube download jobs processed by the sidecar's download worker pool.
YT_ALBUM_DOWNLOAD_CONCURRENCY ytmusic-streamer Optional 1 Worker-thread pool size for library album download jobs (the YouTube Music download source). Tracks within one album always download sequentially; this bounds how many album jobs make progress at once.
DCLAP_IMAGE_VERSION vibe-provider-dclap, soundspan (AIO) Optional split: latest; local: local; direct/AIO runtime: dclap-student-v1 Revision label returned by /v1/space; Compose maps the deployment VERSION value to this variable.
DCLAP_HTTP_PORT vibe-provider-dclap, soundspan (AIO) Optional 8092 Internal HTTP bind port for health, space identity, and embedding routes. Compose does not publish it to the host.
DCLAP_ONNX_INTRA_OP_THREADS vibe-provider-dclap, soundspan (AIO) Optional 1 Positive ONNX Runtime intra-operation thread limit; shared BLAS/OpenMP thread settings use the same cap.
DCLAP_MODEL_PATH vibe-provider-dclap, soundspan (AIO) Optional standalone: /app/models; AIO: /app/vibe-provider-dclap/models Directory containing the pinned audio graph, external weights, and text-tower ONNX artifacts.
DCLAP_TOKENIZER_PATH vibe-provider-dclap, soundspan (AIO) Optional standalone: /app/tokenizer; AIO: /app/vibe-provider-dclap/tokenizer Directory containing the offline-vendored teacher tokenizer snapshot.
DCLAP_MODEL_IDLE_TIMEOUT Compose host mapping to vibe-provider-dclap:MODEL_IDLE_TIMEOUT Optional 300 Deployment-facing idle timeout in seconds before the DCLAP model bundle unloads; 0 disables idle unloading.
MODEL_IDLE_TIMEOUT vibe-provider-dclap, soundspan (AIO) Optional 300 Runtime idle timeout consumed by the DCLAP provider; Compose supplies it from DCLAP_MODEL_IDLE_TIMEOUT, while AIO uses it directly with AUDIO_MODEL_IDLE_TIMEOUT as a compatibility fallback.

Analyzer Variables

The AIO image maps its deployment-facing analyzer variables into the MusicCNN runtime. Its AUDIO_MODEL_IDLE_TIMEOUT fallback is also passed to the embedded DCLAP provider.

Variable Used In Container(s) Required Default What It Does
AUDIO_ANALYSIS_BATCH_SIZE soundspan (AIO) and compose host variable mapping to audio-analyzer:BATCH_SIZE Optional 10 Batch size for MusicCNN analyzer.
AUDIO_ANALYSIS_INTERVAL compose host variable mapping to audio-analyzer:SLEEP_INTERVAL Optional 5 Loop interval between analyzer cycles (seconds).
AUDIO_BRPOP_TIMEOUT soundspan (AIO) and compose host variable mapping to audio-analyzer:BRPOP_TIMEOUT Optional 30 Redis blocking pop timeout for analyzer worker (seconds).
AUDIO_REDIS_SOCKET_TIMEOUT soundspan (AIO) and audio-analyzer Optional 35 Redis socket read timeout for the MusicCNN queue worker (seconds). The runtime enforces an effective minimum of BRPOP_TIMEOUT + 5 so blocking queue polls complete before the socket deadline.
AUDIO_MODEL_IDLE_TIMEOUT soundspan (AIO) and compose host variable mapping to audio-analyzer:MODEL_IDLE_TIMEOUT Optional 300 Idle timeout before unloading analyzer ML models (seconds); in AIO it is also the embedded DCLAP fallback unless MODEL_IDLE_TIMEOUT is set directly.
AUDIO_ANALYSIS_WORKERS soundspan (AIO) and compose host variable mapping to audio-analyzer:NUM_WORKERS Optional 2 Parallel MusicCNN analyzer workers.
AUDIO_ANALYSIS_THREADS_PER_WORKER soundspan (AIO) and compose host variable mapping to audio-analyzer:THREADS_PER_WORKER Optional 1 CPU threads per MusicCNN analyzer worker.
MAX_FILE_SIZE_MB audio-analyzer Optional 500 Hard file-size cap for analysis candidates (0 disables cap).
BATCH_ANALYSIS_TIMEOUT_SECONDS audio-analyzer Optional 900 Timeout for a batch before failure handling.
MAX_RETRIES audio-analyzer Optional 3 Max retries for failed analyzer jobs.
STALE_PROCESSING_MINUTES audio-analyzer Optional 15 Resets tracks stuck in processing state after this age.
MAX_ANALYZE_SECONDS audio-analyzer Optional 90 Max audio duration analyzed per track clip.
LOUDNESS_MEASURE_TIMEOUT_SECONDS audio-analyzer Optional 120 Per-track deadline for the full-stream ffmpeg EBU R128 pass; values are clamped to 13600 seconds.
ACOUSTID_API_KEY audio-analyzer, soundspan (AIO) Optional unset Enables resumable AcoustID lookups for locally computed Chromaprint fingerprints. When unset, fingerprint computation continues and lookup errors are not surfaced.
DB_RECONCILE_MIN_INTERVAL_SECONDS audio-analyzer Optional defaults to BRPOP_TIMEOUT Minimum DB reconciliation interval while idle.
DB_RECONCILE_MAX_INTERVAL_SECONDS audio-analyzer Optional max(BRPOP_TIMEOUT*12, 60) Maximum DB reconciliation interval while idle.
DB_RECONCILE_BACKOFF_MULTIPLIER audio-analyzer Optional 2.0 Idle reconciliation backoff multiplier.

Debug and Trace Variables

Variable Used In Container(s) Required Default What It Does
PODCAST_DEBUG backend Optional 0 Enables extra podcast streaming/cache debug logs when set to 1.
DEBUG_WEBHOOKS backend Optional false Enables verbose webhook route diagnostics.
SUBSONIC_TRACE_LOGS backend Optional false Enables request/response trace logs for Subsonic endpoints.

Compose Host-Side Control Variables

These are read by Docker Compose itself and are not always injected into containers as runtime env vars.

Variable Applies To Required Default What It Does
SOUNDSPAN_AIO_IMAGE docker-compose.aio.yml Optional ghcr.io/soundspan/soundspan AIO image repository.
VERSION docker-compose.aio.yml Optional latest AIO image tag.
SOUNDSPAN_AIO_CONTAINER_NAME AIO container Optional soundspan AIO container name override.
SOUNDSPAN_AIO_DATA_VOLUME AIO volume Optional soundspan_data AIO data volume name override.
SOUNDSPAN_DB_CONTAINER_NAME split postgres Optional soundspan_db Postgres container name override.
SOUNDSPAN_REDIS_CONTAINER_NAME split redis Optional soundspan_redis Redis container name override.
SOUNDSPAN_TIDAL_CONTAINER_NAME split TIDAL sidecar Optional soundspan_tidal_streamer TIDAL sidecar container name override.
SOUNDSPAN_YTMUSIC_CONTAINER_NAME split ytmusic sidecar Optional soundspan_ytmusic_streamer YTMusic sidecar container name override.
SOUNDSPAN_AUDIO_ANALYZER_CONTAINER_NAME split analyzer Optional soundspan_audio_analyzer MusicCNN analyzer container name override.
SOUNDSPAN_DCLAP_CONTAINER_NAME split DCLAP provider Optional soundspan_vibe_provider_dclap DCLAP provider container name override.
SOUNDSPAN_LIDARR_CONTAINER_NAME optional Lidarr Optional soundspan_lidarr Lidarr container name override.
SOUNDSPAN_NETWORK_NAME split stack network Optional soundspan_network Docker network name override.
BACKEND_PORT split backend port publish Optional 3006 (0 recommended for local replica scale-out) Host port mapped to backend container port 3006.
FRONTEND_PORT split frontend port publish Optional 3030 Host port mapped to frontend container port 3030.
POSTGRES_PORT split postgres port publish Optional 5432 Host port mapped to postgres container port 5432. Bound to 127.0.0.1 only; see docs/UPGRADING.md for the override-file escape hatch to publish on other interfaces.
REDIS_PORT split redis port publish Optional 6379 Host port mapped to redis container port 6379. Bound to 127.0.0.1 only; see docs/UPGRADING.md for the override-file escape hatch to publish on other interfaces.
LIDARR_PORT optional Lidarr port publish Optional 8686 Host port mapped to Lidarr container port 8686.
PORT AIO port publish Optional 3030 Host port mapped to AIO container port 3030.
DOWNLOAD_PATH optional Lidarr volume mount Optional ./downloads Host download path mounted into Lidarr /downloads.
PUID optional Lidarr Optional 1000 Linux user ID for Lidarr container permissions.
PGID optional Lidarr Optional 1000 Linux group ID for Lidarr container permissions.
TZ AIO + optional Lidarr Optional UTC Container timezone.

Local Host-Run Profile Variables

Used primarily with docker-compose.local.yml (host-run backend/frontend; containers for infra + optional analyzers):

Variable Used In Container(s) Required Default What It Does
AUDIO_REDIS_SOCKET_TIMEOUT audio-analyzer-local Optional 15 MusicCNN Redis socket read timeout; effective minimum is local BRPOP_TIMEOUT + 5 seconds.

Operational Notes

Topic Recommendation
Secrets Always set SESSION_SECRET, SETTINGS_ENCRYPTION_KEY, POSTGRES_PASSWORD, and INTERNAL_API_SECRET explicitly in production. Also set OIDC_CLIENT_SECRET when OIDC is enabled.
API routing mode Keep NEXT_PUBLIC_API_PATH_MODE=auto unless you intentionally need direct browser calls (direct).
Frontend build-time vars In prebuilt frontend images, NEXT_PUBLIC_API_URL, NEXT_PUBLIC_API_PATH_MODE, and NEXT_PUBLIC_LISTEN_TOGETHER_ALLOW_POLLING require an image rebuild to change browser behavior.
Frontend CSP rollout Keep CSP_ENFORCE=false until report-only violations have been reviewed across playback, Listen Together, offline/PWA, share-page, and administrator workflows.
HA behavior Keep Listen Together Redis/state/lock flags enabled for multi-replica correctness.
Drift control When adding/changing/removing env vars in compose, backend/frontend config, or sidecars, update this file in the same PR.