All articles
TutorialsJun 11, 2026 · 23 min read

Self-Host Navidrome on a VPS for Your Own Music Streaming

Self-Host Navidrome on a VPS for Your Own Music Streaming

If you have spent years building a music collection - ripped CDs, Bandcamp purchases, DRM-free downloads - it probably sits in a folder you can only reach from one computer. Meanwhile you pay a monthly fee to stream music you do not own, with songs that quietly disappear when a licensing deal expires. Navidrome fixes both problems. It is a tiny, fast music server that turns your library into a streaming service: a clean web player, your own playlists, play counts, smart playlists, and access from any device through the wide Subsonic app ecosystem.

This guide walks through a production-ready Navidrome install on a VPS using Docker, with Caddy in front for automatic HTTPS. It covers the parts that trip people up: file tagging, transcoding for slow connections, the Subsonic API that unlocks dozens of mobile apps, and scrobbling to Last.fm or ListenBrainz so you keep your listening history.

Navidrome is read-only against your music files - it never edits or moves them. That means the quality of your library depends entirely on your tags. Fix metadata before you import, not after, or you will be re-scanning a thousand albums to correct one typo.

TL;DR

  • Install Docker and Docker Compose on a fresh VPS
  • Point a subdomain like music.example.com at your server
  • Run Navidrome and Caddy from a single docker-compose.yml
  • Mount two volumes: data (database) and music (read-only library)
  • Caddy handles HTTPS automatically
  • Create the admin account on first run, then let the scanner index your files
  • Connect any Subsonic app on mobile and stream from anywhere

Total time: about 15 minutes.

What You Need

  • A VPS with 1 GB RAM (Navidrome is written in Go and sips memory)
  • Disk space for your music - lossless libraries get big, so a storage VPS is worth it past a few thousand albums
  • A domain you can add DNS records to
  • Ports 80 and 443 open to the internet (Let's Encrypt needs them)
  • Root or sudo access

Navidrome is one of the lightest media servers you can run. The whole binary is a few tens of megabytes, the database is SQLite, and streaming a file is cheap. The only heavy operation is on-the-fly transcoding, which is optional. Disk for the music is the real sizing question.

Step 1: Point a Subdomain at Your VPS

In your DNS provider, create an A record:

music.example.com → YOUR_VPS_IPV4

Add an AAAA record too if your server has IPv6. Confirm it resolves:

dig +short music.example.com

The output should be your VPS IP. Caddy cannot issue a certificate until DNS points at the box.

Step 2: Install Docker and Docker Compose

On a fresh Ubuntu 22.04 or 24.04 server:

sudo apt update sudo apt install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Check it works:

docker --version docker compose version

Step 3: Open the Firewall

sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable

Caddy needs 80 for the ACME HTTP challenge and 443 for HTTPS. Do not expose Navidrome's internal port 4533 to the internet - Caddy is the only thing that should answer publicly.

Step 4: Create the Project Directory

sudo mkdir -p /opt/navidrome cd /opt/navidrome sudo mkdir -p data music caddy-data caddy-config

The two data directories map to how Navidrome splits state:

  • data/ holds the SQLite database, the search index, cached artwork, and your settings, playlists, and play counts
  • music/ is your library, mounted read-only

If your VPS has a large data disk mounted elsewhere, point the music folder at it:

sudo mkdir -p /mnt/storage/music sudo ln -s /mnt/storage/music /opt/navidrome/music

Step 5: Tag Your Music Properly

Navidrome builds its entire library from the tags embedded in your files, not from folder names. Good tags mean clean albums, correct artists, and working browse-by-genre. Bad tags mean a hundred "Unknown Artist" entries. Before you upload anything, run your collection through a tagger like MusicBrainz Picard, which matches files against the MusicBrainz database and writes consistent tags automatically.

The tags that matter most:

  • Album Artist - this is what Navidrome groups by, not Artist. Set it on every track or compilations will scatter into one entry per featured artist.
  • Album, Title, Track Number, Disc Number - the basics that order an album correctly.
  • Date or Year, Genre - for browsing and smart playlists.

A clean folder layout helps you stay organized even though Navidrome ignores it for grouping:

music/ The National/ 2019 - I Am Easy to Find/ 01 - You Had Your Soul with You.flac 02 - Quiet Light.flac Khruangbin/ 2018 - Con Todo el Mundo/ 01 - Cómo Me Quieres.flac

Navidrome plays FLAC, MP3, AAC, Ogg, Opus, WavPack, and more. Keep your originals in whatever quality you ripped them at - transcoding for slow connections happens on demand without touching the source files.

Step 6: Write the Compose File

Create /opt/navidrome/docker-compose.yml:

services: navidrome: image: deluan/navidrome:latest container_name: navidrome restart: unless-stopped user: "1000:1000" environment: ND_SCANSCHEDULE: "1h" ND_LOGLEVEL: "info" ND_BASEURL: "https://music.example.com" ND_ENABLETRANSCODINGCONFIG: "true" TZ: "Europe/Berlin" volumes: - ./data:/data - ./music:/music:ro networks: - musicnet caddy: image: caddy:2 container_name: navidrome-caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - ./caddy-data:/data - ./caddy-config:/config networks: - musicnet networks: musicnet:

Notes:

  • The user: "1000:1000" line runs Navidrome as your first non-root user. Match it to whoever owns the music files. Run id -u and id -g to confirm.
  • music is mounted :ro (read-only) on purpose. Navidrome never needs to write to your library, and read-only mounts mean a bug or a bad actor cannot delete your collection.
  • ND_SCANSCHEDULE: "1h" re-scans for new files every hour. Set it to "0" to disable scheduled scans and trigger them manually instead.
  • ND_ENABLETRANSCODINGCONFIG: "true" unlocks the transcoding settings in the web UI. Leave it on if you want to stream lower bitrates on mobile data.
  • The internal port 4533 stays on the musicnet Docker network. Caddy reaches it by container name.

Step 7: Write the Caddyfile

Create /opt/navidrome/Caddyfile:

music.example.com { encode zstd gzip reverse_proxy navidrome:4533 { header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme} header_up Host {host} } }

