Skip to content

Navidrome -- Setup

Self-hosted music streaming server with OpenSubsonic API, paired with Yubal for YouTube Music library sync. Runs as a Docker Compose stack on a dedicated Debian LXC (132). No SSO -- mobile apps authenticate via Subsonic API tokens. Music stored on unohana.

  • GitHub: https://github.com/navidrome/navidrome
  • Website: https://www.navidrome.org
  • Docs: https://www.navidrome.org/docs
  • API (OpenSubsonic): https://opensubsonic.netlify.app
  • Yubal GitHub: https://github.com/guillevc/yubal

Infrastructure

Host LXC ID Internal External CPU RAM Disk
Debian LXC 132 192.168.1.132:4533 https://music.eva-00.network 2 cores 2 GiB 4 GiB

Observability

Logs

Container logs are collected via Docker log driver. Query in Loki:

Query Purpose
{container_name="navidrome"} Navidrome server logs
{container_name="yubal"} Yubal YouTube Music sync logs
{container_name=~"navidrome\|yubal"} All music stack logs
{container_name="navidrome"} \|= "error" Navidrome errors
{job="navidrome"} All containers on this LXC

Access: Grafana > Explore > Loki > Enter query

IaC

Artifact Path
Playbook ansible/playbooks/navidrome.yml
Workflow .forgejo/workflows/navidrome.yml
Docker Compose services/navidrome/docker-compose.yml
Navidrome config services/navidrome/navidrome.toml
Caddy entry services/caddy/Caddyfile > music.eva-00.network
Glance entry services/glance/glance.yml > Media section + monitor
Custom image homelab/navidrome-custom repo on Forgejo (Dockerfile + themes)

The playbook manages the full lifecycle:

  1. Provisions LXC 132 on Proxmox (via create-lxc.yml)
  2. Installs Docker
  3. Fetches secrets from Vault
  4. Deploys Navidrome + Yubal via docker-compose
  5. Health checks Navidrome at /ping
  6. Deploys Alloy for log shipping

Vault Secrets

Path Key Purpose
secret/navidrome admin_password Navidrome admin (holo) password
secret/navidrome milton_password Navidrome user (milton) password
secret/navidrome lastfm_api_key Last.fm API key for scrobbling + metadata
secret/navidrome lastfm_secret Last.fm API secret

Storage

Music files are stored on unohana (sdc, 4TB) and bind-mounted into LXC 132:

Host Path LXC Path Access Purpose
/mnt/all-might/music /music Read-write Music library (shared with Yubal)

Host directory must be owned by 101000:101000 (maps to Yubal's UID 1000 inside the unprivileged LXC). The playbook handles this automatically.

Navidrome reads music from /music (read-only in container). Yubal writes downloaded music to /music (read-write). Navidrome rescans every 15 minutes.

Navidrome's own data (SQLite DB, cover art cache, metadata) is stored at /opt/navidrome/data/ in the LXC rootfs.

Authentication

No SSO. Navidrome rejected OIDC support (GitHub #858). Subsonic API authentication (used by all mobile apps) requires username/password tokens that bypass reverse proxy auth.

  • Web UI: Access via https://music.eva-00.network, log in with Navidrome credentials
  • Mobile apps: Connect using Subsonic API at https://music.eva-00.network with username/password
  • Security: Caddy reverse proxy provides TLS. Access restricted via network (NetBird VPN for remote)

Users

The playbook automatically creates two users on first deploy:

Username Role Purpose
holo Admin Primary admin account
milton User Regular user account

Passwords are stored in Vault at secret/navidrome.

Containers

Setting Value
Image git.eva-00.network/homelab/navidrome-custom:latest
Port 4533
Data /opt/navidrome/data
Config /opt/navidrome/navidrome.toml
Music /music (read-only bind mount from unohana)
Scan schedule Every 15 minutes
Scrobbling Last.fm + ListenBrainz
Metadata Last.fm, Spotify, Deezer (artist bios, cover art)

Yubal

Setting Value
Image ghcr.io/guillevc/yubal:latest
Port 8000
Config /opt/yubal/config
Output /music (read-write bind mount from unohana)
Sync schedule Every 6 hours (0 */6 * * *)
Audio format Opus

Yubal bridges YouTube Music with Navidrome:

  • Downloads albums/playlists from YouTube Music via ytmusicapi + yt-dlp
  • Tags files with metadata and downloads synced .lrc lyrics
  • Organizes into Artist/Year-Album/Track folder structure
  • Scheduled sync keeps subscribed playlists up to date
  • Browser extension (Firefox/Chrome) for one-click downloads

Custom Image & Themes

Navidrome themes are Material-UI JavaScript objects compiled into the React bundle -- there is no runtime theme injection. To add custom themes, we build a custom Docker image from upstream source with additional themes injected.

Repository: homelab/navidrome-custom on Forgejo

How it works

  1. Dockerfile clones upstream Navidrome source
  2. Custom theme files (in themes/) are copied into ui/src/themes/custom/
  3. Themes are registered in ui/src/themes/index.js via sed
  4. Full build: Node.js (UI bundle) + Go (binary with embedded UI)
  5. Image pushed to Forgejo container registry at git.eva-00.network/homelab/navidrome-custom:latest

Custom themes

Theme Type Description
AMusic Light Blue Light AMusic theme converted to light mode with Apple blue (#007AFF)
Apple Music Light Clean light theme imitating the Apple Music desktop app

Updating

The build workflow runs on push to homelab/navidrome-custom main branch. To rebuild with a specific upstream version:

# Dispatch workflow with version input
# e.g. navidrome_version=0.61.2 (or "latest" for HEAD)

To add more themes: add .js and .css.js files to themes/, the Dockerfile auto-registers them.

Build infrastructure

The image builds on the Forgejo runner (LXC 101). Docker was enabled by adding nesting support to the LXC and bumping memory to 4GB for the Go + Node.js compilation.

iOS Clients

Connect any Subsonic-compatible iOS app to https://music.eva-00.network:

App Price CarPlay Gapless Offline
play:Sub $4.99 Yes Yes Yes
Arpeggi Free (beta) Yes Yes Yes
Narjo Free (beta) Yes Yes Yes
Amperfy Free Yes No (FLAC) Yes

Scrobbling

Last.fm

  1. Register API key at https://www.last.fm/api/account/create
  2. Store in Vault: vault-write workflow with path navidrome, data {"lastfm_api_key": "...", "lastfm_secret": "..."}
  3. Redeploy to pick up new env vars
  4. Link Last.fm account in Navidrome web UI: Settings > Last.fm

ListenBrainz

  1. Create account at https://listenbrainz.org
  2. Get user token from ListenBrainz settings
  3. Add in Navidrome web UI: Settings > ListenBrainz

Backup

  • LXC rootfs (Navidrome DB, playlists, favorites, cover art cache, Yubal config): PBS automatic backup
  • Music files on unohana: Part of sazabi 18TB offline rsync (user-managed)
  • Vault secrets: Backed up with Vault storage backend