Library / SDK
epoupon/lms avatar
epoupon/lms

epoupon/lms: a tag-driven music server for people who already curate their metadata

Lightweight Music Server. Access your self-hosted music using a web interface.

1,672 stars88 forksC++GPL-3.0

At a glance

What is it?
LMS is a self-hosted music streaming server written in C++ that leans on MusicBrainz identifiers and multi-valued tags rather than folder structure. It is a good fit if your library is already tagged carefully, and a frustrating one if it is not.
Who is it for?
Adopt LMS if your collection is already tagged with MusicBrainz identifiers and multi-valued artist, genre and mood tags, and you want a Subsonic-compatible endpoint that browses by those tags rather than by directory. Do not adopt it if your library is a folder tree of loosely named files, because the scanner's album grouping rules will fragment those albums and the recommendation engine will have little to work with.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

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

DEEP OPEN-SOURCE ANALYSIS

The problem LMS solves is metadata, not streaming

Serving audio files over HTTP is a solved problem. The harder problem is that a self-hosted collection accumulates duplicates, remasters, reissues and collaborations, and a server that indexes by folder path cannot tell you that the 2011 remaster and the 1973 original are the same record. LMS takes the position that the tags are the database. The README lists release groups, release types, MusicBrainz identifiers and artist relationships (composer, conductor, lyricist, mixer, performer, producer, remixer) as main features, and browsing by directory is described as something supported through the Subsonic/OpenSubsonic API rather than as the primary interface. That ordering is the whole design argument. The intended user is someone who runs Picard or an equivalent tagger, keeps MusicBrainz IDs in their files, and wants a server that reflects that work instead of flattening it. Multi-library support means you can point it at separate roots, and the tag filters (genre, mood, grouping, language, codec) operate globally across the collection.

How the scanner decides what an album is

The album grouping rules are the most consequential part of the system and the easiest place to get a bad result. According to the README, the recommended way to group tracks is the musicbrainz_albumid tag. When that tag is absent, LMS falls back to heuristics. If the analyzed file carries a disc number and the total number of discs is greater than one, sibling directories are also scanned to find a matching album. Otherwise only the current directory is considered. A candidate album must then match on five conditions: same name, same total number of discs, identical compilation flag, same record labels, and same barcode. That is a strict conjunction. A compilation flag that differs between two files that are otherwise the same release will split the album, and a missing barcode will prevent a match that a human would consider obvious. The practical consequence is that musicbrainz_albumid is not a nice-to-have, it is the mechanism that keeps the fallback out of the picture. If you are not writing that tag, you are relying on five fields agreeing across every file in the release.

The recommendation engine has two modes with very different costs

LMS offers a recommendation engine, radio mode that fills the play queue with similar tracks, and similarity suggestions for artists and albums. The README states that the engine runs in one of two modes. Audio similarity is the default and uses MusicNN embeddings. Tag-based mode uses the genre, mood, grouping and language tags instead. The cost difference is the point. The README reports extraction throughput for the audio mode ranging from roughly 1k tracks per hour on a Raspberry Pi 4 to roughly 25k tracks per hour on an Intel Core i5-13500, and notes that throughput depends heavily on both the number of scanner threads and the speed of storage. Those are figures reported in the project documentation, not measurements I have taken. The shape of the trade-off is clear enough: on a Pi-class machine with a large library, the first scan is a long-running background job, not a five-minute setup step. Tag-based mode avoids that cost entirely but only works as well as your genre and mood tagging, and it will produce worse results on a collection where mood is blank or set to a single value everywhere.

Getting it running and the config surface you actually touch

The README points to INSTALL.md for installation and lists authentication backends as configurable there; the specific package names and service unit contents are not in the material I have, so I will not invent them. What the README does document concretely is the per-library control files and the tag conventions. A .lmsignore file placed at the root of a media library excludes files from scanning, using a gitignore-inspired reduced syntax: *.jpg ignores all jpg files at any depth, /Unsorted/ ignores only the top-level Unsorted directory, extras/ ignores any extras directory at any depth, !cover.jpg re-includes a file matched by a broader rule, with ? and [abc] as wildcard and character class. Lines starting with # are comments and an empty file has no effect. One constraint is stated plainly: only one .lmsignore per library root is supported, and files placed in subdirectories are ignored. On the tagging side, the README recommends Picard defaults where artist holds a single display value and artists holds the actual names, and gives a Picard script using $setmulti for artistssort, albumartists and albumartistssort. It also documents the lms-audioinfo tool for inspecting which tags LMS parses, which is the fastest way to check a sample of your files before a full scan.