The X-Forwarded-Proto header is the one that matters most. Without it Navidrome can build http:// links internally and the Subsonic apps refuse to connect over mixed-content rules. Caddy handles the certificate, renewal, and HTTP-to-HTTPS redirect with no extra config.

Step 8: Start the Stack

cd /opt/navidrome sudo docker compose up -d sudo docker compose logs -f

Wait until the Caddy logs show the Let's Encrypt certificate was issued, then open https://music.example.com. You should land on the Navidrome account-creation screen.

Step 9: Create the Admin Account

The first screen asks you to create the admin user. This is the all-powerful account, so pick a strong password - there is no email recovery flow, only resetting it from the command line. Once you are in, Navidrome immediately starts its first scan in the background.

You can watch progress in the logs:

sudo docker compose logs -f navidrome

A few thousand tracks index in a minute or two. Large lossless libraries take longer because Navidrome reads the tags and extracts embedded artwork from every file. Let the first scan finish before you judge whether anything is missing.

Step 10: Add Users Without Giving Away the Keys

Navidrome has proper multi-user support with per-user playlists, favorites, and play counts. Add accounts under Settings -> Users -> Add:

  • Leave Admin unchecked for everyone who is not you. Regular users can play music, build playlists, and mark favorites but cannot change server settings or manage other accounts.
  • There is no public signup form. Accounts are created by an admin only, which is exactly the right default for a public-facing server. Keep it that way.

Each user gets their own listening data, so your partner's heavy-rotation playlist never pollutes your own recommendations and play counts.

Step 11: Connect a Mobile App via the Subsonic API

This is where Navidrome shines. It implements the Subsonic API, an open standard supported by a large family of polished third-party apps. You are not locked into one official client - pick the player you like best:

In any of these apps, choose Subsonic or Navidrome as the server type and enter:

  • Server / URL: https://music.example.com
  • Username and password: your Navidrome login

Most apps support offline downloads, gapless playback, and scrobbling. Pull an album over Wi-Fi before a flight and it plays without a connection.

Do not connect over plain http:// or a raw IP address. Subsonic apps and modern browsers block insecure media connections, and your password would travel unencrypted on every request. The Caddy setup above gives you a valid certificate for free - always use the https:// hostname.

