Navidrome on Linux

Navidrome is a lightweight, self‑hosted music streaming server that lets you access your personal music collection from anywhere through a web interface or any Subsonic‑compatible mobile app.

🎵 What Navidrome Is

Navidrome is an open‑source personal music streaming service. It scans your local music library and serves it through:

It’s designed to be fast, minimal, and easy to run on almost any hardware.

⚡ Key Features

🚀 Docker Installation Steps

  1. Update your system
    sudo apt update && sudo apt upgrade -y
  2. Install Docker
    curl -fsSL https://get.docker.com -o get-docker.sh
    sudo sh get-docker.sh
  3. Install Docker Compose
    sudo apt install docker-compose-plugin
  4. Add your user to the Docker group
    sudo usermod -aG docker $USER
  5. Enable Docker at boot
    sudo systemctl enable docker
  6. Reboot
    sudo reboot
  7. Test Docker
    docker run hello-world

📁 Setup Navidrome

mkdir ~/docker
mkdir ~/docker/navidrome

🧾 Docker Compose File

In ~/docker/navidrome create docker-compose.yml

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    user: 1000:1000          # adjust if needed
    ports:
      - "4533:4533"
    volumes:
      - /mnt/Music:/music:ro             # /mnt/Music is the directory where you have all your music files
      - ~/docker/navidrome/data:/data
    environment:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: "http://<YOUR_IP_ADDRESS>:4533"
      ND_LASTFM_APIKEY: "LASTFM_APIKEY" # If you want to scrobble to your last.fm account, you need this and the SECRET
      ND_LASTFM_SECRET: "LASTFM_SECRET"
      ND_ENABLESCROBBLING: "true"
    restart: unless-stopped

To generate the Last.FM KEY and SECRET, browse to https://www.last.fm/api/account/create, fill out that page, and click Submit

▶️ Start Navidrome

From within ~/docker/navidrome run:

docker compose up

📜 View Logs

docker logs navidrome-agent

🌐 Access Web Interface

Open your browser and go to "http://<YOUR_IP_ADDRESS>:4533

You should see your music. If not, you can kick off a manual scan by clicking on Settings (in the upper right hand cornecr. The icon looks like a head and shoulders), then Libraries, then Quick Scan (or Full Scan).

There are a bunch of apps that you can instll on your phone to stream ... I use SubStreamer.

If you want to access this remotely, you can open port 4533 on your router ... just set it for YOUR_IP_ADDRESS. You can also use Tailscale (this is what I do). If using TailScale, you'll use the TailScale IP address of your system for ND_BASEUR and in whatever app you use on your phone.

Importing a playlist from Spotify

The first thing you want to do is export your Spoitfy playlist. I used Exportify. This exports your playlist into a csv file. Call it spotify_playlist.csv. You then need to convert it. Copy that file to your system where you're running Navidrome. Here's a script you can use to convert it to a m3u file (this is a playlist file). Save it as create_playlist.py. It does a pretty good job (at least it did for me). If it doesn't work for you as-is, you may need to tweak it. It's immpossible to create a script for everyone's environment when I only have access to mine.

import csv
import os
import sys
import re

# --- CONFIGURATION ---
MUSIC_DIR = "/mnt/Music"  
PLAYLIST_NAME = "Spotify_Playlist.m3u"
# ---------------------

def normalize(name):
    """
    Lowercase, remove non-alphanumeric characters for robust matching.
    """
    return re.sub(r'[^a-z0-9]', '', name.lower())

def create_m3u(csv_file):
    playlist_entries = []
    
    if not os.path.exists(csv_file):
        print(f"Error: Could not find {csv_file}")
        return

    with open(csv_file, mode='r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            track = row.get('Track Name') or row.get('Title', '').strip()
            artist = row.get('Artist Name(s)') or row.get('Artist', '').strip()
            
            if not track:
                continue

            # Clean names for searching
            clean_track = normalize(track.split(' - ')[0].split(' (')[0])
            clean_artist = normalize(artist)
            found_path = None

            # Walk through every subfolder in MUSIC_DIR
            for root, dirs, files in os.walk(MUSIC_DIR):
                # Optimization: check if the artist name is in the folder path
                # This prevents matching "Time" by Semisonic when looking for "Time" by Pink Floyd
                clean_root = normalize(root)
                
                for file in files:
                    if file.lower().endswith(('.mp3', '.flac', '.m4a', '.wav')):
                        clean_file = normalize(os.path.splitext(file)[0])
                        
                        # MATCH LOGIC:
                        # 1. Track name must be in the filename
                        # 2. Artist name must be in either the filename OR the folder path
                        if clean_track in clean_file:
                            if clean_artist in clean_file or clean_artist in clean_root:
                                found_path = os.path.join(root, file).replace(MUSIC_DIR, "/music")
                                break
                if found_path:
                    break
            
            if found_path:
                playlist_entries.append(f"#EXTINF:-1,{artist} - {track}\n{found_path}")
                print(f"✅ Found: {artist} - {track}")
            else:
                print(f"❌ Missing: {artist} - {track}")

    if playlist_entries:
        with open(PLAYLIST_NAME, "w", encoding='utf-8') as f:
            f.write("#EXTM3U\n" + "\n".join(playlist_entries))
        print(f"\n✅ Done! Created {PLAYLIST_NAME} with {len(playlist_entries)} tracks.")
    else:
        print("\n⚠️ No tracks found, playlist not created.")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 create_playlist.py ")
    else:
        create_m3u(sys.argv[1])

Run this by entering python3 create_playlist.py spotify_playlist.csv. (If you need to install Python, run sudo apt update;sudo apt install python3. If you don't want to install Python, you can paste the script into AI and ask it to convert it to abash script). It will print out a list of each file and if it was successfully mapped to a file in your library or not. You can manually fix the errors. You can also use AI to cheak if it worked. Copy the spotify_playlist.csv file and paste it into your favorite AI (I like Gemini) and say something like "I used this to create this" and paste the m3u file that was an output from the script. The AI will let you know anything that's wrong and you can manually fix the issues.

When you're done cleaning it up, copy the file to the root of your music directory (/mnt/Music in this example) and then kick off a scan in Navidrome. The playlist should show up in the left hand sidebar.

If you find my content useful, please consider supporting this page:

☕ Buy Me a Coffee