Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Docker

Run the self-hosted web server in a container instead of installing it with scripts/install.sh. This skips the systemd / Caddy / Avahi setup and just runs the server binary, so you bring your own networking (port mapping or a reverse proxy).

Two image flavors are built from the same docker/Dockerfile:

  • CPUdebian:bookworm-slim base. Works everywhere.
  • CUDA / GPUnvidia/cuda base. Needs the NVIDIA Container Toolkit on the host for faster analysis.

The heavy ML stack (ffmpeg, uv, Python 3.10, PyTorch, WhisperX, Demucs, UVR models, …) is not baked into the image. It downloads on first launch into the mounted data volume, exactly like a bare-metal install.

Prebuilt images

Each release publishes images to Docker Hub and GHCR, so you don’t have to build locally:

FlavorDocker HubGHCR
CPUrazzaru/nightingale:latestghcr.io/rzru/nightingale:latest
CUDA / GPUrazzaru/nightingale:latest-cudaghcr.io/rzru/nightingale:latest-cuda

Versioned tags (:0.9.0, :0.9.0-cuda, …) are published alongside latest.

docker pull razzaru/nightingale:latest

Quick start (Docker Compose)

From a checkout of the repo:

# edit docker/compose.yaml first: point the /songs bind mount at your music
docker compose -f docker/compose.yaml up -d

Then open http://<host>:8080 and follow the setup wizard.

Quick start (docker run)

docker build -f docker/Dockerfile -t nightingale .

docker run -d \
  --name nightingale \
  -p 8080:8080 \
  -v nightingale-data:/data \
  -v /path/to/your/music:/songs:ro \
  nightingale

First launch

  1. Open http://<host>:8080.
  2. Continue through the setup wizard. The data folder is fixed to the /data volume (the container sets NIGHTINGALE_DATA_PATH=/data), so the wizard skips the data-folder step entirely.
  3. Wait while Nightingale downloads ffmpeg, Python, PyTorch, WhisperX, Demucs, and the UVR models into /data/vendor. This is several GB and only happens once — as long as you keep the volume.
  4. Your library is already configured: the image pins it to /songs, so the folder you mounted is scanned on startup. Add or change songs on the host, then hit Rescan in the sidebar.

Configuring the music library

A browser file picker can’t hand the server an absolute filesystem path, so instead of picking a folder in the UI the library is set by convention: the image pins it to /songs (via a default NIGHTINGALE_LIBRARY_PATH=/songs). Just mount your host music there:

-v /path/to/your/music:/songs:ro

Nightingale configures a folder library source at that path on every startup and rescans if it changed, so your run/compose file is the single source of truth for the library. While it’s pinned, the in-app source buttons (folder / Jellyfin / Navidrome) are hidden.

To mount your music somewhere else, override the path (-e NIGHTINGALE_LIBRARY_PATH=/music -v /path/to/music:/music:ro). To use a remote source (Jellyfin or Navidrome) instead, set it empty (-e NIGHTINGALE_LIBRARY_PATH=) and connect from the sidebar as usual.

GPU

Install the NVIDIA Container Toolkit on the host, then build the CUDA image and run it with GPU access.

docker build -f docker/Dockerfile \
  --build-arg RUNTIME_BASE=nvidia/cuda:12.6.2-runtime-ubuntu24.04 \
  -t nightingale:cuda .

docker run -d \
  --name nightingale-gpu \
  --gpus all \
  -p 8080:8080 \
  -v nightingale-data:/data \
  -v /path/to/your/music:/songs:ro \
  nightingale:cuda

Or with Compose:

docker compose -f docker/compose.yaml --profile gpu up -d nightingale-gpu

Nightingale detects the GPU by running nvidia-smi inside the container (injected by the Container Toolkit) and picks a matching PyTorch CUDA wheel at bootstrap. The PyTorch wheels bundle their own CUDA runtime, so the only host requirement is an NVIDIA driver new enough for the selected CUDA version (12.6 by default).

If nvidia-smi isn’t available in the container, Nightingale falls back to CPU-only PyTorch — analysis still works, just slower.

Persistence

Everything lives under the single /data volume: config.json, songs.db, cache, videos, models, and the vendor/ toolchain. $HOME is also set to /data so uv / pip / Hugging Face caches persist there too.

Keep the nightingale-data volume across upgrades. Deleting it forces the multi-GB dependency download to run again.

Microphone and HTTPS

Browsers only allow microphone capture in a secure context. http://<host>:8080 is not secure, so mic scoring is disabled there — but browsing, playback, queues, and analysis all work fine over plain HTTP.

To enable mic scoring, terminate TLS in front of the container. Two common options:

  • Put a reverse proxy (Caddy, Traefik, nginx) in front and serve HTTPS with a trusted certificate.
  • Run with --network host and front the container with a host-level Caddy, mirroring the bare-metal self-hosted setup.

Updating

Rebuild (or pull) the image and recreate the container:

docker compose -f docker/compose.yaml up -d --build

Your config, library, cache, and downloaded dependencies stay on the /data volume. There is no in-app updater in container mode — you update by rebuilding or pulling a new image.

Music folder permissions

The container runs as a non-root nightingale user. Mount your music read-only (:ro) and make sure it’s world-readable, or adjust ownership so the container user can read it.

Environment variables

VariableDefaultUse
NIGHTINGALE_BIND0.0.0.0:8080Address the server listens on inside the container.
NIGHTINGALE_DATA_PATH/dataData + config directory (mount this as a volume). Fixed, so the setup wizard skips the data-folder step.
NIGHTINGALE_LIBRARY_PATH/songsPin the library to a folder at this in-container path (mount your music here). Scanned on startup; the in-app source pickers are hidden while it’s set. Set empty to use in-app / remote sources instead.

Build-time:

Build argDefaultUse
RUNTIME_BASEdebian:bookworm-slimSet to an nvidia/cuda:*-runtime-* image for GPU support.
PIXABAY_API_KEYemptyBaked into the binary to enable Pixabay video backgrounds.