User Tools

Site Tools


computing:navidrome

  • Navidrome Setup
  • oemb1905
  • gnulinux.studio
  • webmaster@gnulinux.studio

navidromesetup


This tutorial is for Debian Trixie users seeking to set up a production-ready Navidrome server. I used virsh+qemu to make a VM with 8TB of storage, and the VM is set up with a LAMP stack, a reverse proxy with Let's Encrypt, automated syncing, scanning, and some hardening measures. Im using fpm with the mpm_event handler for concurrency. My standard fail2ban setup is in place for protection. This instance is designed for public listening. The library is imaged off my master library via a remote source and includes aggressive cover art fetching. Let's setup our lamp stack and add the basic packages required. If you don't know how to setup TLS / LAMP properly, please see my tutorial.

sudo apt update && sudo apt upgrade -y
sudo apt install ffmpeg wget nano curl snapd ufw fail2ban postfix apache2 php8.4-fpm php8.4-mysql php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip

Next, we download and install Navidrome:

wget https://github.com/navidrome/navidrome/releases/download/v0.54.1/navidrome_0.54.1_linux_amd64.deb
sudo apt install ./navidrome_0.54.1_linux_amd64.deb

Let's set up a music directory:

sudo mkdir -p /opt/navidrome/music
sudo chown -R navidrome:navidrome /opt/navidrome/music
sudo find /opt/navidrome/music -type d -exec chmod 755 {} +
sudo find /opt/navidrome/music -type f -exec chmod 644 {} +

Make sure you enable proper modules and conf files:

sudo a2enmod proxy proxy_http ssl headers rewrite proxy_fcgi setenvif
sudo a2enconf php8.4-fpm
sudo a2dismod mpm_prefork php8.4
sudo a2enmod mpm_event

Setup regular Let's Encrypt with the default vhost and and then replace the vhost with the reverse proxy once everything is setup. Let's setup Let's Encrypt and TLS:

sudo a2ensite gnulinux.studio.conf

sudo apache2ctl configtest
sudo apt install certbot letsencrypt python3-certbot-apache
sudo certbot --authenticator standalone --installer apache -d gnulinux.studio --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2"

Once the default configuration is working for both http and https, disable the let's encrypt configuration with a2dissite gnulinux.studio-le.conf and then edit the primary configuration nano /etc/apache2/sites-enabled/gnulinux.studio.conf with the following:

<VirtualHost *:80>
    ServerName gnulinux.studio
    Redirect permanent / https://gnulinux.studio/
</VirtualHost>
<VirtualHost *:443>
    ServerName gnulinux.studio
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/gnulinux.studio/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/gnulinux.studio/privkey.pem
    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass / http://127.0.0.1:4533/
    ProxyPassReverse / http://127.0.0.1:4533/
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost/"
    </FilesMatch>
    <Location />
        Require all granted
    </Location>
    RequestHeader set X-Real-IP %{REMOTE_ADDR}s
    RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Frame-Options "SAMEORIGIN"
</VirtualHost>

Since TLS was already setup under the regular vhost, and the reverse proxy configuration simply uses the same cert, you can just drop this in and restart apache systemctl restart apache2. This is a good time to visit your site's domain and make sure everything is working. If it won't serve navidrome, check syntax, check logs, and make sure modules/configurations in apache are functioning properly and configured with proper syntax. Next, let's tune the php handler, mpm_event, over in nano /etc/apache2/mods-available/mpm_event.conf, assuming 8 cores and an upper limit of 400 workers:

StartServers 4
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0
ServerLimit 16

Once you've adjusted this, restart apache with sudo systemctl restart apache2. Let's also adjust how php spins up servers for simultaneous requests by adjusting nano /etc/php/8.4/fpm/pool.d/www.conf as follows:

pm = dynamic
pm.max_children = 200
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 20
pm.max_requests = 1000
request_terminate_timeout = 300s

Once you enter the adjustments test fpm and restart the service as follows: sudo php-fpm8.4 -t && sudo systemctl restart php8.4-fpm. It's now time to edit the main Navidrome configuration file. Let's open nano /etc/navidrome/navidrome.toml as follows:

DataFolder = "/var/lib/navidrome"
MusicFolder = "/opt/navidrome/music"
Address = "127.0.0.1"
Port = 4533
BaseUrl = "https://gnulinux.studio"
ShareURL = "https://gnulinux.studio"
EnableUserEditing = false
EnableDownloads = false
ImportPlaylists = false
LogLevel = "info"
LogFile = "/var/log/navidrome/navidrome.log"
[Scanner]
Enabled = true
Schedule = "@every 24h"
WatcherWait = "5s"
ScanOnStartup = true
[CoverArt]
Enabled = true
Embed = true
CacheSize = "2GB"
Sources = ["embedded", "folder", "metadata"]
Priority = ["embedded", "folder", "metadata"]

Let's restart the navidrome service and check its status sudo systemctl enable –now navidrome and then systemctl status navidrome. To manually scan your library, do the following:

cd /var/lib/navidrome
sudo -u navidrome navidrome scan -f

In my case, the navidrome instance is for public listening, and my master library is managed through Airsonic Advanced on a different server. I also use Picard from musicbrainz.org to edit my tags, and even RhythmBox comes in handy from time to time. All of these tools, however, edit a common master file source that's synced with Nextcloud and editable by Airsonic Advanced. It's that library, which I use a cronjob for to pull over to Navidrome, ensuring that Navidrome continues to be a live mirror of my Master Library, or Masters. Let's create a script nano /usr/local/bin/sync-music.sh and make it executable sudo chmod 750 /usr/local/bin/sync-music.sh that does all this:

#!/bin/bash
SOURCE="root@mail.outsidebox.club:/mnt/Storage/obdata/oemb1905/files/Masters/*"
DEST="/opt/navidrome/music/"
LOG="/root/logs/rsync-music.log"
touch /tmp/sync-music.lock
echo "$(date): Starting music sync..." >> "$LOG"
sudo rsync -ai --log-file=/root/logs/rsync-music.log --delete --chown=navidrome:navidrome root@mail.outsidebox.club:/mnt/location/of/your/masters/* /opt/navidrome/music/
echo "$(date): Music sync completed." >> "$LOG"
rm /tmp/sync-music.lock

Let's run this via cron every 2 hours:

0 */2 * * * /usr/bin/flock --nonblock /tmp/sync-music.lock /bin/bash /usr/local/bin/sync-music.sh >> /root/logs/rsync-music.log

Fail2Ban needs to be setup. Users should be familiar with how to do that and can refer to my wiki entry if they need help with basic setup. Here, I am covering how to setup a custom Navidrome jail for extra protection. One should additionally enable the apache protections, sshd, dropbear and/or whatever their instance might benefit from. Let's open nano /etc/fail2ban/jail.local and add a jail:

[navidrome]
enabled = true
backend = systemd
filter = navidrome
maxretry = 5
findtime = 600
bantime = 3600

Next, let's create rules for this jail in its configuration file nano /etc/fail2ban/filter.d/navidrome.conf:

[Definition]
failregex = ^.*msg="Unsuccessful login".*(X-Real-Ip|X-Forwarded-For):\s*\[<HOST>\].*$
ignoreregex =
journalmatch = _SYSTEMD_UNIT=navidrome.service + _TRANSPORT=stderr

After everything is built, let's restart fail2ban sudo systemctl restart fail2ban. For upgradind, simply wget the new image and then sudo apt install ./new.deb.

oemb1905 2025/10/29 02:18

computing/navidrome.txt · Last modified: by oemb1905