Self-hosted service
Chevron7Locked/kima-hub avatar
Chevron7Locked/kima-hub

Kima: a self-hosted music server that matches songs by how they sound and feel

Kima Hub is built for music lovers who want the convenience of streaming services without sacrificing ownership of their library. Point it at your music collection, and Kima handles the rest: artist discovery, personalized playlists, podcast subscriptions, and seamless integration with tools you already use like Lidarr and Audiobookshelf.

1,290 stars62 forksTypeScriptGPL-3.0

At a glance

What is it?
Kima is a self-hosted music server that reads your own files, identifies them with MusicBrainz and AcoustID, and builds similarity from an audio model embedding plus mood and genre, then lays the library out as an explorable 3D map.
Who is it for?
Kima fits a listener who owns their music and wants a streaming front end for their own files, with discovery that goes beyond genre tags, especially someone willing to run a Docker stack with PostgreSQL, Redis and ONNX audio models. It is a poor fit if you want a hosted service, if your library is on a phone rather than a disk, or if a self-hosted media server is not something you want to maintain.
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 11 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

Editorial analysis

It plays your files, then tries to describe how they feel

Kima is a music server you run yourself. Point it at a folder of music and it gives you a web app to play it from on a computer or a phone, along with podcasts and audiobooks. The web app runs in any modern browser and installs to your home screen as an app, and an iOS app is described as coming soon to the App Store.

What separates it from a plain file server is the second half of that promise: it works out how each song sounds and feels, so it can find songs that match and lay your whole library out as a map. The project positions itself for music lovers who want the convenience of streaming services without giving up ownership of the library, and the repository topics include a Soulseek network client, a lyrics fetcher, a podcast player, an audiobook player and Spotify-adjacent metadata, with integration points named for Lidarr and Audiobookshelf.

The implementation is TypeScript across a frontend and backend, with a services directory for the audio analysis pieces, a Dockerfile, and five compose files covering the base stack, development, production, server and services variants. The licence is GPL-3.0, and the repository is not archived, with the last push on 2026-10-12 and release v1.11.0 published the same day.

Three descriptions of a song, and the weights between them

The similarity engine is the technical heart, and the design choice that defines it is that a song is described three ways, matched on all three, and combined into a single score.

Sound is a list of numbers produced by an audio model, CLAP from LAION, listening to the song. Songs that sound alike get similar lists. It carries weight 1, the largest by far.

Feel is how the song feels, calm or intense, bright or dark, which is its valence and arousal. Crucially it is read from the audio and set against the range of your own library, so calm means calm relative to your music rather than on an absolute scale. It carries weight 0.25.

Genre is the genres MusicBrainz lists for the song's artist, with a rule that is easy to miss: genres count only between songs by different artists, so one artist's songs do not match each other just because they share genres. It also carries weight 0.25.

That last rule is a deliberate design choice against a real failure mode. Without it, an artist with a consistent sound would sit in a dense cluster that is not interesting to explore, because the similarity would be telling you what you already know.

The three combine into one score, and every vibe feature uses it: similar tracks, recommendations from a track, Song Path, Blend, and the ranking of similar artists. A list holds at most 2 songs from one album and 4 from one artist, which is a diversity constraint on results rather than a ranking tweak.

What the score deliberately does not cover

Two exclusions matter, because they are the kind of thing that otherwise gets discovered as a bug report.

Text search matches on sound only. A sentence has no feel and no genre, so a query like quiet piano is answered by the sound vectors alone. That is a reasonable design, since a text query has no feel or genre of its own to match against.

Moods are outside the score entirely. Upbeat, Melancholic, Calm, Intense and Energetic are labels, and they exist to tag songs for mood mixes and to drive the mood buttons on the map. They are not one of the three similarity dimensions, so a song can be tagged Calm and still sit next to energetic tracks on the map.

Library radio stations are a third separate thing. They do not use the similarity score at all, which means a radio station and the similar-tracks list will not agree with each other, and that is by design rather than a discrepancy.