The ArtistInfo folder and the Kodi dependency

Artist metadata comes from a folder structure defined by Kodi, not by LMS. The README says the folder can live anywhere inside the scanned libraries but is best placed in a dedicated ArtistInfo directory at a library root for compatibility with other software. artist.nfo files supply biography, sort name and MusicBrainz ArtistID. The naming rule has an edge worth knowing: the canonical artist name used by LMS is the one in artist.nfo, and if an artist.nfo exists but provides no name, the containing folder name is used instead. If no artist info file exists at all, LMS picks the artist name found on the latest release. That last fallback means an artist's displayed name can shift when you add a newer release with different spelling, unless you pin it with an nfo file. It is a small thing that produces confusing results later, and it is the kind of behaviour that only shows up after a rescan.

What LMS does not do well

The failure mode is a library that was never tagged for this model. If your files are a directory tree with inconsistent album names, no barcode, no musicbrainz_albumid and a compilation flag that varies, the scanner will split releases and the tag filters will have little to filter on. The README's own emphasis on Picard defaults and MusicBrainz identifiers is an admission that the system expects a specific tagging discipline. Directory browsing exists, but only through the Subsonic/OpenSubsonic API, so a client that depends on walking folders is working against the grain. The second limitation is scan cost in audio similarity mode, which the documented throughput figures make concrete for low-power hardware. The third is the .lmsignore scope: one file per library root, subdirectory files ignored. If you wanted per-subtree exclusion rules, you would have to restructure into separate libraries instead. None of these are bugs. They are the price of a tag-first design.

Where it sits next to Navidrome and Jellyfin

The obvious comparison is Navidrome, which also speaks the Subsonic API and also targets self-hosters. The difference in approach is what each treats as the source of truth. Navidrome is commonly deployed as a single Go binary over a folder tree, which makes it quick to stand up and forgiving of messy tags. LMS is a C++ server whose feature list is dominated by MusicBrainz-aware concepts: release groups for showing remasters and reissues side by side, release types, artist relationship roles, and MusicBrainz identifiers specifically to disambiguate duplicated artist and release names. If your library is clean, LMS surfaces structure that Navidrome would not model at all. If your library is not clean, that same modelling produces splits and duplicates that Navidrome would simply ignore. Jellyfin is a broader media server where music is one library type among several; LMS does music only, which is why it can afford the tag depth. The choice is not which server is better, it is whether your metadata can support the one you pick.

Maintenance, releases and licence

The project is actively maintained. The repository is not archived, the most recent push is dated 2026-08-19, and the release cadence visible in the material is roughly monthly: v3.78.0 in June 2026, v3.79.0 in July, v3.80.0 in August. That cadence matters for upgrade cost because schema and scan behaviour can change between minor versions, and a tag-driven server that rescans on upgrade is a different maintenance burden from one that does not. The material does not describe migration steps between versions, so the safe assumption is that you should read release notes before upgrading rather than assume a drop-in replacement. The licence is GPL-3.0. That is a copyleft licence, which is relevant if you intend to modify LMS and distribute the result or bundle it into a product; it is not relevant if you are running it on your own hardware for yourself. I am not a lawyer and this is not legal advice. The other cost worth naming is the demo instance at lms-demo.poupon.dev, which the README says has administration settings disabled, so you can evaluate the browsing and playback experience there but not the scanner configuration or library management.

Editorial conclusion

Adopt LMS if your collection is already tagged with MusicBrainz identifiers and multi-valued artist, genre and mood tags, and you want a Subsonic-compatible endpoint that browses by those tags rather than by directory. Do not adopt it if your library is a folder tree of loosely named files, because the scanner's album grouping rules will fragment those albums and the recommendation engine will have little to work with. Before committing, run the lms-audioinfo tool against a sample of your files to see which tags LMS actually parses, and check whether your existing clients rely on directory browsing through the Subsonic API, since that is a secondary path here.

Official sources

  1. epoupon/lms on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes