Feb 2, 2022 Tags: howto, music, workflow
This is a quick how-to post, since I struggled to follow some of the recommendations of individual users on the Navidrome subreddit. Hopefully someone will find it useful.
TL;DR: Jump to this section.
Many years ago, I ran a Subsonic instance for myself
and my friends. Back then, Subsonic was pretty incredible: it “just worked,” assuming you
had a few basic runtime dependencies (namely the Java Runtime and ffmpeg
).
Even back then, however, there were ominous signs on the horizon: Subsonic’s author made new releases proprietary, and community maintained forks churned through names more than they did features1: Libresonic, Airsonic, Airsonic-Advanced?
At the same time, the Foosonic web UI began to show its age: the original Flash-based player
had to be replaced with an HTML5 player, and the <frame>
based interface performed increasingly
poorly on modern browsers.
After a hard drive disaster on the machine that my instance ran on, I gave up on Subsonic and its forks entirely and did my own thing for a while. That also became a hassle, and I became a (recalcitrant) Spotify customer.
Fast forward to 2022, and I’m done with Spotify2: no matter what I do, it only recommends me the same half dozen artists. I miss curating my own music without The Algorithm (un-)helpfully placing its thumb on the scale.
A friend recommends Navidrome to me. He describes it as a Subsonic server, which makes me think that’s another (maintained) Subsonic fork. But no: it’s a completely separate codebase, one that supports the REST API “standard” that Subsonic and its offspring use. That’s the best of both worlds: a new codebase (including a new frontend) with none of Subsonic’s baggage and compatibility with the large ecosystem of Subsonic clients.
Installing it is (blessedly) simple, with just a single Dockerfile and two directories (one for server state, and one for my music):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
version: "3"
services:
navidrome:
image: deluan/navidrome:latest
user: 1000:1000
ports:
- "4533:4533"
restart: unless-stopped
environment:
# This is part of my reverse proxy setup; see below.
ND_BASEURL: /navidrome
# Re-scan the music library every 15 minutes.
ND_SCANSCHEDULE: "@every 15m"
ND_LOGLEVEL: info
ND_SESSIONTIMEOUT: 24h
volumes:
- "/home/william/navidrome/data:/data"
- "/mnt/media/music:/music:ro"
And running:
1
docker-compose up
That’s it. Five seconds later, Navidrome was already indexing my music, listening on my server’s local host, and waiting for me to configure a new user (and admin) via the web UI. The end result is very nice:
This is perfectly sufficient for local use, but I want to replicate the setup I originally had with Subsonic: a reverse-proxied service poking out from one of my domains, complete with HTTPS.
I prefer URL routes to subdomains (because I’m too lazy to update my Let’s Encrypt configuration), so this is my ideal setup:
1
2
https://domain.net/navidrome -> reverse proxy to local-address:4533
http://domain.net/navidrome -> permanent redirect to https://$domain/$path
On the Navidrome side, I only needed a single configuration change: I needed to set
ND_BASEURL: /navidrome
in my docker-compose.yml
, telling Navidrome to base all
URLs on /navidrome
instead of /
. See the full YAML above for the entire context.
On the Nginx side, all I needed was a new location
block under my HTTPS server
spec,
matching all patterns under /navidrome
and passing them onto the underlying server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$server_name$request_uri;
# replace domain.net with your domain
server_name www.domain.net domain.net;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
# replace domain.net with your domain
server_name www.domain.net domain.net;
# the rest of your TLS configuration goes here
location ^~ /navidrome {
# replace local-address with your navidrome server's IP
proxy_pass http://local-address:4533/navidrome;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_buffering off;
}
}
(I’ve included the HTTP server
block as well, just to show the HTTP to HTTPS redirect.)
…and that’s it! Navigating to https://domain.net/navidrome
worked perfectly, including logins.
All of the Subsonic-style clients that I tried also worked, using https://domain.net/navidrome
in the “server” field3.
To be fair, it’s not easy to maintain someone else’s project, much less add new features to it. ↩
Third time’s the charm. ↩
Your mileage may vary. I remember (circa 2015) some of the Subsonic clients being HTTP only or requiring a bare TLD for the server, but hopefully things have improved since then. ↩