Self-Hosting Navidrome: Personal Music Streaming Server (Spotify Alternative)

Spotify costs $12/month, owns the relationship between you and your music, and can remove albums from your library without notice. If you’ve built a music collection over the years — ripped CDs, purchased albums from Bandcamp, curated FLAC files — Navidrome turns that collection into your own streaming service, accessible from any device, anywhere.

Navidrome is a lightweight, open-source music server written in Go. It scans your music library, reads metadata, serves a modern web interface, and speaks the Subsonic API — which means dozens of polished mobile and desktop apps work with it out of the box. It runs comfortably on a Raspberry Pi Zero, handles libraries with hundreds of thousands of tracks, and uses around 30-50MB of RAM for typical collections.

Unlike Plex or Jellyfin, which are general-purpose media servers that happen to play music, Navidrome is purpose-built for music. It handles compilations, box sets, multi-disc albums, and various artist albums correctly. It supports per-user play counts, playlists, favorites, ratings, and scrobbling to Last.fm and ListenBrainz.

FeatureNavidromeJellyfinPlexAirsonicFunkwhale
Open source✅ GPL-3.0✅ GPL-2.0❌ Freemium✅ GPL-3.0✅ AGPL-3.0
PurposeMusic-onlyAll mediaAll mediaMusic-onlyMusic + social
RAM usage~30-50MB~500MB+~300MB+~512MB+~200MB+
Subsonic API✅ Full⚠️ Plugin✅ Full✅ Partial
Mobile apps✅ 20+ via Subsonic✅ Native✅ Native✅ Via Subsonic✅ Native
Transcoding✅ On-the-fly
Multi-user
Multi-library
Scrobbling✅ Last.fm/ListenBrainz⚠️ Plugin✅ Last.fm
Smart playlists✅ Paid
ReplayGain⚠️ Limited
Jukebox mode

Prerequisites

  • A Linux server with Docker and Docker Compose installed
  • A music library with properly tagged files (MP3, FLAC, OGG, Opus, AAC, WMA, etc.)
  • 256MB RAM minimum (Navidrome itself uses ~30-50MB)
  • A domain name and reverse proxy for remote access (optional for LAN-only use)

Directory Structure

navidddroaoctmkaee//r-compose.yml#Navidromedatabase,cache,andconfig

Your music library stays where it is — Navidrome mounts it read-only.

Create the project directory:

mkdir -p navidrome/data
cd navidrome

Docker Compose Setup

Create docker-compose.yml:

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    user: "1000:1000"
    ports:
      - "4533:4533"
    volumes:
      - ./data:/data
      - /path/to/your/music:/music:ro
    environment:
      ND_SCANSCHEDULE: "1h"
      ND_LOGLEVEL: "info"
      ND_SESSIONTIMEOUT: "24h"
      ND_ENABLESHARING: "true"
      ND_ENABLEDOWNLOADS: "true"
    restart: unless-stopped

Important: Replace /path/to/your/music with the actual path to your music library. The user directive should match the UID:GID of your music files’ owner to avoid permission issues. Check with ls -ln /path/to/your/music.

The :ro flag mounts the music directory read-only — Navidrome never modifies your original files.

Start the container:

docker compose up -d

Open http://your-server-ip:4533 in your browser. On first launch, you’ll be prompted to create an admin account. After that, Navidrome immediately begins scanning your music library.

Essential Configuration

Navidrome is configured through environment variables (prefixed with ND_) or a TOML config file. Here are the most useful options:

environment:
  # Library scanning
  ND_SCANSCHEDULE: "1h"              # How often to check for new music (0 to disable)
  ND_LOGLEVEL: "info"                # debug, info, warn, error

  # User experience
  ND_SESSIONTIMEOUT: "720h"          # How long sessions last (30 days)
  ND_DEFAULTTHEME: "Dark"            # Dark or Light
  ND_DEFAULTLANGUAGE: "en"           # UI language
  ND_DEFAULTUIVOLUME: "80"           # Default player volume (0-100)

  # Features
  ND_ENABLESHARING: "true"           # Allow public share links
  ND_ENABLEDOWNLOADS: "true"         # Allow downloading from web UI
  ND_ENABLEFAVOURITES: "true"        # Heart/star tracks
  ND_ENABLESTARRATING: "true"        # 5-star ratings
  ND_ENABLEREPLAYGAIN: "true"        # Volume normalization

  # Transcoding
  ND_DEFAULTDOWNSAMPLINGFORMAT: "opus"  # Format for transcoded streams

  # External services
  ND_ENABLEEXTERNALSERVICES: "true"  # Enable Last.fm, Spotify art, etc.

  # Security
  ND_AUTHREQUESTLIMIT: 5            # Max login attempts per window
  ND_AUTHWINDOWLENGTH: "20s"        # Rate limit window

