Configure Self-Hosted Music Library
I stopped paying for Spotify last year. Not because I don't love music—I do—but because I realized I could own my collection outright and stream it everywhere without monthly fees. A self-hosted music server changed how I listen: lossless audio, unlimited access, no throttling, no recommendations I didn't ask for. This guide walks you through building one.
Why Self-Host Music?
Music streaming services lock you into subscription tiers. You rent access; you don't own anything. A self-hosted music server flips that entirely. You upload your FLAC, MP3, or OGG files once and stream them from anywhere—home network, mobile devices, even remote locations with a reverse proxy. The setup is cheaper than one year of Spotify Premium, and it works forever.
I prefer Navidrome for its lightweight footprint and dead-simple interface. It runs on Docker, scales to 50,000+ tracks, and consumes maybe 100MB of RAM idle. The web client is responsive, native mobile apps work beautifully, and you can stream transcoded audio on bandwidth-constrained connections.
Choose Your Application
Navidrome is my default recommendation. It's fast, modern, and maintains an active community. If you're running a modest homelab on a Raspberry Pi or low-power NAS, Navidrome won't strain hardware.
Subsonic is the veteran option—battle-tested, widely supported by client applications, but requires a Java runtime and isn't as lightweight. The original Subsonic is paid; clones like Airsonic exist if you want open-source.
Funkwhale is federation-focused and feature-rich but heavier. Use it if you want social music sharing and are okay with more overhead.
I'm using Navidrome because it starts faster on cold boot, updates cleanly, and the community fixes bugs within days.
Set Up Navidrome with Docker Compose
Assumption: you have Docker and Docker Compose installed. If not, install Docker first.
Create a directory for your music stack:
mkdir -p ~/music-server/music ~/music-server/data
cd ~/music-server
Create this docker-compose.yml:
version: '3.8'
services:
navidrome:
image: deluan/navidrome:latest
container_name: navidrome
ports:
- "4533:4533"
environment:
ND_LOGLEVEL: info
ND_BASEURL: "http://localhost:4533"
ND_ENABLESTARRATING: "true"
ND_ENABLESHARING: "false"
ND_SESSIONTIMEOUT: "24h"
ND_PLAYLISTSMAXSIZE: "500"
ND_TRANSCODE_MP3: "opus,mp3"
ND_TRANSCODE_DEFAULT: "mp3"
volumes:
- ./data:/data
- ./music:/music:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:4533/rest/ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
Start the container:
docker-compose up -d
Check logs to confirm it's running:
docker-compose logs -f navidrome
Once you see a line like Listening at 0.0.0.0:4533, open http://localhost:4533 in your browser. The default user is admin with password password—change this immediately.
Organize and Populate Your Music Library
Navidrome scans the ./music folder automatically every hour. To speed up the first scan, place your music files in subdirectories:
music/
├── Artist Name/
│ └── Album Name/
│ ├── 01 - Song.flac
│ ├── 02 - Song.flac
│ └── cover.jpg
└── Another Artist/
└── Another Album/
├── 01 - Song.mp3
└── 02 - Song.mp3
Navidrome reads ID3 tags, so proper metadata matters. I use MusicBrainz Picard to tag files automatically—it's free and massively faster than manual tagging.
To force an immediate rescan instead of waiting an hour:
docker-compose exec navidrome curl -s http://localhost:4533/rest/startScan.view
Expose Navidrome Securely with Caddy
To access your music library outside your home network, use Caddy as a reverse proxy. It handles HTTPS automatically via Let's Encrypt.
First, buy a domain. I use a cheap domain registrar (Namecheap, Porkbun) and point it to my home IP or use Cloudflare CNAME. For reliability, consider a small RackNerd VPS to run a reverse proxy in the cloud instead.
On your home server, install Caddy:
sudo apt update
sudo apt install -y caddy
Edit /etc/caddy/Caddyfile:
music.yourdomainhere.com {
encode gzip
reverse_proxy localhost:4533 {
header_uri -Authorization
}
handle_path /admin/* {
reverse_proxy localhost:4533
}
}
Restart Caddy:
sudo systemctl restart caddy
Caddy automatically obtains an SSL certificate and renews it. You can now access https://music.yourdomainhere.com from anywhere.
Add Authentication
Navidrome's built-in user management is basic but sufficient. Log in as admin, go to Settings → Users, and create accounts for family members. Each gets their own play history and ratings without seeing others' data.
For stronger security, run Authelia in front of Navidrome. This adds TOTP (two-factor authentication) and SSO capabilities, but adds complexity. I skip it for home use—network isolation via Tailscale is enough.
Mobile Streaming
Download Navidrome (official iOS/Android app), Substreamer, or DSub for Android. They all work beautifully with a self-hosted Navidrome instance.
Point your app at your external URL (e.g., https://music.yourdomainhere.com) and log in with your Navidrome credentials. Streaming starts instantly, with options to cache tracks for offline listening.
Transcoding on Demand
If you store FLAC files but want to save mobile bandwidth, Navidrome transcodes on the fly. The Docker image includes FFmpeg, so transcoding works out of the box. Configure it in the environment variables:
ND_TRANSCODE_MP3: "opus,mp3"
ND_TRANSCODE_DEFAULT: "mp3"
This tells Navidrome to transcode to MP3 by default on bandwidth-constrained connections and opus for newer clients.
Backup Your Library
Your music metadata and play history live in ./data/navidrome.db. Back it up regularly:
docker-compose exec navidrome tar -czf - /data > navidrome-backup-$(date +%Y%m%d).tar.gz
Store backups on a separate disk or cloud storage. Your FLAC files are replaceable (you own them), but play history and ratings aren't.
Performance Tuning
For large libraries (10,000+ tracks), adjust Docker memory limits:
services:
navidrome:
# ... other config ...
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
Enable database indexing by setting ND_DBPATH: /data/navidrome.db to an SSD if possible. SQLite benefits massively from fast I/O.
Conclusion
You now have a self-hosted music library that rivals Spotify for usability and beats it on flexibility. Your music is yours—no DRM, no licensing agreements, no account termination risk.
Next steps: load your music, invite family to join, and consider setting up automatic backups with Restic or Duplicacy. If you want to go deeper, add Jellyfin for video or Vaultwarden for credential management in the same Docker Compose file.