Navidrome Music Server: Stream Your Personal Audio Library with Docker

Learn how to set up Navidrome on your VPS using Docker Compose to stream your music library securely to Subsonic-compatible mobile apps.

Self-hosting your music library with Navidrome provides a lightweight, fast, and private alternative to proprietary streaming services. Navidrome is compatible with the Subsonic API, allowing you to use a wide range of mobile and desktop clients.

This guide covers the production-ready deployment of Navidrome on a Virtual Private Server (VPS) using Docker Compose, including secure reverse proxy configuration, media scanning optimization, and transcoding setups.

Directory Structure and Host Preparation

Before launching the container, establish a clean directory structure on your host machine. Placing all persistent data under /opt or a dedicated user home directory simplifies backups and permissions management.

Create the required directories:

sudo mkdir -p /opt/navidrome/data
sudo mkdir -p /opt/navidrome/music
sudo mkdir -p /opt/navidrome/playlists

Set the proper ownership. Navidrome runs inside the container as user 1000:1000 by default. Adjust host permissions to ensure the container can read your music and write to its data directory:

sudo chown -R 1000:1000 /opt/navidrome

Docker Compose Configuration

Create a docker-compose.yml file in /opt/navidrome/:

version: '3.8'

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    user: "1000:1000"
    ports:
      - "4533:4533"
    environment:
      # Core settings
      ND_MUSICFOLDER: "/music"
      ND_DATAFOLDER: "/data"
      ND_PLAYLISTSPATH: "/playlists"
      ND_PORT: 4533
      ND_LOGLEVEL: "info"

      # Security & Sessions
      ND_SESSIONTIMEOUT: "24h"
      ND_BASEURL: ""

      # Library & Scanning
      ND_SCANSCHEDULE: "@every 30m"
      ND_MONITORCHANGES: "true"

      # Transcoding & Performance
      ND_TRANSCODING_ENABLED: "true"
      ND_IMAGECACHE_SIZE: "100MB"
      ND_PLAYBACKREPORTING_ENABLED: "true"

      # Metrics (Optional)
      ND_PROMETHEUS_ENABLED: "true"
      ND_PROMETHEUS_PATH: "/metrics"
    volumes:
      - /opt/navidrome/data:/data
      - /opt/navidrome/music:/music:ro
      - /opt/navidrome/playlists:/playlists
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true

Key Environment Variables Explained

  • ND_MONITORCHANGES: Enables real-time file system monitoring using fsnotify. When files are added, modified, or deleted in the /music folder, Navidrome detects the changes and updates the database immediately.
  • ND_SCANSCHEDULE: A cron-like schedule acting as a fallback scanner. @every 30m runs a full library scan every 30 minutes.
  • ND_MUSICFOLDER:ro: The music volume is mounted as read-only (:ro) to protect your original media files from accidental deletion or modification by the container.

Reverse Proxy Configuration

To access Navidrome securely over HTTPS, configure a reverse proxy such as Nginx or Caddy.

Option 1: Nginx Configuration

Create an Nginx server block under /etc/nginx/sites-available/navidrome:

server {
    listen 80;
    server_name music.example.com;

    # Redirect all HTTP traffic to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name music.example.com;

    ssl_certificate /etc/letsencrypt/live/music.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/music.example.com/privkey.pem;

    # Secure SSL parameters
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 100M;

    location / {
        proxy_pass http://127.0.0.1:4533;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support (Required for Navidrome UI)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Buffering settings for streaming stability
        proxy_buffering off;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

Option 2: Caddy (Simplest Configuration)

If you use Caddy, the reverse proxy and automatic SSL generation can be configured with a clean, two-line block in your Caddyfile:

music.example.com {
    reverse_proxy localhost:4533
}

Transcoding Setup & Optimizations

Transcoding allows you to convert high-fidelity lossless files (like FLAC or ALAC) to lower-bitrate lossy formats (like MP3 or Opus) on the fly, saving mobile data when streaming on the go.

Navidrome uses a built-in static build of ffmpeg for transcoding. You do not need to install ffmpeg on your VPS host.

Configuring Transcoding Profiles

Transcoding is configured within the Navidrome Web UI:

  1. Log into your Navidrome instance as an administrator.
  2. Navigate to Settings > Transcoding.
  3. Click Add Transcoding Configuration.
  4. Configure an Opus profile (highly recommended for high-quality audio at low bitrates):
    • Name: Opus Mobile
    • Command: ffmpeg -i %f -map 0:0 -b:a 128k -v 0 -f opus -
    • Extension: opus
    • MimeType: audio/ogg
  5. Assign the transcoding profile to your target players or users in their respective settings panels.

For legacy clients that do not support Opus, configure an MP3 transcoding fallback:

  • Command: ffmpeg -i %f -map 0:0 -codec:a libmp3lame -q:a 2 -v 0 -f mp3 -
  • Extension: mp3
  • MimeType: audio/mpeg

Media Scanning & Tagging Best Practices

Navidrome relies heavily on proper metadata tagging rather than folder layout to organize your library.

Metadata Recommendations

  • Use ID3v2.4 or Vorbis Comments: Ensure your files are tagged using modern standards.
  • Tagging Tool: Use MusicBrainz Picard or Beets to automate and standardize tagging.
  • Embedded Artwork: While Navidrome scans for external files like cover.jpg or folder.png, embedding high-quality artwork directly in the file metadata is the most reliable approach. Keep artwork files under 1MB to prevent memory overhead during scans.

Triggering Manual Scans

If you need to force an immediate scan of your library, trigger it via Navidrome's HTTP API:

curl -X POST "http://localhost:4533/api/scan" \
  -H "Authorization: Bearer <YOUR_ADMIN_TOKEN>"

Alternatively, you can restart the Docker container to force a full re-scan upon startup:

docker compose restart navidrome