For a config file approach, create data/navidrome.toml:

ScanSchedule = "1h"
SessionTimeout = "720h"
EnableSharing = true

And add ND_CONFIGFILE=/data/navidrome.toml to your environment.

Music Library Organization

Navidrome reads metadata from your audio files’ tags, not folder structure. That said, a clean folder structure helps:

/musiAVScrao/truiinsAAoCdMtlnuotobosmrvNutpaiam00chAicem12oerlkeNvrtasO/a--eit/SmrAsiTeTT.ltorrjbsn((aapu/YYccgmNeekkaaa(mrrTTYe))iie//tta(llrYee)e../affrll)aa/cc

Tagging matters more than folders. Use a tagger like MusicBrainz Picard, beets, or Kid3 to ensure your files have correct artist, album, title, track number, year, and genre tags. Navidrome reads embedded cover art and also looks for cover.*, folder.*, and front.* image files in album directories.

For multi-disc albums, use the disc number tag. Navidrome handles these natively — you don’t need separate folders per disc (though it works either way).

Multi-User Setup

Add users through the web UI under Settings → Users. Each user gets:

  • Independent play history and play counts
  • Personal playlists and favorites
  • Individual transcoding preferences
  • Separate Last.fm/ListenBrainz scrobbling accounts
  • Per-user library access (multi-library setups)

This makes Navidrome great for households — everyone gets their own experience without interfering with each other’s data.

Multi-Library Support

Navidrome supports multiple music libraries with per-user access controls. Mount additional volumes:

volumes:
  - ./data:/data
  - /path/to/main-library:/music:ro
  - /path/to/kids-library:/music-kids:ro
  - /path/to/classical:/music-classical:ro

Configure libraries in the admin panel and assign which users can access which libraries.

Mobile and Desktop Apps

Navidrome’s killer feature is Subsonic API compatibility. This opens up a huge ecosystem of polished music players:

iOS: Substreamer, play:Sub, Amperfy, iSub — all connect to Navidrome as a Subsonic server. Substreamer and Amperfy are particularly well-regarded for offline caching and CarPlay support.

Android: Subtracks, Symfonium (paid, excellent), DSub, Ultrasonic — Symfonium is widely considered the best Android Subsonic client with material design, Android Auto, and Chromecast support.

Desktop: Sonixd/Feishin (cross-platform Electron app with a Spotify-like UI), Sublime Music (Linux), Supersonic (cross-platform).

Web: Navidrome’s built-in web player works well, but you can also use Airsonic Refix or Subplayer as alternative web frontends.

To connect any Subsonic client, use:

  • Server URL: http://your-server:4533 (or your reverse proxy URL)
  • Username/Password: Your Navidrome credentials

Last.fm and ListenBrainz Scrobbling

Enable scrobbling so your listening history follows you:

Last.fm: In the Navidrome web UI, go to your user settings and link your Last.fm account. Navidrome handles the OAuth flow — click the link, authorize on Last.fm, and you’re connected.

ListenBrainz: Similarly, add your ListenBrainz API token in user settings. This also works with self-hosted Maloja instances by entering your Maloja URL as a custom ListenBrainz endpoint.

Spotify and Deezer (artist images): Navidrome can fetch artist images from Spotify and Deezer to enrich your library. Configure API keys in the environment:

environment:
  ND_SPOTIFY_ID: "your-spotify-client-id"
  ND_SPOTIFY_SECRET: "your-spotify-client-secret"

Create a Spotify developer app at developer.spotify.com — it’s free and only used for fetching metadata, not streaming.

Transcoding

Navidrome transcodes audio on the fly when a client requests a lower bitrate. This is essential for mobile streaming over cellular data.

Default transcoding profiles are built in — if a client requests 128kbps, Navidrome will transcode your FLAC files to Opus (or MP3, depending on client support) at that bitrate automatically.

Each user can configure their own transcoding preferences:

  • Wi-Fi: Original quality (no transcoding)
  • Cellular: 128kbps Opus or MP3
  • Offline cache: Choose quality per download

The default downsampling format is Opus, which gives excellent quality at lower bitrates. Change it with ND_DEFAULTDOWNSAMPLINGFORMAT if your clients don’t support Opus.

Navidrome requires ffmpeg for transcoding, which is included in the Docker image.

Smart Playlists

Navidrome supports smart/dynamic playlists using a JSON-based rule system. Create playlists that automatically update based on criteria like:

  • Recently played tracks
  • Highest rated albums
  • Genre + year combinations
  • Tracks never played
  • Most played this month

Smart playlists are created through the web UI and update dynamically as your library and listening habits change.

Reverse Proxy Configuration

Caddy

m}usicr.eevxearmspel_ep.rcooxmy{localhost:4533

Nginx

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

    location / {
        proxy_pass http://localhost: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;

        # Important for streaming large files
        proxy_buffering off;
        proxy_request_buffering off;
    }
}

