# Deployment VibeSonic is a single static binary plus `ffmpeg`/`ffprobe` on `PATH` (and optionally `mpv`, for the jukebox feature). Docker is convenient but never required for normal operation. ## Configuration All configuration is via `VIBESONIC_*` environment variables — see [`docs/configuration.md`](configuration.md) for the full list. There is no config file. ## Running natively ```sh go build -o vibesonic ./cmd/vibesonic export VIBESONIC_DATA_DIR=/var/lib/vibesonic export VIBESONIC_MUSIC_DIRS=/music export VIBESONIC_SESSION_SECRET="$(openssl rand -hex 32)" ./vibesonic ``` For a persistent deployment, see [`systemd/vibesonic.service`](../systemd/vibesonic.service) — install it with: ```sh sudo useradd --system --home /var/lib/vibesonic --shell /usr/sbin/nologin vibesonic sudo install -m 0755 vibesonic /usr/local/bin/vibesonic sudo cp systemd/vibesonic.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now vibesonic ``` The unit runs as a dedicated `vibesonic` user (not `DynamicUser`, because it needs a stable UID with read access to your music library — `DynamicUser` allocates a random UID per start, which doesn't compose well with a fixed set of file permissions on external media). Grant that user read access to your music folders: ```sh sudo usermod -aG vibesonic # or, more simply, make the music tree group- or world-readable ``` `StateDirectory=vibesonic` in the unit creates and owns `/var/lib/vibesonic` (mode `0700`) for you — don't create it manually with different permissions first. The unit also applies standard hardening (`ProtectSystem=strict`, `NoNewPrivileges`, `PrivateDevices`, etc.) and `Restart=on-failure`. Remember to set a real `VIBESONIC_SESSION_SECRET` in the unit (or in an `EnvironmentFile=`) — the shipped example is a placeholder. ## Running with Docker ```sh docker build -t vibesonic . ``` Runtime image is `debian:bookworm-slim` with `ffmpeg`, `mpv`, and CA certificates installed, running as a non-root `vibesonic` user (uid 10001). See [`Dockerfile`](../Dockerfile). Example Compose file (also in [`docker-compose.yml`](../docker-compose.yml)): ```yaml services: vibesonic: image: vibesonic ports: - "8080:8080" volumes: - ./data:/var/lib/vibesonic - /mnt/music:/music:ro environment: VIBESONIC_SESSION_SECRET: "change-me-to-a-random-string" ``` Mount your music read-only (`:ro`) — VibeSonic never writes to it. `./data` (or a named volume) holds the SQLite database, cover art cache, and downloaded podcast episodes; back it up per [`docs/backup.md`](backup.md). ## Reverse proxy Run VibeSonic behind a reverse proxy for TLS termination in any real deployment; the examples below forward to VibeSonic on `127.0.0.1:8080`. VibeSonic does not use WebSockets. Streaming and transcoded responses are ordinary long-lived HTTP responses using standard range requests — make sure your proxy doesn't buffer the entire response body before forwarding it (buffering breaks range requests and adds needless latency to playback start). ### Caddy ``` music.example.com { reverse_proxy 127.0.0.1:8080 } ``` Caddy handles TLS automatically and streams request/response bodies without buffering by default — no extra configuration needed for range requests or large responses. ### nginx ```nginx server { listen 443 ssl; server_name music.example.com; ssl_certificate /etc/letsencrypt/live/music.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/music.example.com/privkey.pem; client_max_body_size 0; proxy_buffering off; proxy_request_buffering off; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; } } ``` `proxy_buffering off` matters here: nginx buffers proxied responses to disk/memory by default, which delays the start of playback and range responses. `client_max_body_size 0` avoids nginx rejecting large file uploads if the upload feature is used. ### Traefik ```yaml http: routers: vibesonic: rule: "Host(`music.example.com`)" service: vibesonic tls: certResolver: letsencrypt services: vibesonic: loadBalancer: servers: - url: "http://127.0.0.1:8080" ``` Traefik streams responses without buffering by default, same as Caddy. ## Reverse proxy + external (trusted-proxy) authentication If you're using [`docs/external-auth.md`](external-auth.md) to have the proxy handle SSO (OIDC, SAML, HTTP Basic Auth, ...) for the browser UI, two things are essential: 1. **The proxy must strip any client-supplied identity header before inserting its own.** If it doesn't, a client can set `Remote-User: admin` (or whatever header name you configured) on its own request and have the proxy forward it unchanged, impersonating any user. Every proxy example below includes this. 2. **`/rest/*` must bypass the proxy's SSO layer.** Real Subsonic/ OpenSubsonic clients (mobile apps, desktop players) authenticate directly against VibeSonic's own token/API-key scheme and have no way to complete a browser-based SSO flow (OIDC redirect, SAML POST, etc.). Exclude `/rest/*` from the SSO requirement so those clients keep working, while still letting `/rest/*` reach VibeSonic normally — VibeSonic's own `/rest/*` auth is independent of external auth either way. ### Caddy example (with `forward_auth` for SSO + header stripping) ``` music.example.com { # Strip any client-supplied identity header before forward_auth # potentially sets a real one, so a client can't forge it. header_down -Remote-User @needsAuth { not path /rest/* } forward_auth @needsAuth auth.example.com:9091 { uri /verify copy_headers Remote-User } reverse_proxy 127.0.0.1:8080 } ``` ### nginx example (with `auth_request` for SSO + header stripping) ```nginx server { # ... TLS config as above ... location /rest/ { # Subsonic/OpenSubsonic clients: no SSO, straight through. proxy_pass http://127.0.0.1:8080; proxy_set_header Remote-User ""; # never forward a client-supplied value } location / { auth_request /sso-verify; auth_request_set $remote_user $upstream_http_remote_user; proxy_set_header Remote-User $remote_user; proxy_pass http://127.0.0.1:8080; } location = /sso-verify { internal; proxy_pass http://auth.example.com/verify; proxy_set_header X-Original-URI $request_uri; } } ``` Either way, also set `VIBESONIC_EXTAUTH_TRUSTED_SOURCES` to only the proxy's own address (e.g. `127.0.0.1/32` if it's on the same host) — VibeSonic independently verifies the real TCP peer, so even a misconfigured proxy can't be used to spoof identity from the internet directly. See [`docs/external-auth.md`](external-auth.md) for that check in detail.