Contents

Navidrome and the Case for Owning Your Music Again

A 20 MB Go binary, the Subsonic API, and the tagging problem nobody warns you about

Contents

The album vanished on a Tuesday. A record I had listened to perhaps forty times over three years, in a playlist I had built by hand, replaced in the streaming service’s catalogue by a greyed-out row and a note about licensing. The artist had done nothing. The label had done nothing. Two companies failed to renew a contract and my listening history acquired a hole.

This is the boring, unglamorous argument for self-hosting music, and it is stronger than the audiophile one. Sound quality debates are largely theatre at this point. Permanence is real. The files I ripped in 2004 still play.

Navidrome is the software that turns a directory of those files back into something that behaves like a streaming service. It has been the easiest thing in my rack to install and the hardest to actually finish, and the gap between those two facts is the whole post.

What It Is

Advertisement

A single Go binary, roughly 20 MB, backed by SQLite. It scans a music folder, reads the tags, builds a library, and serves two interfaces: a React web UI that looks like a streaming app, and the Subsonic API.

That API is the reason to choose Navidrome over the alternatives, and it deserves a paragraph. Subsonic was a Java music server from the 2000s whose API became a de facto standard, and a generation of client apps were written against it. Those clients outlived the original server. By implementing the API, Navidrome inherits an entire ecosystem it did not have to write — Symfonium and Substreamer on Android, play:Sub and Amperfy on iOS, Feishin on the desktop, and a dozen more. Offline sync, car integration, gapless playback, all of it maintained by people who are not the Navidrome maintainer.

Compare that with the alternatives. Airsonic-Advanced is the Java lineage itself and carries the memory footprint you would expect. Funkwhale is federated and ambitious and considerably more machine. Jellyfin does music alongside video and does it acceptably rather than well — its music UI has always been the poor relation of its film one, and I say that as someone who runs Jellyfin happily for everything else.

Navidrome does one media type and does it properly. Idle memory on my box is around 60 MB with 30,000 tracks indexed.

Standing It Up

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
services:
  navidrome:
    image: deluan/navidrome:latest
    user: 1000:1000
    restart: unless-stopped
    ports:
      - "4533:4533"
    environment:
      ND_LOGLEVEL: info
      ND_SESSIONTIMEOUT: 24h
      ND_SCANSCHEDULE: '@every 24h'
      ND_BASEURL: ""
      ND_ENABLETRANSCODINGCONFIG: "false"
      ND_MUSICFOLDER: /music
      ND_DATAFOLDER: /data
    volumes:
      - navidrome_data:/data
      - /srv/media/music:/music:ro

volumes:
  navidrome_data:

Three of those deserve comment.

The :ro on the music mount is deliberate and I would argue non-negotiable. Navidrome has no business writing to your library, and a read-only mount means no bug, no misconfiguration and no compromised container can touch twenty years of files. The tagging tools run separately, as a human decision, on a writable path.

ND_ENABLETRANSCODINGCONFIG set to false is the safe default and the docs are blunt about why: turning it on lets an admin user define arbitrary ffmpeg command lines through the web UI, which is remote code execution with a nice form around it. Set the transcoding profiles once with it enabled, then turn it off and leave it off.

ND_SCANSCHEDULE at 24 hours is generous. Navidrome watches the filesystem for changes anyway, so the periodic scan is a safety net for the cases the watcher misses (network shares, mostly). If your library lives on NFS, the watcher will not fire and the schedule is doing all the work — shorten it.

Point it at the same bulk storage everything else uses. Mine sits on the SnapRAID and mergerfs array that holds the rest of the media, which is the correct place for data that is large, mostly-read and reacquirable-but-annoying.

First run creates the admin account through the browser. Then it scans, and then you meet the actual problem.

The Tagging Problem

Advertisement

Here is what nobody tells you. Navidrome is a view of your tags. It has no catalogue, no fingerprinting service, no “we know what this album is” backend. Whatever is embedded in the files is what you see, and twenty years of accumulated files are a catastrophe of inconsistent metadata.

My first scan produced 30,000 tracks and roughly 4,000 “albums”, of which several hundred were the same album fractured into pieces. The causes were all mundane:

  • ALBUMARTIST missing. Compilations exploded into one album per track. This is the big one and it accounts for most of the damage.
  • Multi-disc sets with no DISCNUMBER, so track 1 appeared four times and the sort order was nonsense.
  • The same artist spelled three ways across a decade of ripping — with and without “The”, with an accent and without.
  • Embedded artwork of wildly varying size, including a few 4,000-pixel JPEGs that made the album grid crawl on a phone.
  • Live and remaster suffixes in the album title, giving me six copies of a record I own once.

None of this is Navidrome’s fault and none of it can be fixed in Navidrome. The library is the database and the files are the source of truth, which is the design and also the bill.

The tool for this is beets. It fingerprints audio, matches against MusicBrainz, and rewrites tags to a consistent scheme. Run it against a copy first — it rewrites files, and a bad match applied at scale is a genuinely bad afternoon:

1
2
3
4
5
beet import -t /srv/media/music/incoming    # -t = pretend, print what it would do
beet import /srv/media/music/incoming       # for real, once you trust it
beet ls -a albumartist:"" | head -40        # find albums with no album artist
beet fetchart
beet embedart

That albumartist:"" query is the highest-value ten minutes in this entire post. Fix those and half your fractured albums reassemble on the next scan.

For the artwork, Navidrome prefers embedded art and falls back to cover.jpg in the folder. Standardising on a folder image around 600×600 made my phone’s album grid usable and shaved several hundred megabytes off the library.

Budget a weekend for this. I did it in three sittings over a month and the library got better each time. It is the sort of work that pays a dividend every day afterwards, which is rare.

Troubleshooting

The scan finishes instantly and finds nothing. Permissions. The user: 1000:1000 in the compose file has to be able to read the mount. Check from inside:

1
2
docker compose exec navidrome ls -la /music | head
docker compose logs navidrome | grep -i -E 'scan|permission|error'

A new album never appears. The filesystem watcher does not work over NFS or SMB. Trigger a scan by touching the folder or restart the container, then shorten ND_SCANSCHEDULE and accept it.

Everything is a compilation. Navidrome groups by album artist. See the section above. There is a ND_SCANNER_GROUPALBUMRELEASES behaviour you can experiment with, and the real fix is in the tags.

Clients cannot log in but the web UI works. The Subsonic API uses its own token scheme, and some older clients still send the legacy plaintext password parameter. Navidrome supports both; what breaks people is ND_BASEURL being set for a subpath deployment while the client is configured with the root URL. Serve Navidrome from a subdomain and this whole class of problem disappears.

Transcoding produces silence. The ffmpeg in the image is fine; the profile is wrong. Check the actual command Navidrome ran in the logs at debug level. And ask whether you need transcoding at all — every client I use plays FLAC natively, and the only real case is a phone on a bad connection where you want Opus at 128k.

Playback stutters over the internet. Navidrome is doing its job and your upstream is the constraint. This is where a transcode profile earns its place, or where you accept that music sync — the client downloading the files — is the better answer than streaming.

Getting It Off Your Network Safely

The temptation is to expose it so you have music on your commute. Two sane options.

Put it on a mesh VPN and let the clients dial in. Every Subsonic client I have used works over an overlay network without knowing anything has changed, and the exposure is zero. Either of the options in the mesh VPN comparison does the job in an afternoon. This is what I do.

Or front it with a reverse proxy and real authentication, in which case understand that most Subsonic clients cannot do an interactive login flow, so an SSO portal in front of it will break every app you own. The API needs to stay reachable with its own credentials. Use strong per-user passwords in Navidrome itself, enable HTTPS, and rate-limit the login endpoint.

The middle path — proxy the web UI through your identity provider, keep /rest/ on the VPN — is fiddly, and I have watched several people build it, get it wrong, and expose the API anyway.

What the Library Actually Costs

Some numbers, because the “own your music” argument is usually made without any.

Thirty thousand tracks, mostly FLAC with a long tail of old MP3s, is 1.4 TB on my array. That is unremarkable storage in 2024 and it is also 1.4 TB that has to be backed up, because a ripped library is only reacquirable if you still own the discs and still own a drive to read them, and I fail the second test.

So the real cost is the backup. Music compresses badly — it is already compressed — so the archive is roughly the same size as the library, and that is the line item people forget when they price this against a subscription. My offsite copy is the expensive part of the whole arrangement, and it is the only part that would matter after a fire.

The processing cost is trivial by comparison. Navidrome’s scan of 30,000 tracks takes about four minutes on first run and seconds thereafter, because it tracks modification times and only reads what changed. Idle CPU is indistinguishable from zero. The container is one of the least demanding things I run, and it is sitting on top of the most demanding storage commitment I have.

The last cost is the one that surprised me: attention. A commercial service maintains itself. A local library slowly rots — a rip that was fine in 2009 turns out to have a CRC error you never noticed, a folder gets half-moved during a reorganisation, a tag edit goes wrong. I run a checksum pass over the array on a schedule for exactly this reason, and once a year it finds something.

None of this changes the verdict. It does mean the honest pitch is “own your music, and accept that ownership is a job” rather than the version where you install a container and become free.

Verdict

If you have a music collection on disk and you have ever lost a record to a licensing dispute, install this today. It is one container, one binary, one read-only mount, and it gives you a phone app that behaves like the commercial thing on a library nobody can revoke. On the “self-hosted service that justifies itself” scale it sits alongside Paperless-ngx — a thing you use daily, that replaces a subscription, and that asks for almost no hardware.

If you do not have a collection on disk, be honest about what you are signing up for. The software is ten minutes. Acquiring, ripping and tagging a library that is worth streaming is a project measured in months, and at the end of it you have a service with none of the discovery, none of the new releases, and none of the “here is something you might like” that you are currently paying for. Plenty of people run both, which is the sensible answer and slightly undermines the manifesto.

My honest position after a year: I keep a streaming subscription for finding things and Navidrome for keeping them. The subscription is a shop. The rack is the shelf. That distinction cost me a weekend of beets and it has not lost me an album since.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.