Disabling proxy_buffering is important for music streaming — you want audio data to flow through immediately rather than being buffered by Nginx.

Backup and Restore

Navidrome stores its database (SQLite) and cache in the /data volume. Your music files aren’t modified, so you only need to back up the data directory:

#!/bin/bash
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"

# Stop for consistent SQLite backup
docker compose stop navidrome

# Backup data directory
tar czf "$BACKUP_DIR/navidrome-$DATE.tar.gz" data/

# Restart
docker compose start navidrome

# Keep last 10 backups
ls -t "$BACKUP_DIR"/navidrome-*.tar.gz | tail -n +11 | xargs -r rm
echo "Backup complete: navidrome-$DATE.tar.gz"

Navidrome also has built-in backup support. Configure automatic backups:

environment:
  ND_BACKUP_PATH: "/data/backups"
  ND_BACKUP_SCHEDULE: "0 2 * * *"    # Daily at 2 AM
  ND_BACKUP_COUNT: 7                  # Keep 7 backups

To restore, extract the backup tarball and restart the container. Navidrome will pick up the existing database automatically. If restoring the built-in backups, check the /data/backups directory.

Troubleshooting

Library scan finds no files — Check that the music volume is mounted correctly and the user directive matches the file ownership. Run docker exec navidrome ls /music to verify files are visible inside the container. Ensure your audio files have recognized extensions (.mp3, .flac, .ogg, etc.).

Album art not showing — Navidrome looks for embedded cover art first, then cover.*, folder.*, and front.* files in the album directory. Ensure images are JPEG or PNG format. After adding art, trigger a full rescan from the admin panel. Clear the image cache in /data/cache/ if stale art persists.

Transcoding fails or sounds garbled — The Docker image includes ffmpeg, but verify it’s working: docker exec navidrome ffmpeg -version. If you’re getting errors in logs, check that the transcoding profile format is supported by your client.

Subsonic client can’t connect — Use the full URL including the port (e.g., https://music.example.com). Some older clients need the legacy Subsonic authentication mode — check Navidrome’s compatibility notes. Ensure the client is using the correct username and password (not an API key).

High CPU during scan — Initial library scans are CPU-intensive, especially for large collections. Subsequent scans are incremental and much faster. If it’s a problem, set ND_SCANSCHEDULE=0 and trigger scans manually.

Compilation albums split across multiple entries — This is a tagging issue. Ensure compilation tracks have the Album Artist tag set to “Various Artists” (or a consistent value) and each track’s Artist tag set to the individual artist. MusicBrainz Picard handles this correctly by default.

Shares/public links not working — Enable sharing with ND_ENABLESHARING=true and restart. Share links include an expiration (configurable with ND_DEFAULTSHAREEXPIRATION). Ensure your reverse proxy passes the correct X-Forwarded-Proto header so generated URLs use HTTPS.

Power User Tips

  • Jukebox mode — Play music on speakers connected directly to the server. Enable in settings and control playback from the web UI or any Subsonic client. Great for whole-house audio without casting.

  • Internet radio — Add internet radio station URLs through the web UI. They’re accessible from Subsonic clients alongside your local library.

  • beets integration — Use beets as your music tagger/organizer and point Navidrome at the beets library directory. beets handles tagging, duplicate detection, and format conversion; Navidrome handles streaming.

  • Monitoring — Watch docker logs navidrome for scanning activity and errors. Navidrome logs are structured and easy to parse. Set ND_LOGLEVEL=debug temporarily for troubleshooting.

  • External authentication — Navidrome supports external auth via reverse proxy headers, letting you use Authentik or Authelia for SSO across your homelab.

  • Playlist import — Drop .m3u files into your music library folders. Navidrome auto-imports them and keeps them in sync. Great for migrating playlists from other players.

  • Album ratings — Rate albums and artists, not just tracks. These persist per-user and sync across all Subsonic clients that support the rating API.

  • Docker networking — Run Navidrome on an internal network accessible only through your reverse proxy for better security, especially with authentication disabled on the Navidrome side.

Conclusion

Navidrome is the best self-hosted music streaming server for people who care about their music collection. It’s fast, lightweight, handles large libraries gracefully, and works with every Subsonic-compatible app — which means excellent mobile experiences on both iOS and Android without any custom development.

Set up the Docker container, point it at your music library, install a Subsonic client on your phone, and you’ve replaced Spotify for music you own. Add Last.fm scrobbling, configure transcoding for cellular data, and share playlists with family members who each get their own accounts and preferences.

The resource usage is remarkably low — if you’re already running a homelab, Navidrome adds almost nothing to your server’s load while giving you something you’ll actually use every day.