Step 12: Keep Your Listening History with Scrobbling

If you have years of Last.fm history, you do not have to abandon it. Navidrome scrobbles natively to both Last.fm and the open ListenBrainz. Each user links their own account under Settings -> Personal -> Last.fm / ListenBrainz, authorizes once, and every play from the web player or a connected app gets recorded. ListenBrainz is the better long-term choice if you want your data exportable and not tied to a single company.

Step 13: Transcoding for Slow Connections

Streaming a 1411 kbps FLAC over hotel Wi-Fi or capped mobile data is painful. Navidrome can transcode on the fly to a smaller format without altering your originals. Because you set ND_ENABLETRANSCODINGCONFIG: "true" earlier, the controls live under Settings -> Transcoding in the web UI - choose a player profile and a max bitrate (192 kbps Opus or MP3 is a sweet spot for mobile).

Transcoding uses ffmpeg, which ships inside the official Navidrome image, so there is nothing extra to install. It does use CPU per active stream, so on a small shared VPS keep concurrent transcoded streams modest. For listeners at home on a fast line, leave them on the original-quality profile and skip transcoding entirely.

Step 14: Back Up the Database

Your music files are presumably backed up already. The piece that is unique to Navidrome - playlists, play counts, favorites, ratings, and user accounts - lives in the data directory. Losing it means rebuilding every playlist by hand. Pair it with a nightly restic job to object storage:

restic -r s3:s3.amazonaws.com/my-backup-bucket backup /opt/navidrome/data

The directory is small - usually well under a gigabyte even for a big library - so backups are fast and cheap. Restoring is just dropping the folder back and starting the container.

Optional: Keep It Private with a VPN

If the only people using your server live in your house, you do not need it on the public internet at all. Putting Navidrome behind Tailscale removes every bot scan and login-bruteforce attempt in one move. Skip the firewall openings for 80 and 443, reach music.example.com over your tailnet, and the Subsonic apps connect exactly the same way. A WireGuard VPN on your VPS works just as well if you already run one.

Troubleshooting

Caddy returns a 502 right after docker compose up. Navidrome takes a few seconds to initialize its database on first boot, and Caddy can hit it first. Wait 30 seconds and reload. If it sticks, check docker compose logs navidrome.

The library is empty after the scan. Almost always a path or permission issue. Confirm your files live under the folder mounted at /music and that the container user can read them: docker exec -it navidrome ls -la /music. If you see permission denied, fix ownership on the host with sudo chown -R 1000:1000 /opt/navidrome/music.

Albums are split into many entries or show "Unknown Artist". This is a tagging problem, not a Navidrome bug. Set the Album Artist tag consistently across every track of an album and re-run a scan. MusicBrainz Picard fixes this in bulk.

A Subsonic app says "wrong username or password" with correct credentials. Some older apps default to a legacy authentication mode. In the app's server settings, try toggling between the modern token auth and legacy plain auth, and make sure the URL has no trailing path - just https://music.example.com.

Transcoding does nothing or the bitrate is wrong. Confirm ND_ENABLETRANSCODINGCONFIG is true and that the player profile in Settings -> Transcoding is assigned to the right client. Some apps request the original file regardless of the server profile, so also check the app's own quality settings.

Going Further

  • Clean up tags at scale. Run your whole library through MusicBrainz Picard once and future scans stay tidy. It is the single biggest quality win for a self-hosted music server.
  • Build smart playlists. Navidrome supports NSP (Navidrome Smart Playlist) JSON files dropped into your music folder - auto-updating playlists like "added in the last 30 days" or "5-star jazz".
  • Run it next to your other media. If you already self-host Jellyfin for video or Audiobookshelf for audiobooks, Navidrome rounds out the stack on the same box behind the same Caddy instance - just add another site block.
  • Automate offsite backups. Wire the data folder into a nightly restic job so a dead disk never costs you your playlists and play history.

That's it. Self-hosted Navidrome gives you a fast, private streaming service built from music you actually own, reachable from any device, with no subscription, no ads, and no songs vanishing from your library because a licensing deal lapsed.


Need a VPS for your music collection? Our Linux plans ship with NVMe storage, generous bandwidth, and a storage tier for big lossless libraries. See the options.