Skip to content

How to Deploy Navidrome on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host Navidrome on a tiny VPS with Docker Compose and HTTPS — a Subsonic-compatible music server that streams your own library to web, mobile and desktop apps.

Before you start
  • A small VPS — 1 vCPU / 512 MB RAM is plenty for the server
  • Enough disk for your music library
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Navidrome is

Navidrome is a self-hosted music streaming server. It indexes a folder of music files, serves a web player, and — the part that matters most — speaks the Subsonic API, so dozens of existing mobile and desktop music apps can connect to it as if it were a streaming service. Your library, your server, whichever client you prefer.

It is a single Go binary under GPL-3.0, rated 2 / 5 to deploy. The install is one container. The two things worth getting right are file permissions (Navidrome runs as your user, not root) and setting a password encryption key before you create any users.

Server sizing

Navidrome is the lightest media server in the catalog. On our test box (a GCP e2-standard-2 on Ubuntu 26.04) the idle container used about 16 MB of RAM and 333 MB of disk. The catalog's floor is 256 MB of RAM.

  • 512 MB RAM / 1 vCPU — comfortable for a personal library, with room for the OS and a reverse proxy.
  • 1 GB RAM+ — only if the same box runs other services.

The first scan of a large library is the busiest moment; upstream's own estimate is from under a minute for fewer than 1,000 songs to 15 minutes or more past 50,000. After that, CPU is mostly idle unless clients ask for transcoded streams. Disk is the real constraint — a lossless library outgrows a small VPS disk quickly, so plan for attached block storage if yours is large.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed, along with a non-root user and a ufw firewall. If not, work through Docker & Compose on Ubuntu first.

