Projects/Music service Navidrome
Home services

Music service Navidrome

Self-hosted music streaming lets you listen to your home music library anytime on mobile or web.

I've accumulated a pile of music files at home, scattered across the NAS. Listening on my phone means either copying files over or relying on third-party cloud storage. This project connects them into a self-hosted streaming service—just open a browser or the mobile app and listen, with no dependence on any external platform.

What it is

Run a Navidrome container with Docker, mounting /nas/音乐 on the NAS read-only to /music in the container, with metadata and the database stored in ./data. Only port 4533 is exposed externally, and via ND_BASEURL=/music it's mounted under a subpath of the reverse proxy, sharing a domain with other home services.

Pitfalls I hit

The music directory is a CIFS network share, and the inotify file watcher doesn't receive events, so automatic scanning can't rely on "file change triggers"—it has to be periodic scanning. Here I initially wrote ND_SCANSCHEDULE, and the startup log just came back with Periodic scan is DISABLED—the correct name since 0.6x is ND_SCANNER_SCHEDULE. It's now set to @every 1h, plus a scan at startup, which is enough.

Also, inside the container I use extra_hosts to point api.deezer.com and api.last.fm to 127.0.0.1, deliberately cutting off unreachable external info sources so scans and logs aren't full of timeout errors.

Current status

Running. The library has taken shape, and day-to-day maintenance is handled by a few Python scripts: querying track/album/artist counts in SQLite, triggering rescans to verify tag fixes, checking the cover art API, and cleaning up leftover old files. If I want specific numbers, I just read the database directly—no need to dig through the web UI.

Features

  • Docker 部署 Navidrome,音乐目录以只读方式挂载,原始文件不会被改动
  • 用 `ND_SCANNER_SCHEDULE=@every 1h` 做周期扫描,绕开 CIFS 盘收不到 inotify 事件的问题
  • `extra_hosts` 屏蔽 Deezer / Last.fm 接口,关掉连不上的外部信息源
  • Python 脚本直接读 `navidrome.db`,统计曲目、专辑、艺术家数量并核对封面接口
  • 通过 API 触发重新扫描,用来验证标签修复结果
  • 挂在反向代理子路径下(`ND_BASEURL=/music`),与其它家庭服务共用入口

Stack: Docker · Navidrome · Python · SQLite · CIFS · 反向代理

Deployment Steps

1. Prepare the directory

Create a working directory on the host, for example /opt/navidrome, and put docker-compose.yml in it. Data will be written to the sibling ./data; make sure to leave enough space for it—the database and cover cache are both stored here.

2. Confirm the music directory is readable

The music is stored on the NAS. First mount the share on the host (CIFS here), and confirm that ls /nas/音乐 lists it properly before proceeding. The mount point is mapped into the container as read-only, so the container will not write to your original files.

3. Write the compose file

The core items are just these:

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    restart: unless-stopped
    ports:
      - "4533:4533"
    volumes:
      - /nas/音乐:/music:ro
      - ./data:/data
    extra_hosts:
      - "api.deezer.com:127.0.0.1"
      - "api.last.fm:127.0.0.1"
    environment:
      - ND_BASEURL=/music
      - ND_MUSICFOLDER=/music
      - ND_SCANNER_SCHEDULE=@every 1h
      - ND_SCANNER_SCANONSTARTUP=true
      - ND_SCANNER_WATCHERWAIT=5s
      - ND_SESSIONTIMEOUT=24h
      - ND_LOGLEVEL=info
      - ND_ENABLETRANSCODINGCONFIG=true

4. Start the service and check the logs

Run docker compose up -d, then docker logs -f navidrome. The first startup will create the database and run a full scan; if the library is large, it will take a while. If Periodic scan is DISABLED appears in the logs, the scan schedule did not take effect—most likely because the old ND_SCANSCHEDULE was used; change it to ND_SCANNER_SCHEDULE.

5. Create an admin account

On first startup, the logs will provide an initial admin password. Log in and change the password immediately, then store the new password in a file readable only by root (the script reads this file).

6. Reverse proxy

ND_BASEURL=/music must match the path prefix on the reverse proxy; otherwise, static assets will return 404. Remember to relax large file upload/long connection timeouts on the proxy—streaming uses long-lived connections.

7. Acceptance check

Open data/navidrome.db read-only with sqlite3, query the row counts of the media_file, album, and artist tables, and confirm that the scan actually wrote to the database; then click a few songs to check whether cover art and playback work properly.