← Self-Hosting · Media · Replaces: Spotify, Apple Music
What You'll Build
Spotify rents you music: stop paying and your library disappears. This project flips that around. You'll turn a Raspberry Pi into a private music-streaming server that plays your own music files to your phone, laptop, and car — anywhere in the world — with no subscription and no ads.
We'll use Navidrome, a free, lightweight music server that runs happily on a Pi. It gives you:
- A clean web player (looks and feels like a streaming service)
- Albums, artists, playlists, favourites, "recently played", and smart playlists
- Support for dozens of phone apps with offline downloads (via the Subsonic API)
- On-the-fly compression so streaming over mobile data doesn't eat your plan
- Multiple user accounts for family members
How it fits together:
Music files on USB drive
│
▼
┌─────────────────────────┐
│ Raspberry Pi │
│ └─ Docker │
│ └─ Navidrome :4533 │
└─────────────────────────┘
│ (home Wi-Fi, or Tailscale when you're away)
▼
Phone app · Web browser · Desktop player
Time needed: about 1–2 hours, plus however long it takes to copy your music. Difficulty: Medium — you'll use the terminal, but every command is given.
Why Navidrome? (vs Jellyfin and Plex)
| Navidrome | Jellyfin | Plex | |
|---|---|---|---|
| Focus | Music only | Movies, TV, music | Movies, TV, music |
| Cost | Free, open source | Free, open source | Free tier; remote streaming and some features need Plex Pass |
| RAM on a Pi | Very low (tens of MB) | Higher | Higher |
| Phone apps | Many (any Subsonic app) | Official + third-party | Plexamp (polished) |
| Needs an online account | No | No | Yes (Plex login) |
If you only want music, Navidrome is the lightest and simplest choice. If you also want movies, Jellyfin is the better fit.
What You Need
- Raspberry Pi 4 (2 GB+) or Raspberry Pi 5. A Pi 3B+ works for one or two listeners, but it's slow at scanning big libraries.
- Official power supply for your model. Cheap chargers cause random crashes and corrupted SD cards.
- microSD card, 32 GB or larger (A1/A2-rated), for the operating system.
- USB SSD or hard drive for your music (a 1 TB drive holds roughly 25,000 FLAC songs or 200,000 MP3s). If you use a spinning hard drive, give it its own power (powered USB hub or a drive with its own adapter).
- Ethernet cable (recommended). Wi-Fi works, but wired is faster and more reliable for a server.
- Your music files — MP3, FLAC, AAC/M4A, OGG, and Opus are all supported.
- A computer to set things up from. Basic terminal comfort helps: see Your First Commands.
The Build
Follow the steps in order. Every command is run on the Pi over SSH unless it says otherwise.
Step 1: Flash Raspberry Pi OS Lite
"Lite" means no desktop — perfect for a server, since it saves memory for Navidrome.
- On your computer, download and open Raspberry Pi Imager.
- Choose Device: your Pi model.
- Choose OS: Raspberry Pi OS (other) → Raspberry Pi OS Lite (64-bit).
- Choose Storage: your microSD card.
- When asked to apply OS customisation, choose Edit Settings and set:
- Hostname:
musicpi - A username and a strong password (we'll assume the username
pibelow — use yours) - Wi-Fi details (only if you can't use Ethernet)
- Your time zone
- Services tab: Enable SSH with password authentication
- Hostname:
- Write the card, put it in the Pi, plug in Ethernet and the music drive, then power on. Give it 2 minutes for the first boot.
Step 2: Connect Over SSH and Update
From your computer's terminal (Windows: PowerShell):
# Connect to the Pi (replace "pi" with the username you chose)
ssh pi@musicpi.local
# Once logged in, bring everything up to date
sudo apt update && sudo apt full-upgrade -y
# Reboot to load any new kernel, then reconnect after ~1 minute
sudo reboot
"Could not resolve hostname musicpi.local"? Log in to your router and find the Pi's IP address in the connected-devices list, then use ssh pi@192.168.x.x instead.
Step 3: Set Up the Music Drive
We want the USB drive to appear at the same folder, /mnt/music, every time the Pi boots.
3a. Find the drive
lsblk -f
# Look for your USB drive — usually "sda" with a partition "sda1".
# The FSTYPE column shows the format (ext4, exfat, ntfs …).
3b. (Optional) Format it as ext4
This erases the drive. Skip this if your music is already on it. exFAT and NTFS drives work fine as-is; ext4 is simply the most reliable choice for a Linux server.
# Double-check the name with lsblk first! Formatting the wrong disk destroys it.
sudo mkfs.ext4 -L music /dev/sda1
3c. Mount it permanently
# Create the folder the drive will appear in
sudo mkdir -p /mnt/music
# Get the drive's unique ID (UUID) — copy the long UUID="…" value for sda1
sudo blkid /dev/sda1
# Open the file that lists drives to mount at boot
sudo nano /etc/fstab
Add this line at the bottom (paste your UUID and change ext4 to exfat or ntfs3 if you didn't format):
UUID=your-uuid-here /mnt/music ext4 defaults,nofail 0 2
nofail means the Pi still boots if the drive is unplugged. Save with Ctrl+O, Enter, then exit with Ctrl+X.
# Reload the list and mount everything in it
sudo systemctl daemon-reload
sudo mount -a
# Confirm it worked — you should see the drive's size
df -h /mnt/music
# ext4 only: give your user ownership so you can copy music in
sudo chown -R $USER:$USER /mnt/music
Step 4: Install Docker
Docker runs Navidrome in a self-contained "container", which makes installing, updating, and removing it a one-line job.
# Docker's official install script (works on Raspberry Pi OS)
curl -fsSL https://get.docker.com | sh
# Let your user run docker without typing sudo every time
sudo usermod -aG docker $USER
# Log out and back in so the group change takes effect
exit
ssh pi@musicpi.local
# Test — should print "Hello from Docker!"
docker run --rm hello-world
Step 5: Create the Navidrome Configuration
# A home for Navidrome's settings and database
mkdir -p ~/navidrome/data
cd ~/navidrome
# Find your user and group ID numbers (usually 1000 and 1000)
id -u
id -g
# Create the configuration file
nano compose.yaml
Paste this in (change 1000:1000 if id printed different numbers):
services:
navidrome:
image: deluan/navidrome:latest # official image, supports Pi (arm64)
container_name: navidrome
user: "1000:1000" # run as your user, not root
ports:
- "4533:4533" # web player on port 4533
restart: unless-stopped # start again automatically after a reboot
environment:
ND_SCANSCHEDULE: "1h" # look for new music every hour
ND_LOGLEVEL: "info"
ND_SESSIONTIMEOUT: "168h" # stay logged in for a week
ND_ENABLESHARING: "true" # allow share links for songs/albums
volumes:
- "./data:/data" # database + settings (back this up!)
- "/mnt/music:/music:ro" # your music, read-only so it can't be changed
Save and exit (Ctrl+O, Enter, Ctrl+X).
YAML files are picky about spaces. Use spaces (never tabs) and keep the indentation exactly as shown.
Step 6: Start Navidrome and Create Your Account
# Start Navidrome in the background (-d = detached)
docker compose up -d
# Check it's running — STATUS should say "Up"
docker compose ps
# Watch the logs as it scans your music (Ctrl+C to stop watching)
docker compose logs -f
- On any device on your home network, open http://musicpi.local:4533
- The first visit asks you to create the admin user. Pick a strong password — this account can manage everything.
- You'll land in the web player. If your drive already had music on it, albums start appearing as the scan runs.
- For family members: open the admin Settings → Users menu and click + Create, and give each person their own login so they get their own playlists and history.
Step 7: Add and Organise Your Music
Folder layout
Navidrome reads the tags inside each file (artist, album, track number), so good tags matter more than folder names. A tidy layout still helps you:
/mnt/music/
├── Artist Name/
│ └── Album Name (Year)/
│ ├── 01 - First Song.flac
│ ├── 02 - Second Song.flac
│ └── cover.jpg ← album art is picked up automatically
└── Another Artist/
└── …
Copying music from your computer
# Run this on YOUR COMPUTER (macOS/Linux), not the Pi.
# rsync copies only new/changed files, so re-running it is fast.
rsync -avh --progress ~/Music/ pi@musicpi.local:/mnt/music/
On Windows, use WinSCP (drag and drop over SSH), or unplug the drive and copy to it directly.
Fixing messy tags
MusicBrainz Picard (free, on Windows, macOS, and Linux) listens to each file's audio fingerprint, looks it up in a huge online database, and fixes titles, track numbers, and album art automatically. Run it on your collection before copying it to the Pi.
Where to get music you own
- Rip your CDs — Sound Juicer or Asunder on Linux, fre:ac or Exact Audio Copy on Windows. Rip to FLAC to keep full quality.
- Buy DRM-free downloads — Bandcamp (artists get a large share), Qobuz, and 7digital sell files you keep forever.
- Old iTunes purchases — songs bought since 2009 are DRM-free AAC files and work as-is.
New music appears within the hour (the scan schedule). To scan right away, open the web player, click the activity icon in the top bar, and start a quick scan. (Or restart the container: docker compose restart.)
Step 8: Install the Phone and Desktop Apps
Navidrome speaks the Subsonic protocol, so any Subsonic-compatible app works. Good picks:
| Platform | App | Notes |
|---|---|---|
| Android | Symfonium | Paid (one-time), the most polished. Offline sync, Android Auto, casting. |
| Android | Tempo | Free and open source; available on F-Droid and GitHub. |
| iPhone | Amperfy | Free and open source. CarPlay and offline downloads. |
| iPhone | play:Sub / substreamer | Popular alternatives with CarPlay support. |
| Desktop | Feishin / Supersonic | Spotify-style desktop players for Linux, Windows, and macOS. |
In any of these apps, add a server with:
- Server address:
http://musicpi.local:4533(or the Pi's IP, e.g.http://192.168.1.50:4533) - Username / password: your Navidrome login
At this point everything works at home. The next step makes it work everywhere.
Step 9: Listen From Anywhere (Safely) With Tailscale
Tailscale creates a private, encrypted network between your own devices. Your phone can reach the Pi from anywhere, but the Pi is not exposed to the public internet. It's free for personal use.
# On the Pi: install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Connect the Pi to your Tailscale account — open the link it prints and log in
sudo tailscale up
# Show the Pi's Tailscale address (starts with 100.)
tailscale ip -4
- Install the Tailscale app on your phone and log in with the same account.
- In your music app, change the server address to
http://musicpi:4533(Tailscale's MagicDNS name) orhttp://100.x.y.z:4533. - Turn off Wi-Fi on your phone and hit play. If music plays over mobile data, you're done.
Don't just forward port 4533 on your router. That puts an unencrypted login page on the open internet. If you really want a public address (e.g. to share links with friends), put Navidrome behind a reverse proxy with HTTPS, such as Caddy, and keep it updated. See Server Hardening first.
Step 10: Save Mobile Data With Transcoding
A FLAC song can be 30–40 MB. Navidrome can shrink songs on the fly to a much smaller format while streaming,
without touching your originals. The Docker image already includes the ffmpeg converter it needs.
- Most apps have a "max bitrate on mobile data" setting — set it to 128 or 160 kbps. The app asks Navidrome to transcode, and it does.
- Navidrome comes with MP3 and Opus transcoding already set up. Opus gives the best quality per MB. (Want to edit the formats yourself? Add
ND_ENABLETRANSCODINGCONFIG: "true"to the environment list, restart, and a Transcodings page appears in Settings.) - For Wi-Fi at home, leave it at "original" for full quality.
Getting the "Spotify Experience"
| Spotify feature | How to get it here |
|---|---|
| Playlists & liked songs | Built in. Hearts (stars) sync across every app. |
| Offline downloads | Symfonium, Amperfy, and Tempo can download albums/playlists to your phone. |
| Auto-generated playlists | Smart playlists: drop a .nsp file into your music folder with rules like "rating ≥ 4 and played in the last 30 days". See Navidrome's docs. |
| Listening history / "Wrapped" | Connect ListenBrainz or Last.fm scrobbling from your Navidrome profile page. |
| Lyrics | Embedded lyrics tags, or .lrc files with the same name as the song, are shown by supporting apps. |
| Share a song with a friend | Share links (enabled by ND_ENABLESHARING). Only reachable by others if you've set up public access. |
| Discover new music | Not included — you only hear what you own. Use Bandcamp, ListenBrainz recommendations, or radio for discovery. |
Keeping It Running
Update Navidrome
cd ~/navidrome
docker compose pull # download the newest version
docker compose up -d # restart on the new version (your data is kept)
docker image prune -f # delete the old version to free space
Update the Pi itself (monthly)
sudo apt update && sudo apt full-upgrade -y
Back up the important parts
Your music is the big one — keep a second copy on another drive or computer. Navidrome's own data (users, playlists, play counts) is tiny and lives in ~/navidrome/data:
# Run on your computer: copies the Pi's Navidrome data to a local backup folder
rsync -avh pi@musicpi.local:~/navidrome/data/ ~/Backups/navidrome-data/
Troubleshooting
- Web page won't load — Run
docker compose psin~/navidrome. If it isn't "Up", checkdocker compose logsfor the error (usually a typo incompose.yaml). - Library is empty — Check that the files are really there:
ls /mnt/music. If/mnt/musicis empty, the drive didn't mount — runsudo mount -aand re-check/etc/fstab. - "permission denied" in the logs — The
user:numbers incompose.yamlmust match the owner of the files. Fix withsudo chown -R 1000:1000 /mnt/music ~/navidrome/data. - Songs show as "Unknown Artist" — The files are missing tags. Fix them with MusicBrainz Picard, then rescan.
- Pi reboots randomly or the drive disconnects — Almost always power. Run
vcgencmd get_throttled; anything other thanthrottled=0x0means under-voltage. Use the official PSU and a powered hub for hard drives. - Streaming stutters away from home — Lower the mobile bitrate in your app (Step 10), and use Ethernet on the Pi.