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:
- Covers deployment/runtime variables used by containerized services in this repo.
- Includes compose host-side variables (ports, image tags, container names, mounts).
- Excludes test-only/CI-only variables (
SOUNDSPAN_TEST_*, Playwright helpers, etc.).
Primary sources:
docker-compose.ymldocker-compose.aio.ymldocker-compose.override.ha.ymldocker-compose.services.ymldocker-compose.local.ymlbackend/src/config.tsbackend/src/utils/db.tsfrontend/lib/api/core.tsservices/audio-analyzer/analyzer.pyservices/audio-analyzer/loudness.pyservices/common/analyzer_env.pyservices/common/environment.pyservices/vibe-provider-dclap/settings.pyservices/vibe-provider-dclap/music_path.pyservices/tidal-streamer/tidal_runtime.pyservices/ytmusic-streamer/ytmusic_runtime.pyservices/ytmusic-streamer/ytmusic_client.pyservices/ytmusic-streamer/ytmusic_stream.pyservices/ytmusic-streamer/ytmusic_downloads.pyservices/ytmusic-streamer/ytmusic_browse.py
Status labels:
Required: must exist at runtime for service startup/feature operation (compose may still supply a default automatically).Required (production): technically has a fallback, but must be explicitly set in real deployments.Optional: safe default exists, or feature is disabled when unset.
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_PEPPER → SETTINGS_ENCRYPTION_KEY → ENCRYPTION_KEY → SESSION_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 1–3600 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. |