ZenHoot
Self-hosted · Free

Your music on one machine. Playable from all of them.

If your collection lives on a NAS, a home server, or just the desktop in the other room, ZenHoot Server puts it on your network. Every copy of ZenHoot you own can then browse and play it — over your own wires, with nothing uploaded and no account involved.

Do you actually need this?

No, if your music is on the machine you listen on.

ZenHoot reads local folders on its own. Point it at a folder and you are finished — there is nothing to install and nothing on this page to read.

Yes, if the files live somewhere else.

A NAS, a home server, an old laptop in a cupboard. Run this there once, and your laptop, desktop and phone all see the same library.

1 Start it on the machine with the music

One command. It runs on ordinary PCs and on ARM boxes like a Synology or a Raspberry Pi — the same image covers both.

Docker
docker run -d \
  --name zenhoot \
  -p 9555:9555 \
  -v /path/to/your/music:/music:ro \
  -v ./zenhoot-cache:/cache \
  -e DESTRUCTIVE_LOCALHOST_ONLY=false \
  -e ZENHOOT_API_TOKEN=pick-a-long-random-string \
  znowf0x/zenhoot:latest

Replace /path/to/your/music with your library, and pick your own value for the token. The :ro means the server can read your files but never change them.

Prefer Docker Compose?
docker-compose.yml
services:
  zenhoot:
    image: znowf0x/zenhoot:latest
    container_name: zenhoot
    restart: unless-stopped
    ports:
      - "9555:9555"
    volumes:
      - /path/to/your/music:/music:ro
      - ./zenhoot-cache:/cache
    environment:
      PUID: 1000
      PGID: 1000
      DESTRUCTIVE_LOCALHOST_ONLY: "false"
      ZENHOOT_API_TOKEN: "pick-a-long-random-string"

Save as docker-compose.yml, then run docker compose up -d. Set PUID and PGID to the user that owns your cache folder — id -u and id -g will tell you. NAS users usually need to.

2 Point ZenHoot at it

  1. Open ZenHoot and go to Settings → Library.
  2. Add a source, choose Remote server, and enter http://your-machine:9555.
  3. Paste the same token you chose above.

The first scan takes a while on a large collection — it reads every file once and caches what it learns. After that it only looks at what changed.

One thing worth getting right

The command above sets DESTRUCTIVE_LOCALHOST_ONLY=false, which is what lets you reach the server from another device — the entire point. That switch is also the only thing protecting a server with no token, so once it is off, your token is the lock on the door. Choose a long random one, and do not expose port 9555 to the open internet: keep it on your own network, or reach it through a VPN.

3 Optional: better tagging

Skip this unless you want it. Lyrics, artwork and identification already work, and a large collection is the only reason to change anything here. Both take an environment variable.

ACOUSTID_API_KEY

Faster identification of files whose tags are wrong or missing, on a rate limit of your own rather than a shared one.

Register an application
GENIUS_API_KEY

A second lyrics source behind LRCLIB — mostly older, rarer and non-English tracks. Copy the Client Access Token, not the client ID.

Create an API client

Everything else

Ports, volumes, cache location, scan concurrency and the full list of settings are documented on Docker Hub, alongside the image itself.