These boundaries are worth internalising before you configure anything, because they determine which of the app's outputs are grounded in the audio and which are grounded in metadata or in the other songs you happen to own. The sound vector is the only dimension that is intrinsic to the recording.

Song Path, Blend, and a map you can fly through

Three features sit on top of the similarity score, and each is a different way of turning it into a sequence.

Song Path takes a start and an end, either a song, an artist or a sound, and queues a run of songs that moves from one to the other. Blend takes songs you want more of and, optionally, songs you want less of, and builds a playlist from the songs that match the first group and not the second. Both can also start from a description of a sound such as quiet piano, which is the text-to-vector path rather than the song-to-vector one.

The map is the third. Open Vibe in the sidebar and your library appears as a 3D picture laid out from that same score, so songs that match sit close together, drawn as stars. The map is laid out once, after your library has been through enrichment. New songs are then placed among the others and keep their place, and a wipe and re-run lays the whole map out fresh. That ordering matters: the map is laid out once after enrichment, and songs added later are placed among the existing ones and keep their place.

Clicking a star gives four actions: Play, Match Vibe, which queues the song and its closest matches, Similar, which lights up its closest matches on the map without queuing them, and Song Path. The search box finds songs and artists by name, which is a plain name lookup rather than a similarity operation.

Five enrichment passes, and the cost of the first scan

After a scan, Kima runs five enrichment steps in the background: artist metadata, mood tags, audio analysis, vibe embeddings, which are the sound vectors, and similarity, which produces the matches and the map. Settings > Library Enrichment shows their progress, so a long first run is visible rather than mysterious.

The documentation gives concrete figures for this. On a 16-core machine running 16 workers, 9,004 songs took 87 minutes with an NVIDIA RTX 4060 Ti and both GPU switches on, about 1 minute per 100 songs. Without a graphics card, an earlier 1.11 build on the same machine took 98 minutes for 8,985 songs, about 1.1 minutes per 100. Fewer cores or workers take longer. So the accelerator saves about a tenth of the wall clock, not an order of magnitude, which is worth knowing before you go looking for a bigger GPU.

What is actually analysed is bounded. Audio analysis reads the last 90 seconds of each song, or the whole song when it is shorter. The sound vector comes from five 10-second samples spread across the song. Both audio models run on ONNX Runtime on the processor by default, and an optional switch moves them to an NVIDIA graphics card, with the details in docs/gpu.md.

That sampling design is a real trade-off. It is what makes a large library tractable, and it also means a song with a long instrumental intro and a short chorus is characterised by its ending.

Identification by tag, track list, then audio fingerprint

File identification is a cascade, and knowing the order tells you what happens to your library when the metadata is bad.

First, by a MusicBrainz tag already in the file. Then by the album's track list, and by a name search with a title check. Last, by an AcoustID audio fingerprint. The order means the cheap and accurate sources are tried before the expensive one, and it also means a file with a wrong tag keeps the wrong tag, because the tag wins and the fingerprint is never consulted.

The formats listed are MP3, FLAC, M4A, AAC, OGG, Opus, WAV, WMA, APE and WV. Playback offers a choice between the original file and an on-the-fly conversion to 320, 192 or 128 kbps, with the transcode cache size controlled by an environment variable and a default of 10 GB.

Lyrics come either from what is stored in the file or fetched from LRCLIB, and timed lyrics follow along with the song. Playlists can be private or public to other accounts on the server, reordered by dragging, or shared as a link that opens without an account, which is a deliberate choice for a self-hosted server where some content is meant to be shared outside the household.

There is also a stats page covering what you replay most, which artists make up most of your listening, and how much of your library you have never played. The last of those is the kind of number that changes how people organise a collection, and it depends entirely on the play counts being recorded correctly from the start.

Running it: one container, PostgreSQL, Redis, and two keys you must set

Kima is distributed as a Docker image, and the all-in-one Dockerfile documents the intended single-command start, which publishes port 3030 and mounts a music folder at /music:

bash
docker run -d -p 3030:3030 -v /path/to/music:/music chevron7locked/kima

That image bundles the backend, the frontend, PostgreSQL, Redis and the audio analyzer. The compose files in the repository break the same stack into different shapes, and the environment template spells out the configuration. The database connection string and the Redis URL are in there, and two security values are marked as required:

bash
SETTINGS_ENCRYPTION_KEY=
SESSION_SECRET=

The encryption key is for sensitive data such as API keys, passwords and 2FA secrets, and the file marks it as something you must set before starting Kima, with a note to generate it using openssl rand -base64 32. The session secret is auto-generated if left unset, and the compose file also carries an ADMIN_RESET_PASSWORD variable for resetting the admin password, commented out until you need it.

The music path is the other required value, and the distinction between host and container paths is the thing that trips people up. MUSIC_PATH is the host path, and inside Docker the music is always at /music, so the value in the compose file is deliberately hardcoded rather than templated from the host variable. The backend runs as an unprivileged user, with DATA_DIR set to the app directory because that one is writable, while the data volume is not.

The audio model is built inside the image rather than downloaded at runtime, which is a deliberate choice about reproducibility. The Dockerfile exports the CLAP model to ONNX in a build stage using pinned toolchain versions, a pinned upstream checkpoint revision, and a checksum gate that fails the build if any of those drift. The stated goal is that the same pins produce the same bytes on amd64 and arm64.

Editorial conclusion

Kima fits a listener who owns their music and wants a streaming front end for their own files, with discovery that goes beyond genre tags, especially someone willing to run a Docker stack with PostgreSQL, Redis and ONNX audio models. It is a poor fit if you want a hosted service, if your library is on a phone rather than a disk, or if a self-hosted media server is not something you want to maintain. Before adopting, set SETTINGS_ENCRYPTION_KEY and SESSION_SECRET in your .env file, point MUSIC_PATH at the host folder holding your audio, and read the enrichment figures in the documentation, because building the sound vectors for a large library takes real compute time on first scan.

Frequently asked questions

What is Kima and who is it for?

Kima is a self-hosted music server you run yourself. You point it at a folder of music and it gives you a web app to play your own files from on a computer or phone, along with podcasts and audiobooks, and it works out how each song sounds and feels so it can find matches and lay the library out as a map.

How do I install and run Kima?

Kima is published as a Docker image, and the all-in-one image bundles the backend, frontend, PostgreSQL, Redis and the audio analyzer. The documented start command is docker run -d -p 3030:3030 -v /path/to/music:/music chevron7locked/kima.

How does Kima work out which songs are similar?

Every song is described three ways and matched on all three: sound, from an audio model such as CLAP, with weight 1; feel, the valence and arousal read from the audio and set against the range of your own library, with weight 0.25; and genre from MusicBrainz, which counts only between songs by different artists, also with weight 0.25. The three combine into one score used by similar tracks, recommendations, Song Path, Blend and the map.

How long does the first scan take?

The documentation reports that on a 16-core machine with 16 workers, 9,004 songs took 87 minutes with an NVIDIA RTX 4060 Ti and both GPU switches on, about 1 minute per 100 songs. Without a graphics card, an earlier 1.11 build took 98 minutes for 8,985 songs, about 1.1 minutes per 100, and fewer cores or workers take longer.

What file formats does Kima read and how does it identify songs?

Kima reads MP3, FLAC, M4A, AAC, OGG, Opus, WAV, WMA, APE and WV. Identification is tried in order: a MusicBrainz tag in the file, then the album's track list and a title-checked name search, and last an AcoustID audio fingerprint.

Official sources

  1. Chevron7Locked/kima-hub on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
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/chevron7locked-kima-hub.svg)](https://hysenlabs.com/projects/chevron7locked-kima-hub)

Was this useful?