This is an old revision of the document!
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.
Initial VM Prep and Navidrome Installation
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
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
Set up music dir:
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 {} +
Apache Reverse Proxy and Let's Encrypt and associated modules:
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
Create vhost nano /etc/apache2/sites-available/gnulinux.studio.conf:
<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>
Enable site and Certbot:
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"
Tune mpm_event 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. Make sure fpm is tweaked by heading to nano /etc/php/8.4/fpm/pool.d/www.conf and adjust servers 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/Storage/obdata/oemb1905/files/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