20240725 - self-hosting a music streaming server on a raspberry pi with navidrome
|
(\__/) (='.'=) (")_(") |
Thoughts
I am currently writing this guide while listening to the wonderful forgotten 2008 album Evolove by JAMETATONE and the EVOLOVE Vortex from a bench in the back yard of Misfit Kava Bar in Bushwick, NY. At the time of writing this guide, only one other person has rated this album on rateyourmusic.com. It was the person who I downloaded it from on soulseek and they rated it in 2010. They used to make fantastic music under the name tinyfolk and rated this album 5 stars so I trusted their judgement on it and patiently spent over a year sporadically seaching for it on soulseek until finally they logged on and I was able to download it. It is one of the weirdest albums I've ever heard (long prog-y hip hop influenced indietronica songs) and I love it so much. For me, finding obscure and lost indiepop music from the early 2000's is one of the greatest joys of life. Until winter of 2024, I would download this music on my laptop and listen to it at home, work or school. When away from my laptop I would use my phone to listen to music on spotify, or illegally uploaded albums on youtube, and most of my downloaded music would inevitably find its way onto the backburner because it was so much easier for me to open up my spotify app, type in "tiger trap" and listen to the same album I'd listened to on average once a week since I was 13 years old. Exiting the world of spotify, for me, means I can spend more time connecting with all of the deeply personal music that I spend so much of my time hunting down.
If you're here, I probably don't need to tell you how horrible streaming services are, but I will list a few points:
- with subscription based music streaming models you never own music in your collection, and will lose access to this music if the artist stops paying to have their music hosted on the service
- your relationship with music becomes mediated by a tech company. their (evil!) recommendation algorithms influence what you listen to and even if it is fairly minimal in the grand scheme of things, you have forfeited a piece of your autonomy to technocapital
- your relationship with your friends becomes mediated by a tech company. the quasi-social media-ificiation of spotify is so bizarre. you can see what music your friends, lovers, ex-lovers, and random people whose playlists you follow are listening to at all times and you form an odd parallel parasocial relationship with them. there is no messaging built in to spotify so you will never be able to talk to that cool person who made the screamo playlist you listen to. you feel self-conscious when listening to certain music because people might see it or because you are worried that it will show up in your spotify wrapped
- you miss out on listening to all the rad music that isn't on streaming services (local artists, inactive artists, old artists who haven't adapted to the streaming economy, artists from countries where streaming services aren't used much, Jametatone and the Evolove Vortex, etc.)
- streaming services as we know them now are an unsustainable business model. people always give flack to spotify for not paying artists well, accusing them of corporate greed (which surely is the case too) but the reality of it is that spotify as a company, despite having been around since 2006, still loses a lot of money. They reported a net loss of €532 million in 2023, an even greater loss than their loss of €430 million in 2022. in all likelihood streaming services will not continue to exist as they currently do for long
This blog will guide you through the process of setting up a Navidrome server to stream your music collection. Navidrome is a self-hosted streaming server which provides an interface similar to modern streaming services like spotify or apple music. It supports making playlists, saving your favorite albums and once you connect it to a web server you will be able to access it over the internet from anywhere on your phone or computer. Once you get it set up you will not miss spotify one bit!
My main goal in writing this blog post is to document the specific process I used to set up my streaming server as a person with very little prior knowledge of linux, servers, networking, etc. I aim to share what I learned and hopefully help other people escape the horrible world of paid streaming services. By the end of this guide you will have a functional music server as well as a general purpose self-hosted website you can use for anything you want!
Setting up your raspberry pi
The first thing you need is a computer to host our server on. This guide assumes you are using a Raspberry Pi. I am using a Raspberry Pi 3b, but this guide should apply to most other models with no changes. You could also likely run this on any computer running linux with minimal changes, or a computer running Windows or Mac OS with more changes. If you are unfamiliar with Raspberry Pi, they are essentially single board computers that typically run some form of linux. They are great because they are relatively cheap, have easily accessible GPIO pins (which are not actually used in this post) and have a large online ecosystem of support for use in DIY projects.
If you have not set up an operating system on your Raspberry Pi yet, their website has a great guide on doing so! I recommend installing the default Raspberry Pi OS which is a port of Debian. My Pi has the 32-bit OS installed, but a 64-bit version came out recently, and that shouldn't change any of the steps in this guide so I would install it! To follow that guide, you will need an SD card, a keyboard, a mouse, an HDMI monitor and an HDMI cable (Pi 4 and 5 only have micro HDMI ports so if you have one of those you will probably need an adaptor cable). This step shouldn't be too dissimilar to setting up any new computer. There seem to be ways to skip the keyboard, mouse and monitor and move straight to the next step where we access the Pi through SSH but I haven't tested them.
Once the operating system is installed, there are a few things you need to do.
- Connect the PI to the internet.
- Enable SSH on the pi
- When you find the private IP address of your Pi, write it down somewhere, we will be needing it often throughout this guide. An IP address is a unique identifier assigned to every device connected to a network. In this case, what we are finding is the IP address assigned to your Pi by your router, which will allow it to be identified by any other devices also connected to the same network, but NOT to outside devices on the world wide web. This local IP address will likely look like 192.168.1.SOMENUMBER. It is entirely possible that your Pi has the same private IP address as mine but since they are connected to different routers it is like they are in separate universes so they don't conflict with each other.
- Replace "admin" with the name of the admin user you created when setting up the Pi. It probably defaulted to either "admin" or "pi" if you didn't change it to anything else.
- Replace the IP address after the "@" with your Pi's IP address found in the last step
- Set up a static IP address for your Pi.
NOTE: Later on in this guide, you will need admin access to your router. If you have a roommate or partner who set up your internet you will need to get the password from them. If they forgot it, the default login, or reset instructions might printed on the label on the back of your router. If you live in an apartment building and do not manage your own internet, you may be out of luck. Jack hints in his guide that there are ways to get by without admin access but they are beyond my knowledge level.
If at all possible you should connect your Pi to your router directly with an ethernet cable, my Pi lives right next to my router and I have never had any reason to move it. Wifi would also work but streaming your music would be slower. You may need to disable Wifi on your Pi, but if an ethernet cable is plugged in, it should default to the wired connection. Load any webpage in your Pi's internet browser to verify that it is connected to the internet.
SSH stands for Secure Shell Protocol. Setting it up will allow you to remotely control your Pi from your regular computer so long as it is connected to the same network as the Pi. For the rest of the guide you will be interacting with the Pi purely through this remote command line interface, rather than a desktop, which will allow you to ditch the keyboard, mouse and monitor.
Raspberry Pi, again, has a guide for setting up SSH which I recommend you follow, but I will leave an important point here.
Once you have enabled SSH on the Pi, you should be able to connect to it using your main computer, which we will be using for the rest of this guide. Open up your favorite command-line interface (I use the default "Terminal" app on my macbook) and run the following command:
ssh admin@192.168.1.148
The Pi will then prompt you to enter your password you created when setting up the Pi. Type it in (typed text probably won't appear in the terminal when entering a password but it is still reading your keypresses) and press enter and you should be in! Now any commands you type will be run on the Pi rather than your local machine! If you are unfamiliar with using command-line interfaces that is okay, I will do my best to explain any commands we use. Your Pi's default command line interface is probably bash, which is a UNIX-like shell and has the same commands used on Mac OS and Linux for the most part, so any documentation you find for those systems will likely apply to your Pi as well.
A tricky thing about IP addresses is that they can change! It doesn't happen often, but our router could decide one day to assign a different address to our Pi which could cause problems for us. Luckily we are able to assign a static IP address to our Pi, and while it's not strictly necessary, it will make our lives easier in the long run. There are plenty of guides online covering this, but I followed this oneand it worked great!
Get a domain name and point it to your Pi
The next two steps of this post will guide you through self-hosting a website on your Pi. If you want your music server to be able to access your navidrome server outside of your home network you need a website to attach it to!
Domain names are essentially a human friendly way of pointing to an IP address. Rather than typing in a bunch of numbers when accessing a website, we type in the domain name, and a DNS (Domain Name Server) points us to the IP address of the server running the site we want to access, which then gives our browser whatever content that website serves. You are going to be setting up this process for your website. Remember, at the end of this guide you will have both a functional music server and a self-hosted website, so if you have any interest in using your pi for hosting a personal website, project website or something else, you will want to choose whatever domain name you would use for that purpose as well!
- Register a domain name
- Enable Port forwarding from your router to your Pi This is how you point http and https requests coming into your router to your Pi.
- Find your Router's private IP address This process differs depending on your operating system, so if you have trouble, google it. You should be able to find your router's IP address in the network settings on your computer. On Mac OS, you go to system settings, select Network, select Wi-Fi and press "details" next to the network you are connected to. There should be a line labelled Router with an IP Address. Mine is 192.168.1.1, yours will likely look similar.
- Log into your router Type your Router's private IP address into your web browser and press enter. Every router/ISP(Internet Service Provider) has a different interface but some sort of page should appear prompting you to log in. The username and password were set by whoever set up your internet. If you can't find this info, the default is often printed on the label on your router (it may be admin:password) and there is always a method to reset it, which is specific to your router. You can likely find this method printed on the router itself or in the router's manual.
- Enable port forwarding After logging in, look for a section called "Port Forwarding", it may be under an Advanced Settings menu. Add two new Port Forwarding rules to send ports 80 (the default port for sending http) and 443 (the default port for sending https) to your Pi's IP address. This interface will look different depending on your router/ISP but one of these rules from my router is shown below.
- Pointing your domain name to your router
- Creating "A" records in your DNS records
- Running a dynamic DNS script on your Pi
There are many sites that will let you register domain names and they all work similarly, my server is hosted using a name I registered using GoDaddy (which I don't recommend because they recently restricted their API access. namecheap or cloudflare are probably good!). Any domain name will cost money (mine is $11/year) and be aware that different domain names and extensions cost different amounts. Many domain name registration services also offer hosting services (and will probably try to get you to use them) but you don't need that since you will be hosting the website yourself so be sure not to sign up for that!
Now that the router is pointing to the Pi, the next link in the chain is pointing our domain name to the router. Similar to how the router might assign the Pi a different IP address, our ISP may assign a different IP address to our router. Unfortunately we are not able to assign a static IP address to the router so instead we will configure the domain name to respond to dynamic IP addresses and run a script on the Pi to periodically check the router's IP address and send that to our domain name to keep its records up to date (ie. pointing at our router). Your site will still work if you set this IP address manually, it just won't update itself.
DNS records tell a DNS (Domain Name Server) information about a domain, including what IP address is associated with that domain. We want to tell the DNS to associate the domain name you registered with your router's IP address. There are many DNS records that give different information about a domain name, but the one we care about is the "A" record which provides the IPv4 address you want to associate with your domain.
First we need to go to our domain name provider's website (in my case GoDaddy) and find the page for managing your domain's DNS. On this page there should be some DNS records that were created for us. If they don't already exist, we want to create two "A" records. One will have the name (or host) "@" and the other will have the name "www". Enter your router's public IP address into the data (or value) field for both of them. The "www" record will allow us to access our site through the url "www.yourdomain.com" and the "@" record will allow you to access it through "yourdomain.com". It's nice to have both! TTL specifies how often DNS servers will update their records. Mine are set to update every 10 minutes. My records look like this:
As mentioned above, there is a chance that our ISP could assign a different IP address to our router and we want our Pi to update these DNS records automatically so we don't have to manually set them again.
There are a few ways to do this. Jack's guide recommends using ddclient which seems like a convenient way to handle it. I recommend setting it up by taking a look at that section of Jack's guide. Your router may even provide options for configuring dynamic DNS which would allow you to handle dynamic DNS without even using the Pi! I realized after getting my script set up that my router from Optimum does provide seem to provide dynamic DNS management.
I was unable to get ddclient working with GoDaddy, but it should work for most cases. Previously, I had set up a bash script as a cron job to periodically update my DNS records but GoDaddy recently restricted API access to only accounts that own more than 10 domains, which effectively blocked my script. I have yet to find a workaround and will likely by migrating my domain away from GoDaddy soon! Don't use them!
Setting up nginx
nginx is a free and open source web server that is currently used to host approximately a third of all websites connected to the world wide web. Web servers are able to accept HTTP/HTTPS requests and return data to the user, often in the form of html files which your browser will display. nginx can also function as a reverse proxy, forwarding these requests to a specific server on our host computer. This will come in handy later in this post!
,NOTICE' In the following code, replace "yourdomain" with the domain you registered in the previous step. For example, if you registered "mycoolwebsite.com", type "mycoolwebsite" (without the .com) wherever you see "yourdomain". Anywhere you do see a ".com" you should change to whatever domain extension you registered, such as ".party", ".gay", or ".motorcycles"
- Install nginx
You will often see the word
sudo
placed before unix commands. It is essentially a keyword that runs the command as the computer's superuser rather than your current user, which will give you permission to do just about anything, such as install packages to the computer or access restricted files.apt-get
is a command-line tool that allows you to install and update software. First we update the apt-get tool and then we use it to install nginx.sudo apt-get update sudo apt-get install nginx
- Configure your nginx server
Now we need to create a configuration file for our nginx server. nano is a command-line native text editor. This command will create an empty text file called "yourdomain" and open it in nano editor so we can edit it.
sudo nano /etc/nginx/sites-available/yourdomain
In nano editor, enter the following code
server { listen 80; listen [::]:80; server_name yourdomain.com, www.yourdomain.com; root /var/www/yourdomain; index index.html; location / { try_files $uri $uri/ =404; } }
server { listen 80; server_name www.yourdomain.com; return 301 http://yourdomain.com$request_uri; }
Once you have typed the above lines, you can save the file and exit nano by typing
CTRL
+X
followed byY
and then theENTER
key - Enable this web server by adding it to nginx's enable list
sudo ln -s /etc/nginx/sites-available/yourdomain /etc/nginx/sites-enabled/yourdomain
- Remove nginx's default site, we are going to create our own
sudo rm -rf /etc/nginx/sites-enabled/default
- Reload nginx to update it with the settings you changed
sudo nginx -s reload
- Create a directory to host your websites files
sudo mkdir /var/www/yourdomain
- Create a basic html file
sudo nano /var/www/yourdomain/index.html
Enter the following then save and exit nano. This will create a basic webpage that displays "Hello world!"
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>hiiii :3</title> </head> <body> <p>Hello world!</p> </body> </html>
Enabling https
Now that you have your website working, you need to set up https support. https is standard and is really important for privacy/security reasons that I don't entirely understand, but the good news is that this is probably the easiest step in this whole guide.
We will be using EEF's certbot which will automatically configure our nginx server for us and create TLS certs. The following is a paraphrasing of the important parts of their setup guide.
- Install snapd
then rebootsudo apt update sudo apt install snapd
then install core from snapdsudo reboot
sudo snap install core
- Install certbot
sudo snap install --classic certbot
- Prepare certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
- Run certbot: this will get your certs and modify your nginx config file
sudo certbot certonly --nginx
- Test automatic renewal
sudo certbot renew --dry-run
- Confirm that certbot worked Visit https://www.yourwebsite.com in your browser and look for the lock icon in the URL bar.
Setting up a local navidrome server
Navidrome is a piece of software that lets you stream your personal music collection. There are several ways to set it up, but we are going to do it using Docker. Docker is kind of like a tiny virtual machine. It sets up a container inside of your operating system for the purpose of running a specific application, and helps to nicely package all dependencies, making it easy for us to set up the application!
In this step we will set up navidrome to run on our local network and in the next step we will connect it to the external internet.
First you need to create a docker compose file. This is the configuration file that will tell your computer how to set up your navidrome server.
- Create a directory for our navidrome setup
Create a directory for navidrome data and make sure your current user has access to this directorycd /opt/stacks sudo mkdir navidrome cd navidrome
sudo mkdir data sudo chown $USER: /opt/stacks/navidrome/data
- Create a docker compose file.
sudo nano compose.yaml
version: "3" services: navidrome: image: deluan/navidrome:latest user: 1000:1000 ports: - "4533:4533" restart: unless-stopped environment: ND_SCANSCHEDULE: "@every 5m" ND_LOGLEVEL: info ND_SESSIONTIMEOUT: 24h ND_BASEURL: "/navidrome" volumes: - "./data:/data" - "<PATHTOMUSIC>:/music:ro"
Important parts of this code:
- image: deluan/navidrome This points to docker hub which hosts docker images created by the navidrome developers. It will automatically fetch the image created for your processor (my raspberry pi has an arm v7 processor, yours could be different if you have a different pi version!)
- ports: - "4533:4533" This specifies that our server will be hosted on port 4533 of our local network. A port, in the world of networking, is the next step of specificity after the IP address. When a request comes to your IP address, it probably wants to connect to a specific process within the device at this IP address. These processes are specified by their port number. Some port numbers are reserved for certain protocols (http is on port 80, ssh is on port 22). In our case, Navidrome will be running on port 4533, so if a user wants to access navidrome they will need both the IP address of the Pi and the port number of our server (don't worry, we will simplify this into a domain name in the next step!)
- environment: There are a ton of options for customizing your navidrome installation. These are not necessary for the server to work but feel free to look through and add any that you want! I have my ND_SCANSCHEDULE (how often your navidrome server will scan for new files) set to five minutes because I upload new music to my server very frequently and I'm too impatient to wait for it to show up after I upload it but if you're not uploading very often or have patience you should probably set this to a longer interval.
- volumes replace <PATHTOMUSIC> with the path to where your music is stored (mine is on an external SSD at /media/admin/mason_ssd/Music).
- Start your navidrome server
docker compose up -d
This will start your navidrome server! The
-d
command option will run the server in detached mode, meaning that it won't be tied to this specific shell instance. You can run it without the-d
option if you want to see a detailed real-time log from the server, but the server will shut down if you close your shell. - Test your local navidrome server!
The server should now be running and accessible on your local network. You can access it through your web browser by navigating to port 4533 of your raspberry pi's local IP address. For me that looks like this:
http://192.168.1.148:4533
but your pi's IP address will likely be different.
Connecting navidrome to your nginx server
Your server should now be working great for streaming music at home, but wouldn't it be great to be able to access it
from anywhere? That is where the nginx server we set up earlier comes in! Our goal is to create a page on our website
which will be served by our navidrome server rather than a static html file like our homepage was. We do this by setting up
a reverse proxy. When a user requests the relevant page on our website: www.yourwebsite.com/navidrome"
for example,
this reverse proxy will handle redirecting them to the navidrome server which we have set up on port 4533, effectively creating
a bridge between this local server and the whole of the internet.
To create this bridge, we need to do the following:
-
open up the nginx config file
sudo nano /etc/nginx/sites-available/yourwebsite
-
Add this code to the server block of your nginx configuration
location /navidrome{ proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_pass http://127.0.0.1:4533; }
-
restart your webserver
sudo nginx -s reload
You should now be able to access your server from any internet connection by navigating to your url:
www.yourwebsite.com/navidrome
Using your server
Now that you've set up your server comes the fun part, loading up music and listening to it!
- Getting digital audio files
If you have never collected digital audio before here are some ways you can get started:
- Buy CDs from your local record store/thrift store or discogs.com and rip them
- Download directly from artists on bandcamp
- Learn to play ukulele and record acoustic covers of all your favorite songs using your laptop microphone and audacity. Organize these files and upload them to your server.
- Download files through soulseek or a private torrent tracker
- Download files from youtube. Almost every album uploaded to major streaming services is also uploaded
to youtube as individual songs in a playlist under an account called "ArtistName - topic". Once you find the playlist
of the album you want, you can use the command line tool
youtube-dlp to
download all songs in the playlist as separate mp3 files. The command I use is:
I then manually set the files' metadata using mp3tag (there's probably a FOSS program for setting this metadata but I haven't found one yet).yt-dlp -x --audio-format mp3 "URL_OF_ONE_OF_THE_VIDEOS_IN_THE_PLAYLIST"
- Look through old blogs and artists/labels websites for direct downloads of albums
- Look on archive.org, many old sites with albums are archived but lots of albums are directly archived on there as well!
- Transferring those files to your Pi
- Streaming your files
- Share with your friends!
There are lots of ways you can transfer files to your Pi but the way I do it is using SFTP. I use the FTP client Cyberduck and connect to my Pi (when on my local network) using its private IP address and my admin username and password (the same ones used when SSHing). I can then transfer files directly from my laptop to the Navidrome music directory on the external hard drive mounted to my Pi. You can also set up port forwarding on your router for port 22 (the default port for SSH, which SFTP uses) and connect to your Pi from anywhere by connecting through your router's public IP address. Once the files are transferred it may take a few minutes for them to show up in your navidrome server depending on what you set ND_SCANSCHEDULE to in your navidrome docker compose file.
You can use the web interface to stream your files just fine and I do that a lot, but you can also stream your navidrome server from any app that supports Subsonic! There are plenty of desktop and mobile apps to choose from listed at the bottom of the Navidrome overview page and on the Subsonic website. There are even more that aren't listed there if you search "subsonic" in your device's app store. I use Tempo on my Android phone and currently just use the web interface on my laptop though I did use Submariner for a bit!
Send your navidrome URL to your friends and they can listen to all the cool music you host! You can even give them personal accounts in navidrome so they can login, make their own playlists, have their own recently played section, etc. Maybe you will inspire them to set up their own server. Or if you trust them a lot, give them the SFTP password to your pi and let them upload albums and create the ultimate shared streaming server for your friend group.
Addendums
convenient commands to have on hand
- editing navidrome configuration
sudo nano /opt/stacks/navidrome/compose.yaml
- restarting navidrome server (should be done after editing navidrome configuration)
cd /opt/stacks/navidrome docker compose down docker compose up -d
- editing nginx configuration
sudo nano /etc/nginx/sites-available/yourdomain
- restarting nginx server (should be done after editing nginx configuration)
sudo nginx -s reload
additional notes
Using an external SSD as your Navidrome music folder like I do can have its issues. Whenever I restart my Pi, it will mounted my drive with an extra number at the end (my drive is named mason_ssd, and when i restart the pi it will mount as mason_ssd1 or sometimes another number). This breaks the path to the music folder set in navidrome configuration. For now I just manually edit the configuration file whenever I need to restart my Pi (which isn't very often). The correct solution would be to not rely on the Pi's automatic disk mounting utility, and set up a script to manually mount it, but I haven't gone down that rabbit hole just yet!