Open SSH and the reverse proxy ports only:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose
Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Contaboalso works on
4 vCPU · 8 GB RAM · 100 GB SSD · $4.95/mo
Get Contabo (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Navidrome (Docker Compose)

Permissions first

Navidrome needs read-write access to /data (its database and cache), read access to /music, and upstream is firm that it should not run as root. The image uses a user: directive — the PUID/PGID variables you may know from linuxserver images have no effect here. So create the folders as your own user and record your IDs:

mkdir -p ~/navidrome && cd ~/navidrome
mkdir -p data music backup
printf 'PUID=%s\nPGID=%s\n' "$(id -u)" "$(id -g)" > .env

The password encryption key

To stay compatible with the Subsonic API, Navidrome has to store user passwords in a recoverable form. By default it encrypts them with a shared key that is in the public source code, which is obfuscation rather than protection. Setting your own PasswordEncryptionKey fixes that — and upstream warns it is a one-time setting: once passwords are encrypted with it, changing it locks users out. Set it now, before the first user exists:

cd ~/navidrome
grep -q ND_PASSWORDENCRYPTIONKEY .env || echo "ND_PASSWORDENCRYPTIONKEY=$(openssl rand -hex 32)" >> .env

Keep a copy of .env with your backups. Without that key, a restored database has passwords nobody can use.

The compose file

This is upstream's compose example with the port on loopback, the music mounted read-only, and automatic database backups turned on:

cd ~/navidrome
cat > docker-compose.yml <<'YAML'
services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    user: "${PUID}:${PGID}"
    ports:
      # Loopback only — Caddy is the sole route in from outside.
      - "127.0.0.1:4533:4533"
    restart: unless-stopped
    environment:
      ND_PASSWORDENCRYPTIONKEY: ${ND_PASSWORDENCRYPTIONKEY}
      ND_BACKUP_PATH: /backup
      ND_BACKUP_SCHEDULE: "0 3 * * *"
      ND_BACKUP_COUNT: 7
    volumes:
      - ./data:/data
      - ./music:/music:ro
      - ./backup:/backup
YAML
docker compose up -d

Check that it started and answers:

cd ~/navidrome
for i in $(seq 1 30); do curl -fsS -o /dev/null http://127.0.0.1:4533/ && break; sleep 2; done
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4533/
docker compose logs --tail 20 navidrome

The line to look for in the log is Navidrome server is ready!, followed by the first library scan.

HTTPS + domain

Point an A record for music.example.com at the server's public IP, wait for it to resolve, then put Caddy in front — see Automatic HTTPS with Caddy:

music.example.com {
    reverse_proxy 127.0.0.1:4533
}

Navidrome's security notes recommend exactly this: its built-in HTTP server works, but a reverse proxy should handle TLS. If you'd rather serve it under a path such as example.com/music, set ND_BASEURL: /music in the environment and proxy that path. If Caddy runs as a container, use reverse_proxy navidrome:4533 inside a shared compose file instead of 127.0.0.1.

First run

Open https://music.example.com. The first screen is admin user creation: pick a username and password and click Create Admin. As with most servers of this kind, whoever reaches the page first gets the admin account, so do this immediately after the proxy is up.

Then upload your music. The simplest route is rsync from your own computer into ~/navidrome/music, organised as Artist/Album/tracks. Navidrome notices new files on its own; you can browse as soon as the first albums appear.

To use a phone app, pick any Subsonic-compatible client and point it at https://music.example.com with your Navidrome username and password.

Securing it

  • One user per listener. Play counts, favourites and playlists are per user.
  • Leave transcoding config locked. Upstream disables editing transcoding commands in the web UI by default, because a transcoding command is a command that runs on your server. Enable ND_ENABLETRANSCODINGCONFIG only while you make a change, then turn it off again.
  • Rely on the rate limiter, but use strong passwords. Login attempts are rate-limited by default, but Subsonic clients authenticate with your password on every request, so make it a unique one.
  • Never drop the user: line to "fix" permissions. Running as root makes a permission error disappear by removing the protection. Fix the folder ownership instead.

Backups

The compose file above already makes a daily database backup at 03:00, keeping seven, in ~/navidrome/backup. Those contain the database only — users, play counts, playlists — not the music and not your .env. You can also make one on demand:

cd ~/navidrome
docker compose run --rm navidrome backup create
ls -la backup

Then copy the backups, .env (the encryption key) and docker-compose.yml off the box, along with the music if it doesn't exist anywhere else:

cd ~/navidrome
tar czf navidrome-$(date +%F).tar.gz backup .env docker-compose.yml

To restore, upstream is explicit that the server must be stopped first: docker compose stop, then docker compose run --rm navidrome backup restore with the backup file, then docker compose start.

Upgrades

cd ~/navidrome
docker compose pull
docker compose up -d

latest follows each release. Read the release notes before upgrading across a larger version jump; the automatic backup gives you a recent restore point either way.

Troubleshooting

The container exits at startup with unable to open database file. The data folder isn't writable by the IDs in .env. Fix it with sudo chown -R $(id -u):$(id -g) ~/navidrome/data.

The web UI works but the library is empty, and the log says open /music: permission denied. The music folder isn't readable. Either chown it to your user, or open it for reading with sudo chmod -R a+rX ~/navidrome/music. Upstream notes the misleading Target folder does not exist warning that comes with it — the folder exists; the container user just can't open it.

A client app can't log in. Check the server URL includes https:// and no port, and that the same username and password work in the web UI. If the web UI works and the app doesn't, read docker compose logs --tail 50 navidrome while the app retries — the failed request shows up there.

Verification + next steps

You're done when you can load https://music.example.com over a valid certificate, sign in, play a track in the browser, connect a Subsonic app on your phone, and see a file in ~/navidrome/backup the morning after.

From there, automate the library with Lidarr, which watches artists for new releases and files them into your music folder. For films and TV, Jellyfin is the companion server; for audiobooks and podcasts, Audiobookshelf.

Next steps

How to self-host Navidrome →More self-hosted media server tools →Best VPS for Jellyfin →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.