Self-hosted music discovery for Navidrome. Search and stream songs you don't own. Heart what you like — Octo grabs the FLAC and adds it to your library forever.
If you self-host your music with Navidrome (or any Subsonic-compatible server), you've already opted out of streaming-service lock-in. The downside: your library is only as interesting as the music you've already collected. Searching for something new just gets you "no results."
Octo is for people who want both — your music on your hardware, plus a working discovery engine that lets you preview anything for free and keep only what you decide you want.
Built for:
- Self-hosters running Navidrome who miss Spotify-style discovery.
- Music nerds who want full FLAC quality, not 320kbps streaming.
- Subsonic app users (Feishin, Arpeggi, Narjo) who want their existing apps to suddenly be smarter.
- People canceling Spotify / Apple Music / Tidal who need a real replacement, not "well, I'll just listen to less music."
- Plexamp / Roon refugees who like the discovery features but don't want the proprietary stack.
Note: if you already pay for Qobuz, Deezer, or Yandex Music and want a Subsonic frontend that ingests your paid catalog into your library, V1ck3s/octo-fiesta is closer to what you want — it downloads from those APIs directly. Octo is the no-paid-streaming-required path: previews come from YouTube, downloads come from Soulseek.
- Search finds music you don't own. Tap a result to hear it instantly via YouTube preview.
- Radio works on every song. Owned tracks play at full FLAC; missing ones preview from YouTube.
- Heart to keep. Star a previewed song and Octo grabs the FLAC from Soulseek, adds it to your library, and tells Navidrome to rescan. Within a minute, the song is yours forever.
Plug Octo in front of your Navidrome. Point your Subsonic apps (Feishin, Arpeggi, Narjo, etc.) at Octo instead. Nothing else changes.
You need:
- A box with Docker installed.
- An existing Navidrome server.
- A free Last.fm API key.
- A free Soulseek account.
Then:
git clone https://github.com/winters27/octo.git
cd octo
./install.shThe installer asks for those four things, brings the stack up, and prints the address.
When it's done:
- Point your Subsonic apps at
http://<your-host>:5274. - Open the admin dashboard at
http://<your-host>:5274/adminto manage every setting from the browser — no editing config files by hand.
| Works | App |
|---|---|
| ✅ | Feishin (desktop) |
| ✅ | Arpeggi (iOS) |
| ✅ | Narjo (iOS) |
| ✅ | most other Subsonic apps |
| ❌ | Symfonium — offline-first, doesn't query the server for searches |
git pull && ./install.shRe-running the installer keeps your existing answers.
http://<your-host>:5274/admin
Every setting has a form, every backing service has a live status indicator, and the Raw Config tab lets you edit the whole effective configuration as a JSON file if you'd rather work that way. Changes hot-reload — no rebuild, no restart for most settings.
It's the discovery half. Octo doesn't replace your music server — that's still Navidrome — but it adds the search-and-listen-to-anything experience that streaming services do well. With Octo plugged in, your Subsonic app behaves more like Spotify or Apple Music: search returns recommendations, radio works on any song, and you can preview tracks you don't own. The difference is that "I want to keep this" downloads it as a real FLAC into your library, instead of renting it.
No. Octo speaks the Subsonic API, not the Plex API. If you're a Plex user looking for self-hosted alternatives with discovery, the move is Navidrome + Octo + a Subsonic client like Feishin or Arpeggi.
Navidrome's radio plays songs from your existing library. Octo's radio reaches outside your library — Last.fm finds similar tracks, YouTube provides the preview, and Soulseek provides the keep-it-forever path. Navidrome alone gives you a great library player; Octo turns that library into a launchpad for discovery.
No. Octo runs entirely on your hardware. It calls Last.fm (for similar-tracks data), YouTube via yt-dlp (for audio previews), and Soulseek peers (for downloads). Those are outbound queries — nothing about your library or listening history is shipped anywhere.
Yes. Soulseek peers share full FLAC files with their existing ID3 tags intact. Octo organizes them per your FolderStructure setting (Flat or Organized), then triggers a Navidrome rescan so they appear in your library exactly like everything else you own.
Set Subsonic__DownloadOnStar=false in .env. Hearting a song will still register the favorite, but won't trigger any download. You'll keep search and radio enrichment via YouTube preview — useful if you only want the discovery layer and prefer to acquire FLACs another way.
Yes — multi-arch images are published for amd64 and arm64. The yt-dlp sidecar does most of the CPU work; a Pi 4 or Pi 5 handles a single household's listening fine.
Why is Octo a refactor of octo-radiostarr?
The earlier project leaned on SquidWTF (a public TIDAL proxy) for streaming. In April 2026 Tidal hardened their API and broke every TIDAL proxy at once. Rather than patch around it, Octo was rebuilt on two sources that don't depend on a single fragile vendor API — YouTube via yt-dlp, and Soulseek via slskd. The old repo is archived; new development happens here.
How is Octo different from octo-fiesta?
Octo's earliest commits descended from V1ck3s/octo-fiesta (via bransoned/octo-fiestarr), so the concept is the same: a Subsonic proxy that fills in songs you don't own. The implementation has diverged completely:
- Octo-fiesta's model: when you play an unowned song, it hits the Qobuz / Deezer / Yandex API with your paid streaming credentials, decrypts the audio, and writes the FLAC to disk permanently. Every play = a downloaded file. Excellent if you have a paid streaming sub and want a unified Subsonic UX over your subscription catalog.
- Octo's model: when you play an unowned song, you get a YouTube preview with zero disk impact. If you decide you want to keep it, you star it and Octo grabs the FLAC from Soulseek peers. Preview is free, ownership is opt-in.
Different audience. If you pay for streaming and want every play to enrich your library, octo-fiesta is the right tool. If you don't pay for streaming and want discovery + selective FLAC ownership, Octo is the right tool.
Other practical differences in Octo: a real admin UI, multi-peer Soulseek retry, HTTP Range support for iOS clients, Last.fm-driven discovery and radio, an interactive installer.
Advanced — architecture, technical details, more FAQ
Octo is a full refactor of octo-radiostarr. That earlier project ran on SquidWTF + Tidal and broke when Tidal hardened their API in April 2026. Octo pivots to YouTube via yt-dlp for previews and Soulseek via slskd for downloads — neither of which depends on a single fragile public API.
Three Docker containers in one docker compose stack:
┌──────────────────────────┐ ┌──────────────────┐
│ Subsonic clients │────────▶│ octo │──▶ Navidrome
│ (Feishin, Arpeggi, …) │ │ (port 5274) │ (your library)
└──────────────────────────┘ └──┬───────────┬───┘
│ │
┌─────────▼──┐ ┌───▼─────┐
│ yt-dlp shim│ │ slskd │
│ sidecar │ │ Soulseek│
└────────────┘ └─────────┘
octo(port 5274) — the proxy + admin UI. Hijacks the Subsonic endpoints that need enrichment (search3,getSimilarSongs2,stream,getCoverArt,star,scrobble,getTranscodeDecision); passes everything else through to Navidrome unchanged.yt-dlp-shim(internal) — wrapsyt-dlpbehind two HTTP endpoints. Process-isolation keeps yt-dlp's frequent extractor breakage from affecting the rest of the stack.slskd(port 5030) — Soulseek client with REST API. Octo authenticates and queues downloads.
Navidrome is not part of the stack — Octo just talks to whatever Navidrome you already have.
Octo reads from three sources, highest priority first:
settings.json(admin UI writes here, hot-reloads in ~500ms).- Environment variables in
.env/docker-compose.yml. appsettings.jsonshipped with the image.
The admin UI's "Config sources" tab shows the merged effective value for every key.
Stream(default) — preview-only. Heart a song to download.Permanent— every song you play gets downloaded.Cache— downloads expire afterCacheDurationHours.
Flat(default) —Artist - Title.flac.Organized—Artist/Title/file.flac.
Octo hijacks these endpoints; everything else proxies to Navidrome unchanged:
| Endpoint | Why |
|---|---|
search3 |
merge local + Last.fm-driven external results |
getSimilarSongs2 |
radio queue with local-first preference |
stream |
YouTube proxy with Range support, mp4/m4a passthrough |
getCoverArt |
Deezer → iTunes → Last.fm aggregator with Octo watermark |
star |
trigger Soulseek download (multi-peer retry, FLAC) |
scrobble |
sliding-window prewarm of next 8 in queue |
getTranscodeDecision |
OpenSubsonic — return direct-play for Octo IDs |
When a song is starred, Octo:
- Searches Soulseek for
<artist> <title>(cleaned of[brackets]and redundantArtist -prefixes). - Falls back to title-only search if the first query returns nothing usable.
- Ranks candidates by queue depth, upload speed, file size.
- Tries the top 5 peers in sequence with a 60s per-peer timeout.
- Verifies the file landed on disk (slskd's polling endpoint sometimes drops successful transfers between polls).
- Renames per
FolderStructuresetting and triggers a Navidrome rescan.
Around 30–50% of Soulseek peer requests get rejected ("overwhelmed", queue full, banned). Single-peer-try downloads were too fragile — multi-peer is the difference between "downloads sometimes work" and "downloads reliably work."
Three sources tried in order; first hit wins:
- Deezer — broad international catalog, picks 1000×1000 covers.
- iTunes — limit=5, scored by artist match (avoids "Karaoke Version" hits).
- Last.fm — track-level images, skips the deprecated artist-image placeholder.
Cached cross-source so a queue scroll doesn't trigger N external API calls per visible song.
Do downloaded songs get tagged?
Yes — slskd downloads are full FLACs from peer libraries that already have ID3 tags. Octo organizes them per FolderStructure, then triggers a Navidrome rescan.
What if all 5 Soulseek peers reject? Octo throws an error and the star icon stays filled. Try again later or grab the file by hand. Real failures are rare.
Can it run without Soulseek?
Yes — set Subsonic__DownloadOnStar=false. Star fills the heart but won't trigger a download. Search and radio enrichment still work.
Can it run without Last.fm? Yes, but search and radio fall back to local-only — no discovery layer. The free Last.fm key takes 30 seconds.
dotnet restore
dotnet build
dotnet testProject layout:
| Path | What's there |
|---|---|
octo/Controllers/ |
Subsonic API surface, admin API |
octo/Services/Soulseek/ |
slskd client, multi-peer download logic |
octo/Services/YouTube/ |
shim HTTP client |
octo/Services/CoverArt/ |
Deezer / iTunes / Last.fm aggregator |
octo/Services/Subsonic/ |
request parsing, response building |
octo/Services/Admin/ |
settings file writer (atomic, deep-merge) |
octo/wwwroot/admin/ |
the admin UI (vanilla JS, hand-rolled CSS, no build step) |
yt-dlp-shim/ |
Python/Flask sidecar (~200 lines) |
- Navidrome — the music server Octo proxies.
- slskd — Soulseek with a REST API.
- yt-dlp — makes YouTube preview feasible.
- Last.fm — similar-tracks API.
- V1ck3s/octo-fiesta — the upstream root of this lineage. The Qobuz/Deezer/Yandex Subsonic-proxy concept that Octo eventually rebuilt around YouTube + Soulseek started here.
- bransoned/octo-fiestarr — the intermediate fork of octo-fiesta whose codebase Octo's earliest commits descended from.