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.
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.
sudo apt update && sudo apt upgrade -ycurl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh
sudo apt install docker-compose-pluginsudo usermod -aG docker $USERsudo systemctl enable dockersudo rebootdocker run hello-worldmkdir ~/docker mkdir ~/docker/navidrome
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
From within ~/docker/navidrome run:
docker compose up
docker logs navidrome-agent
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.
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: