Self-hosted service
terry90/soulbeet avatar
terry90/soulbeet

Soulbeet glues slskd, beets and Navidrome into a self-hosted music pipeline

Automated music downloader and manager integrating Slskd and Beets.

654 stars28 forksRustMIT

At a glance

What is it?
Soulbeet is a Rust application that finds music on Soulseek through slskd, tags it with beets, and can turn your Last.fm or ListenBrainz history into playlists in Navidrome. The Docker Compose file is the install, and the volume mappings are the part to get right.
Who is it for?
Adopt Soulbeet if you run a self-hosted music stack, already have Navidrome and slskd in mind, and want discovery and library cleanup driven by your own listening history rather than manual imports. Do not adopt it if you only want a tagger, because beets alone does that job without a second service, and do not adopt it without reading the Rate and Keep rules, since unrated downloads expire and one star deletes a file.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three tools, one pipeline

Soulbeet is a self-hosted music downloader, library manager and discovery engine, written in Rust and released under the MIT licence. The architecture is a chain of well known pieces, and the documentation is explicit about the order. Soulbeet finds the best source on Soulseek, downloads it through slskd, tags and organises it with beets, and puts it in your library. Navidrome then picks up the new files and makes them streamable. For the discovery side of the product, Last.fm or ListenBrainz feed your listening history into the recommendation engine, which finds new tracks, downloads them and pushes playlists to Navidrome. That makes the role of each component clear. slskd is the transport and the search surface, beets is the tagger and organiser, Navidrome is the server, and Soulbeet is the orchestrator that decides what to fetch and what to throw away.

The compose file is the installation, and the volumes are the trap

The recommended way to run Soulbeet is Docker Compose, and the reason given is that it ensures all dependencies such as beets and python are correctly set up. The image supports both AMD64 and ARM64 architectures, and the only prerequisite listed is Docker and Docker Compose, or podman-compose. The compose file in the documentation starts like this:

yaml
services:
  soulbeet:
    image: docker.io/docccccc/soulbeet:latest
    restart: unless-stopped
    ports:
      - 9765:9765
    environment:
      - DATABASE_URL=sqlite:/data/soulbeet.db
      - DOWNLOAD_PATH=/downloads
      - SECRET_KEY=change-me-in-production
      - NAVIDROME_URL=http://navidrome:4533

Two constraints are stated in bold in the documentation and they decide whether the setup works. The /downloads volume must match between slskd and soulbeet, so Soulbeet can see the files slskd downloaded, and the /music volume must match between soulbeet and navidrome, so Navidrome can see the organised library. There is also a version floor: Soulbeet requires slskd 0.26 or newer, because downloads are enqueued through the batches API introduced in that version. Once the file is in place, the start is one command:

bash
docker-compose up -d

The compose file also defines navidrome on port 4533 with a music folder mapping, and slskd on port 5030 with remote configuration enabled, so the three services can be run together or pointed at existing ones.

Logging in depends on whether Navidrome is wired up

The first run starts at http://localhost:9765, and what happens at the login screen depends on one environment variable. With Navidrome configured, that is with NAVIDROME_URL set, you use your Navidrome credentials and your account is created on first login. Without Navidrome you log in as admin with the password admin, and then you are told to change that password or create your own account in Settings, Users. There is a fallback worth knowing about. Local passwords always work, and if Navidrome rejects your credentials or cannot be reached, Soulbeet checks the password stored in its own database, which is updated to your Navidrome password on every successful Navidrome login. After login, the setup sequence is short: in Settings, Config you connect slskd with a URL and an API key, in Settings, Library you add your music folders, and then you search for something and download it. The slskd URL and API key are stored in the database and configured through the web interface, while scrobble credentials, meaning the Last.fm API key and the ListenBrainz token, are configured per user in Settings, Library.

Discovery reads your scrobbles, not your library

The discovery engine is the part that makes Soulbeet more than a downloader with a UI. It analyses your Last.fm and ListenBrainz history and finds new music through track similarity, artist exploration, collaborative filtering and genre discovery, downloads the best candidates, and pushes playlists to your Navidrome server, and the documentation describes this as fully automatic. There are three discovery profiles, and the choice is about how far the engine strays from what you already like. Conservative stays close to what you know, Balanced sits in the middle, and Adventurous pushes into unfamiliar territory. You can run one or all three, each with its own playlist, which means you can keep a safe lane and an experimental one running side by side. Discovery is optional and configured in Settings, Library, where you add the Last.fm API key or the ListenBrainz token, enable discovery on a folder, pick your profiles and generate.

Rate and Keep will delete files you did not rate

The feature that most deserves a careful read is Rate and Keep, because it deletes data. The rule is that you listen in Navidrome, three or more stars promotes a track to your permanent library, one star deletes it, and unrated tracks expire and get replaced with fresh picks. Combined with discovery running on a schedule, that means your library is managed by your own rating behaviour, and anything you skip is treated as unwanted. The documentation is candid about the consequence of the design, and the environment variable table records the practical limit as well: without NAVIDROME_URL you log in with local accounts and discovery playlists, rating sync and auto-delete are unavailable. In other words the rating feedback loop only exists when Navidrome is in the picture, and the same variable that makes the feature possible also makes it dangerous. If you want to try the discovery engine without risking your library, the settings are where to turn the automatic behaviour off before you generate anything.

Beets does the tagging, with a fixed set of import flags

The import step is beets, and the documentation lists the exact flags used by default. Quiet mode is -q, so no user interaction. Singleton mode is -s, which is the default behaviour unless BEETS_ALBUM_MODE is set, and album mode is the alternative. The library database path is passed with -l and is per folder, and -d sets the target path to the specific folder selected in the web interface. The important consequence is that beets is not driven by a configuration you own unless you provide one. You can mount a custom config.yaml to /config/config.yaml, or to wherever you point BEETS_CONFIG, to customise how beets behaves, including plugins and naming formats. The repository also carries a beets_plugins/ directory and the compose file documents a drop-in mount for pure Python beets plugins, so the extension path is a mounted folder rather than a fork. Metadata sources are selectable per user, with MusicBrainz used for albums and Last.fm for single tracks, which is the sensible split when you import whole releases but also grab loose tracks.

A Rust workspace, a Dioxus front end and a two stage image

The build is where you can see how the project is put together. The Rust workspace lists ui, web, api and lib/soulbeet as members, with desktop and mobile commented out, and depends on Dioxus 0.7.4, which is the UI framework. The Dockerfile builds in stages: a Rust 1.91 bookworm builder that installs Node 22, with a comment explaining that the distro Node 18 is too old for Tailwind v4 oxide, then installs the Dioxus CLI and bundles the web package with a release build, followed by a separate python:3.11-slim-bookworm stage that provides the beets runtime. The image also takes a TIER argument, defaulting to light, and the documentation describes three image tiers, with light being the default that does MusicBrainz tagging only and medium adding AcoustID fingerprinting. A plain docker build keeps the pre-tiering image shape, which is a deliberate choice to avoid surprising existing users. Release history shows steady work, with v0.6.1 on 2026-07-21, v0.7.0 on 2026-10-09 and v0.7.1 on 2026-10-14, and the last push was on 2026-10-14.

Editorial conclusion

Adopt Soulbeet if you run a self-hosted music stack, already have Navidrome and slskd in mind, and want discovery and library cleanup driven by your own listening history rather than manual imports. Do not adopt it if you only want a tagger, because beets alone does that job without a second service, and do not adopt it without reading the Rate and Keep rules, since unrated downloads expire and one star deletes a file. Before you start, map the same physical folder to /downloads for slskd and soulbeet and the same folder to /music for soulbeet and navidrome, use slskd 0.26 or newer, and replace the SECRET_KEY value before the first start.

Frequently asked questions

How do I install Soulbeet?

The recommended way is Docker Compose, which ensures the dependencies such as beets and python are set up correctly, and the image supports AMD64 and ARM64. Create a docker-compose.yml with the soulbeet service and its dependencies, then run docker-compose up -d.

What must the Docker volume mappings match for Soulbeet to work?

The /downloads volume must be the same between slskd and soulbeet so Soulbeet sees what slskd downloaded, and the /music volume must be the same between soulbeet and navidrome so Navidrome sees the organised library. Soulbeet also requires slskd 0.26 or newer because downloads are enqueued through the batches API.

How do I log into Soulbeet for the first time?

Open http://localhost:9765. With NAVIDROME_URL set you use your Navidrome credentials and the account is created on first login, and without it you log in as admin with the password admin and then change it in Settings, Users. Local passwords always work even when Navidrome rejects them.

What do the Soulbeet discovery profiles do?

There are three: Conservative stays close to what you already know, Balanced is the middle setting, and Adventurous goes into unfamiliar territory. You can run one or all three at once, each with its own playlist, and the engine uses your Last.fm or ListenBrainz history to find candidates.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. terry90/soulbeet on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/terry90-soulbeet.svg)](https://hysenlabs.com/projects/terry90-soulbeet)

Was this useful?