Skip to content

Installation

Nessie runs on a Linux machine next to your music: a Raspberry Pi is the ideal cheap one. The easiest way to run it is Docker Compose, which starts Nessie and its PostgreSQL database together.

Docker Compose

You need Docker with the Compose plugin, and git.

git clone https://github.com/jonp92/Nessie.git
cd Nessie/deploy/example
cp .env.example .env      # then set MUSIC_DIR and POSTGRES_PASSWORD
docker compose up -d --build

Open http://<server-address>:8000. With no libraries yet, the app opens Settings → Libraries: add /music (or a folder inside it) and the scan starts.

The compose file, deploy/example/compose.yaml:

# Nessie with its own Postgres: a starting point for your own server. See docs/install.md
# (https://getnessie.app/install/). Copy .env.example to .env next to this file and fill it in, then:
#
#   docker compose up -d --build
#
# Upgrading: git pull, then the same command. The database is migrated when the container starts.

name: nessie

services:
  nessie:
    build: ../..
    image: nessie:local
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "${NESSIE_PORT:-8000}:8000"
    env_file: .env
    environment:
      DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-nessie}:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-nessie}
      # The folders Settings → Libraries can add, inside the container
      LIBRARY_BROWSE_ROOTS: '["/music"]'
    volumes:
      - data:/data
      - ${MUSIC_DIR:?set MUSIC_DIR to your music folder}:/music:ro
      # Playing on the server's speakers: uncomment these, and set MPV_AUDIO_DEVICE in .env
      # (docker compose run --rm nessie mpv --audio-device=help lists them)
      # - /dev/snd:/dev/snd
    # device_cgroup_rules:
    #   - "c 116:* rmw"  # ALSA's devices
    # group_add:
    #   - "${AUDIO_GID:-29}"  # the host's audio group: getent group audio
    extra_hosts:
      # Lidarr and the like, on this machine: LIDARR_URL=http://host.docker.internal:8686
      - "host.docker.internal:host-gateway"

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-nessie}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB:-nessie}
    volumes:
      - db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  data:
  db:

and its .env, where every setting from Configuration can go too:

# Nessie: copy to .env next to compose.yaml and fill in. Every setting in docs/configuration.md
# can go here too.

# Your music, on this machine (mounted read-only at /music)
MUSIC_DIR=/srv/music
NESSIE_PORT=8000

POSTGRES_PASSWORD=change-me

# The server's name in the app's output menu
PLAYER_NAME=Living Room
# MPV_AUDIO_DEVICE=alsa/hw:CARD=Device,DEV=0

# Sent to MusicBrainz with artwork lookups, as it asks: an email address or URL
MUSICBRAINZ_CONTACT=[email protected]

# Let Settings → Server change the settings it lists (saved in the data volume)
ALLOW_REMOTE_SETTINGS=true
  • The music is mounted read-only at /music. More than one folder? Mount each one (say /music/rock, /music/jazz) and list them in LIBRARY_BROWSE_ROOTS.
  • Settings, artwork and volume are kept in the data volume, the library in the db volume.
  • Playing on the server's speakers needs the sound card in the container: uncomment the /dev/snd lines, and pick the device with MPV_AUDIO_DEVICE (docker compose run --rm nessie mpv --audio-device=help lists them). Without it, music plays in the browser and the apps.

Upgrading: git pull, then docker compose up -d --build again. The database is brought up to date when the container starts. Back it up first with docker compose exec db pg_dump -Fc -U nessie nessie > nessie.dump.

Without Docker

Requirements

  • Python 3.12 or newer (developed on 3.14)
  • PostgreSQL (Docker is fine for just the database)
  • mpv for playback on the server
  • ffmpeg: optional, only used by some tests
  • shairport-sync: optional, for AirPlay. It needs metadata support and the pipe turned on in its config: metadata = { enabled = "yes"; include_cover_art = "yes"; pipe_name = "/tmp/shairport-sync-metadata"; };

Setup

cp .env.example .env              # then set POSTGRES_PASSWORD (and DATABASE_URL to match)
docker compose up -d              # PostgreSQL on :5432, Adminer on :8080

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

alembic upgrade head              # create or update the database tables
uvicorn app.main:app --host 0.0.0.0 --port 8000 --timeout-graceful-shutdown 3

--timeout-graceful-shutdown 3 because the app keeps its live updates (/v1/events) open: without it, stopping or reloading the server waits for every open page. Stopping with one open logs Cancel 1 running task(s), timeout graceful shutdown exceeded, which is expected; the page reconnects by itself. Behind a proxy, live updates need the response passed on as it comes: Caddy does that for text/event-stream already, and Nessie sends X-Accel-Buffering: no for nginx.

Libraries can also be added from the command line:

python -m app.cli scan /media/usb/Music --name "USB Music"

Run alembic upgrade head again after pulling changes: new versions may add database migrations.

The classic app

The app before this one is still at http://<server-address>:8000/v2/ (linked as Classic view in the sidebar). Both use the same API and data.

HTTPS

Over plain http://<lan-address> everything works except the service worker, so there is no offline music on phones. Browsers won't register one on a page with a certificate error even after the warning is accepted, so the certificate has to be trusted: for example, a local certificate authority installed on each device, Caddy with tls internal, or a Tailscale HTTPS certificate.

To reach Nessie away from home, and to let each person sign in, see Profiles and sign-in.