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,279
stars
247
commits
TypeScript
primary language
Aug 24, 2026
updated
A self-hosted, on-demand audio streaming platform that brings the Spotify experience to your personal music library.
Kima 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.

The native iOS app is built and in testing — targeting an October release. It's a real Swift app, not a wrapper around the web view.
Why iOS first, and not Android. The PWA is genuinely good on Android — background playback, lock-screen controls, and resuming after an interruption all just work. On iOS it fights the platform. A standalone PWA's audio session gets suspended when the app is backgrounded, and playback stops dead when a track ends: Media Session still reports "playing" while no sound comes out. That's WebKit #261858 — filed September 2023, still open.
Kima works around it, and the workarounds hold: audio is routed through an AudioContext that claims the session more durably than a bare <audio> element, the session is re-claimed on foreground and route changes, and every wait in the recovery ladder has a deadline. But that machinery exists because the platform breaks the ordinary path, not because the ordinary path was wrong. A native app needs none of it.
The PWA isn't going anywhere and stays fully supported — Android users in particular lose nothing by staying on it.
Thanks for your patience while I work through this.
The centerpiece of music discovery in Kima. Your entire library is analyzed by a CLAP neural network and projected into a 2D/3D space where similar-sounding tracks cluster together. The result is a living map of your music collection you can explore, search, and navigate.
Music Map -- the default 2D view. Every track in your library is a point on the map, colored by mood cluster. Zoom and pan to explore. Click any track to inspect it; double-click to play it immediately.
Galaxy View -- the same data rendered as a 3D star field. Orbit, zoom, and fly through your library. Switch between Map and Galaxy with the toggle in the top-left corner.
Drift -- pick any two tracks as start and end points and Kima plots a smooth path through the audio space between them. The resulting queue travels gradually from one sonic neighborhood to the other.
Blend -- add multiple tracks and let Kima find the centroid in audio space. The result is a queue of tracks that blend all of the inputs together into something new.
Additional features:
Mood Mixer -- pick a mood preset (Happy, Energetic, Chill, Focus, Party, Acoustic, Melancholy, Sad, Aggressive) to instantly generate a playlist calibrated to that sound. Moods are derived from audio analysis of your actual library, not genre tags.
Import playlists from Spotify, Deezer, and YouTube, or browse and discover new music directly.
Kima works as a PWA on mobile devices, giving you a native app-like experience without needing to download from an app store.
To install on Android:
To install on iOS:
PWA Features:
Kima includes a dedicated interface optimized for television displays:
The TV interface is automatically enabled when accessing Kima from an Android TV device's browser.
docker run -d \
--name kima \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
chevron7locked/kima:latest
That's it! Open http://localhost:3030 and create your account.
With GPU acceleration (requires NVIDIA Container Toolkit):
docker run -d \
--name kima \
--gpus all \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
chevron7locked/kima:latest
The Kima container includes everything you need:
docker run -d \
--name kima \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
-e SESSION_SECRET=your-secret-key \
-e TZ=America/New_York \
--add-host=host.docker.internal:host-gateway \
chevron7locked/kima:latest
| Variable | Description | Default |
|---|---|---|
SESSION_SECRET | Session encryption key | Auto-generated |
TZ | Timezone | UTC |
Create a docker-compose.yml file:
services:
kima:
image: chevron7locked/kima:latest
container_name: kima
ports:
- "3030:3030"
volumes:
- /path/to/your/music:/music
- kima_data:/data
environment:
- TZ=America/New_York
# Required for Lidarr webhook integration on Linux
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
volumes:
kima_data:
Then run:
docker compose up -d
Updating with Docker Compose:
docker compose pull
docker compose up -d
/data on LinuxNamed volumes are recommended. If you bind-mount /data, make sure required subdirectories exist and are writable by the container service users.
mkdir -p /path/to/kima-data/postgres /path/to/kima-data/redis
If startup logs report a permission error, chown the host path to the UID/GID shown in the logs (for example, the postgres user).
Kima will begin scanning your music library automatically. Depending on the size of your collection, this may take a few minutes to several hours.
Kima offers two release channels to match your stability preferences:
Production-ready releases. Updated when new stable versions are released.
docker pull chevron7locked/kima:latest
# or specific version
docker pull chevron7locked/kima:v1.10.0
Unreleased builds, published on demand rather than on a timer — one exists because someone deliberately cut it, not because a clock struck 4am. They live on GitHub's registry; Docker Hub carries stable releases only.
⚠️ Not recommended for production — may be unstable or broken. A pre-release
build labels itself in the interface (v1.10.0-prerelease) so you can always
tell which channel a running instance came from.
docker pull ghcr.io/chevron7locked/kima:prerelease
:prerelease always points at the newest one — pull it again whenever you want
to move up.
In Compose, the registry prefix is required. Stable images come from Docker
Hub, which Docker assumes by default, so chevron7locked/kima:latest resolves.
Pre-releases live on GitHub's registry and will not resolve without the
ghcr.io/ prefix — a bare kima:prerelease sends Docker looking on Docker Hub
for an image that isn't there.
services:
kima-hub:
image: ghcr.io/chevron7locked/kima:prerelease
Every build is also tagged with its commit (:prerelease-a1b2c3d). You don't
need it to follow the channel — it's there for pinning a known-good build or
for saying which one you were on in a bug report.
⚠️ Point it at a fresh data volume, not your live one. A pre-release runs any new database migrations on first boot, and Prisma migrations don't roll back. Once they've applied, the older image can no longer read that database. Use a separate volume and a separate port to run one alongside your real instance — or take a backup first if you're deliberately upgrading in place.
The old
chevron7locked/kima:nightlytag is retired. It was last built successfully on 2026-08-12 and is not updated any more. If you are running it, move to a stable release or to the pre-release channel above.
For contributors: See CONTRIBUTING.md for information on submitting pull requests and contributing to Kima.
The unified Kima container handles most configuration automatically. Here are the available options:
| Variable | Default | Description |
|---|---|---|
SESSION_SECRET | Auto-generated | Session encryption key (recommended to set for persistence across restarts) |
SETTINGS_ENCRYPTION_KEY | Required | Encryption key for stored credentials (generate with openssl rand -base64 32) |
TZ | UTC | Timezone for the container |
PORT | 3030 | Port to access Kima |
KIMA_CALLBACK_URL | http://host.docker.internal:3030 | URL for Lidarr webhook callbacks (see Lidarr integration) |
AUDIO_ANALYSIS_WORKERS | 2 | Number of parallel workers for audio analysis (1-8) |
AUDIO_ANALYSIS_THREADS_PER_WORKER | 1 | Threads per worker for TensorFlow/FFT operations (1-4) |
AUDIO_ANALYSIS_BATCH_SIZE | 10 | Tracks per analysis batch |
AUDIO_BRPOP_TIMEOUT | 30 | Redis blocking wait timeout in seconds (also controls DB reconciliation) |
AUDIO_MODEL_IDLE_TIMEOUT | 300 | Seconds before unloading idle ML models to free memory (0 = never unload) |
LOG_LEVEL | warn (prod) / debug (dev) | Logging verbosity: debug, info, warn, error, silent |
DOCS_PUBLIC | false | Set to true to allow public access to API docs in production |
The music library path is configured via Docker volume mount (-v /path/to/music:/music).
If you're accessing Kima from outside your local network (via reverse proxy, for example), set the API URL:
NEXT_PUBLIC_API_URL=https://kima-api.yourdomain.com
And add your domain to the allowed origins:
ALLOWED_ORIGINS=http://localhost:3030,https://kima.yourdomain.com
Kima uses several sensitive environment variables. Never commit your .env file.
| Variable | Purpose | Required |
|---|---|---|
SESSION_SECRET | Session encryption (32+ chars) | Yes |
SETTINGS_ENCRYPTION_KEY | Encrypts stored credentials | Yes |
SOULSEEK_USERNAME | Soulseek login | If using Soulseek |
SOULSEEK_PASSWORD | Soulseek password | If using Soulseek |
LIDARR_API_KEY | Lidarr integration | If using Lidarr |
OPENAI_API_KEY | AI features | Optional |
LASTFM_API_KEY | Artist recommendations | Optional |
FANART_API_KEY | Artist images | Optional |
/api/auth/refresh endpointhttpOnly, sameSite=strict, and secure (in production)/admin/queues requires authenticated admin user/api-docs require authentication in production (unless DOCS_PUBLIC=true)If using Mullvad VPN for Soulseek:
backend/mullvad/ (gitignored)*.conf and key.txt patterns are already in .gitignore# Generate a secure session secret
openssl rand -base64 32
# Generate encryption key
openssl rand -base64 32
ALLOWED_ORIGINS for your domainThe CLAP (Contrastive Language-Audio Pretraining) service generates embeddings for audio similarity search, powering the Vibe button's track matching feature.
pgvector/pgvector:pg16 image)Environment variables in docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
CLAP_WORKERS | 2 | Number of analysis workers (1-8) |
CLAP_THREADS_PER_WORKER | 1 | CPU threads per worker (1-4) |
CLAP_SLEEP_INTERVAL | 5 | Queue poll interval in seconds |
The CLAP analyzer runs automatically alongside the main audio analyzer. The vibe button uses CLAP embeddings for finding similar tracks. Text-based vibe search is available at /api/vibe/search.
| Endpoint | Method | Description |
|---|---|---|
/api/vibe/similar/:trackId | GET | Get tracks similar to the given track |
/api/vibe/search | POST | Search tracks by text description |
/api/vibe/status | GET | Get embedding progress |
GPU acceleration speeds up audio analysis (mood detection, BPM extraction, vibe embeddings). It is optional -- everything works on CPU, just slower.
nvidia-smi should work)The toolkit is required for any Docker container to access the GPU. Install it once:
Fedora / Nobara / RHEL:
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo && sudo dnf install -y nvidia-container-toolkit && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
Ubuntu / Debian:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list && sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
# Check NVIDIA driver
nvidia-smi
# Check container toolkit
nvidia-container-runtime --version
All-in-One container:
docker run -d --gpus all -p 3030:3030 -v /path/to/music:/music -v kima_data:/data chevron7locked/kima:latest
Docker Compose:
Uncomment the devices block under audio-analyzer (and optionally audio-analyzer-clap) in docker-compose.yml:
reservations:
memory: 2G
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Then restart: docker compose up -d
# MusiCNN analyzer
docker logs kima_audio_analyzer 2>&1 | grep -i gpu
# CLAP analyzer
docker logs kima_audio_analyzer_clap 2>&1 | grep -i gpu
Expected: TensorFlow GPU detected: ... or CUDA available: True
If you see TensorFlow running on CPU, GPU passthrough is not active.
Kima works beautifully on its own, but it becomes even more powerful when connected to other services.
Connect Kima to your Lidarr instance to request and download new music directly from the app.
What you get:
Request downloads with a single click
Setup:
http://localhost:8686)Kima will automatically configure a webhook in Lidarr to receive notifications when new music is imported.
Networking Note:
The webhook requires Lidarr to be able to reach Kima. By default, Kima uses host.docker.internal:3030 which works automatically when using the provided docker-compose files (they include extra_hosts to enable this on Linux).
If you're using custom Docker networks with static IPs, set the callback URL so Lidarr knows how to reach Kima:
environment:
- KIMA_CALLBACK_URL=http://YOUR_KIMA_IP:3030
Use the IP address that Lidarr can reach. If both containers are on the same Docker network, use Kima's container IP.
Connect to your Audiobookshelf instance to browse and listen to audiobooks within Kima.
What you get:
Setup:
http://localhost:13378)Kima includes built-in Soulseek support for finding rare tracks and one-offs that aren't available through traditional download sources like Lidarr.
Soulseek is a peer-to-peer file sharing network focused on music. Users share their music libraries and can browse/download from each other. Kima connects directly to the Soulseek network -- no additional software (like slskd) is required.
Setup:
How Search Works:
When you search for music in Kima's Discovery tab, Soulseek results appear alongside Last.fm and Deezer results. Each result shows the filename, file size, bitrate, and format (FLAC/MP3). Metadata like artist and album is parsed from the file path structure (typically Artist/Album/01 - Track.flac).
How Download Works:
You can also configure Soulseek as a download source for playlist imports. In Settings > Downloads, set Soulseek as primary or fallback source. When importing a Spotify/Deezer playlist, tracks not found in your library will be searched and downloaded from Soulseek automatically.
Download progress is visible in the Activity Panel (bell icon in the top bar).
Limitations:
Kima implements the OpenSubsonic REST API, making it compatible with any Subsonic client.
Tested clients: Amperfy (iOS), Symfonium, DSub, Ultrasonic
Setup:
http://192.168.1.10:3030)Notes:
p= is a token before checking it against your account password, so the same field works for eithert=/s=) is not supported -- it's a challenge-response scheme that requires the server to hold your token in a reversible form, which Kima no longer does (tokens are hashed at rest, the same way passwords are). A client hard-coded to that scheme will fail to authenticate; every client we've tested falls back to sending the password field instead, which worksapiKeyAuthentication, songLyrics, indexBasedQueue, and getPodcastEpisodetokenInfo, startScan, getScanStatus, search, search2, search3, getUser, getUsers, createUser, updateUser, deleteUser, changePassword, getPlaylists, getPlaylist, createPlaylist, updatePlaylist, deletePlaylist, setRating, getPlayQueue, getPlayQueueByIndex, savePlayQueue, savePlayQueueByIndex, getBookmarks, createBookmark, deleteBookmark, getInternetRadioStations, createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation, getAvatar, getShares, createShare, updateShare, deleteShare, getChatMessages, addChatMessage, getVideos, getVideoInfo, getCaptions, jukeboxControl, getTranscodeDecision, getTranscodeStream, hls, getLyricsBySongId, getLyrics, getNowPlaying, getTopSongs, getSongsByGenre, getSimilarSongs, getSimilarSongs2, getMusicDirectory, getPodcasts, getNewestPodcasts, getPodcastEpisode, refreshPodcastsSubsonic route module layout (backend):
backend/src/routes/subsonic/index.ts -- top-level router composition, auth/rate-limit, system endpointslibrary.ts -- artists/albums/tracks browsing and directory traversalsearch.ts -- search/search2/search3, genre/top/similar discoveryplayback.ts -- stream/download/cover-art/scrobble/now-playing plus hls/getTranscodeStreamplaylists.ts -- playlist list/read/create/update/deletequeue.ts -- play queue get/save (ID-based and index-based)starred.ts -- star/unstar, starred lists, setRatingartistInfo.ts / lyrics.ts -- artist metadata and lyric endpointsuserManagement.ts / profile.ts -- user admin endpoints and getUserpodcasts.ts -- podcast subscription and episode endpointscompat.ts -- compatibility/stub endpoints for clients that expect optional APIsWhen adding a Subsonic endpoint:
subsonicError.subsonicOk using existing mapper helpers where possible.backend/src/routes/subsonic/index.ts (preserve catch-all behavior).When you first access Kima, you'll be guided through a setup wizard:
After setup, your home screen displays:
Kima offers two search modes:
Library Search - Find artists, albums, and tracks in your collection. Results are instant and searchable by name.
Discovery Search - Find new music and podcasts you don't own. Powered by Last.fm for music and iTunes for podcasts. From discovery results, you can:
Your listening progress is saved automatically, so you can pause on one device and resume on another.
Exploring the map:
Drift -- journey between two tracks:
Blend -- find the space between multiple tracks:
Keep The Vibe Going (from the player):
Mood Mixer:
From Spotify:
From Deezer:
From YouTube:
In Settings, you can configure:
When using the web interface, these keyboard shortcuts are available during playback:
| Key | Action |
|---|---|
| Space | Play / Pause |
| N | Next track |
| P | Previous track |
| S | Toggle shuffle |
| M | Toggle mute |
| Arrow Up | Volume up |
| Arrow Down | Volume down |
| Arrow Right | Seek forward 10 seconds |
| Arrow Left | Seek backward 10 seconds |
Kima includes a dedicated interface optimized for television displays:
The TV interface is automatically enabled when accessing Kima from an Android TV device. Access it through your TV's web browser.
As an administrator, you can:
Administrators have access to additional settings:
Configure how Kima acquires new music in Settings → Downloads:
Control metadata enrichment in Settings → Cache & Automation:
The Activity Panel provides real-time visibility into downloads and system events:
Access the Activity Panel by clicking the bell icon in the top bar (desktop) or through the menu (mobile).
For programmatic access to Kima:
Authorization header: Bearer YOUR_API_KEYAPI documentation is available at /api-docs when the backend is running (requires authentication in production).
Monitor background job queues at /admin/queues:
Kima consists of several components working together:
┌─────────────────┐
│ Your Browser │
└────────┬────────┘
│
▼
┌─────────────────┐ ┌─────────────────────┐
│ Music Library │◄────────────►│ Frontend │
│ (Your Files) │ │ (Next.js :3030) │
└─────────────────┘ └──────────┬──────────┘
│
▼
┌─────────────────┐ ┌─────────────────────┐
│ Lidarr │◄────────────►│ Backend │
│ (Optional) │ │ (Express.js :3006) │
└─────────────────┘ └──────────┬──────────┘
│
┌─────────────────┐ ┌──────────┴──────────┐
│ Audiobookshelf │◄────────────►│ │
│ (Optional) │ │ ┌───────────────┐ │
└─────────────────┘ │ │ PostgreSQL │ │
│ └───────────────┘ │
│ ┌───────────────┐ │
│ │ Redis │ │
│ └───────────────┘ │
└─────────────────────┘
| Component | Purpose | Default Port |
|---|---|---|
| Frontend | Web interface (Next.js) | 3030 |
| Backend | API server (Express.js) | 3006 |
| PostgreSQL | Database (with pgvector) | 5432 |
| Redis | Caching and job queues | 6379 |
| Audio Analyzer | Mood, BPM, key detection (Essentia MusiCNN) | -- |
| Audio Analyzer CLAP | Vibe similarity embeddings (LAION CLAP) | -- |
Kima is under active development. Here's what's planned:
Contributions and suggestions are welcome.
Kima is released under the GNU General Public License v3.0.
You are free to use, modify, and distribute this software under the terms of the GPL-3.0 license.
Kima wouldn't be possible without these services and projects:
If you encounter issues or have questions:
docker compose logs if relevantBuilt with love for the self-hosted community.
TypeScript
95.2%
Python
2.2%
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,279
stars
247
commits
TypeScript
primary language
Aug 24, 2026
updated
A self-hosted, on-demand audio streaming platform that brings the Spotify experience to your personal music library.
Kima 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.

The native iOS app is built and in testing — targeting an October release. It's a real Swift app, not a wrapper around the web view.
Why iOS first, and not Android. The PWA is genuinely good on Android — background playback, lock-screen controls, and resuming after an interruption all just work. On iOS it fights the platform. A standalone PWA's audio session gets suspended when the app is backgrounded, and playback stops dead when a track ends: Media Session still reports "playing" while no sound comes out. That's WebKit #261858 — filed September 2023, still open.
Kima works around it, and the workarounds hold: audio is routed through an AudioContext that claims the session more durably than a bare <audio> element, the session is re-claimed on foreground and route changes, and every wait in the recovery ladder has a deadline. But that machinery exists because the platform breaks the ordinary path, not because the ordinary path was wrong. A native app needs none of it.
The PWA isn't going anywhere and stays fully supported — Android users in particular lose nothing by staying on it.
Thanks for your patience while I work through this.
The centerpiece of music discovery in Kima. Your entire library is analyzed by a CLAP neural network and projected into a 2D/3D space where similar-sounding tracks cluster together. The result is a living map of your music collection you can explore, search, and navigate.
Music Map -- the default 2D view. Every track in your library is a point on the map, colored by mood cluster. Zoom and pan to explore. Click any track to inspect it; double-click to play it immediately.
Galaxy View -- the same data rendered as a 3D star field. Orbit, zoom, and fly through your library. Switch between Map and Galaxy with the toggle in the top-left corner.
Drift -- pick any two tracks as start and end points and Kima plots a smooth path through the audio space between them. The resulting queue travels gradually from one sonic neighborhood to the other.
Blend -- add multiple tracks and let Kima find the centroid in audio space. The result is a queue of tracks that blend all of the inputs together into something new.
Additional features:
Mood Mixer -- pick a mood preset (Happy, Energetic, Chill, Focus, Party, Acoustic, Melancholy, Sad, Aggressive) to instantly generate a playlist calibrated to that sound. Moods are derived from audio analysis of your actual library, not genre tags.
Import playlists from Spotify, Deezer, and YouTube, or browse and discover new music directly.
Kima works as a PWA on mobile devices, giving you a native app-like experience without needing to download from an app store.
To install on Android:
To install on iOS:
PWA Features:
Kima includes a dedicated interface optimized for television displays:
The TV interface is automatically enabled when accessing Kima from an Android TV device's browser.
docker run -d \
--name kima \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
chevron7locked/kima:latest
That's it! Open http://localhost:3030 and create your account.
With GPU acceleration (requires NVIDIA Container Toolkit):
docker run -d \
--name kima \
--gpus all \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
chevron7locked/kima:latest
The Kima container includes everything you need:
docker run -d \
--name kima \
-p 3030:3030 \
-v /path/to/your/music:/music \
-v kima_data:/data \
-e SESSION_SECRET=your-secret-key \
-e TZ=America/New_York \
--add-host=host.docker.internal:host-gateway \
chevron7locked/kima:latest
| Variable | Description | Default |
|---|---|---|
SESSION_SECRET | Session encryption key | Auto-generated |
TZ | Timezone | UTC |
Create a docker-compose.yml file:
services:
kima:
image: chevron7locked/kima:latest
container_name: kima
ports:
- "3030:3030"
volumes:
- /path/to/your/music:/music
- kima_data:/data
environment:
- TZ=America/New_York
# Required for Lidarr webhook integration on Linux
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
volumes:
kima_data:
Then run:
docker compose up -d
Updating with Docker Compose:
docker compose pull
docker compose up -d
/data on LinuxNamed volumes are recommended. If you bind-mount /data, make sure required subdirectories exist and are writable by the container service users.
mkdir -p /path/to/kima-data/postgres /path/to/kima-data/redis
If startup logs report a permission error, chown the host path to the UID/GID shown in the logs (for example, the postgres user).
Kima will begin scanning your music library automatically. Depending on the size of your collection, this may take a few minutes to several hours.
Kima offers two release channels to match your stability preferences:
Production-ready releases. Updated when new stable versions are released.
docker pull chevron7locked/kima:latest
# or specific version
docker pull chevron7locked/kima:v1.10.0
Unreleased builds, published on demand rather than on a timer — one exists because someone deliberately cut it, not because a clock struck 4am. They live on GitHub's registry; Docker Hub carries stable releases only.
⚠️ Not recommended for production — may be unstable or broken. A pre-release
build labels itself in the interface (v1.10.0-prerelease) so you can always
tell which channel a running instance came from.
docker pull ghcr.io/chevron7locked/kima:prerelease
:prerelease always points at the newest one — pull it again whenever you want
to move up.
In Compose, the registry prefix is required. Stable images come from Docker
Hub, which Docker assumes by default, so chevron7locked/kima:latest resolves.
Pre-releases live on GitHub's registry and will not resolve without the
ghcr.io/ prefix — a bare kima:prerelease sends Docker looking on Docker Hub
for an image that isn't there.
services:
kima-hub:
image: ghcr.io/chevron7locked/kima:prerelease
Every build is also tagged with its commit (:prerelease-a1b2c3d). You don't
need it to follow the channel — it's there for pinning a known-good build or
for saying which one you were on in a bug report.
⚠️ Point it at a fresh data volume, not your live one. A pre-release runs any new database migrations on first boot, and Prisma migrations don't roll back. Once they've applied, the older image can no longer read that database. Use a separate volume and a separate port to run one alongside your real instance — or take a backup first if you're deliberately upgrading in place.
The old
chevron7locked/kima:nightlytag is retired. It was last built successfully on 2026-08-12 and is not updated any more. If you are running it, move to a stable release or to the pre-release channel above.
For contributors: See CONTRIBUTING.md for information on submitting pull requests and contributing to Kima.
The unified Kima container handles most configuration automatically. Here are the available options:
| Variable | Default | Description |
|---|---|---|
SESSION_SECRET | Auto-generated | Session encryption key (recommended to set for persistence across restarts) |
SETTINGS_ENCRYPTION_KEY | Required | Encryption key for stored credentials (generate with openssl rand -base64 32) |
TZ | UTC | Timezone for the container |
PORT | 3030 | Port to access Kima |
KIMA_CALLBACK_URL | http://host.docker.internal:3030 | URL for Lidarr webhook callbacks (see Lidarr integration) |
AUDIO_ANALYSIS_WORKERS | 2 | Number of parallel workers for audio analysis (1-8) |
AUDIO_ANALYSIS_THREADS_PER_WORKER | 1 | Threads per worker for TensorFlow/FFT operations (1-4) |
AUDIO_ANALYSIS_BATCH_SIZE | 10 | Tracks per analysis batch |
AUDIO_BRPOP_TIMEOUT | 30 | Redis blocking wait timeout in seconds (also controls DB reconciliation) |
AUDIO_MODEL_IDLE_TIMEOUT | 300 | Seconds before unloading idle ML models to free memory (0 = never unload) |
LOG_LEVEL | warn (prod) / debug (dev) | Logging verbosity: debug, info, warn, error, silent |
DOCS_PUBLIC | false | Set to true to allow public access to API docs in production |
The music library path is configured via Docker volume mount (-v /path/to/music:/music).
If you're accessing Kima from outside your local network (via reverse proxy, for example), set the API URL:
NEXT_PUBLIC_API_URL=https://kima-api.yourdomain.com
And add your domain to the allowed origins:
ALLOWED_ORIGINS=http://localhost:3030,https://kima.yourdomain.com
Kima uses several sensitive environment variables. Never commit your .env file.
| Variable | Purpose | Required |
|---|---|---|
SESSION_SECRET | Session encryption (32+ chars) | Yes |
SETTINGS_ENCRYPTION_KEY | Encrypts stored credentials | Yes |
SOULSEEK_USERNAME | Soulseek login | If using Soulseek |
SOULSEEK_PASSWORD | Soulseek password | If using Soulseek |
LIDARR_API_KEY | Lidarr integration | If using Lidarr |
OPENAI_API_KEY | AI features | Optional |
LASTFM_API_KEY | Artist recommendations | Optional |
FANART_API_KEY | Artist images | Optional |
/api/auth/refresh endpointhttpOnly, sameSite=strict, and secure (in production)/admin/queues requires authenticated admin user/api-docs require authentication in production (unless DOCS_PUBLIC=true)If using Mullvad VPN for Soulseek:
backend/mullvad/ (gitignored)*.conf and key.txt patterns are already in .gitignore# Generate a secure session secret
openssl rand -base64 32
# Generate encryption key
openssl rand -base64 32
ALLOWED_ORIGINS for your domainThe CLAP (Contrastive Language-Audio Pretraining) service generates embeddings for audio similarity search, powering the Vibe button's track matching feature.
pgvector/pgvector:pg16 image)Environment variables in docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
CLAP_WORKERS | 2 | Number of analysis workers (1-8) |
CLAP_THREADS_PER_WORKER | 1 | CPU threads per worker (1-4) |
CLAP_SLEEP_INTERVAL | 5 | Queue poll interval in seconds |
The CLAP analyzer runs automatically alongside the main audio analyzer. The vibe button uses CLAP embeddings for finding similar tracks. Text-based vibe search is available at /api/vibe/search.
| Endpoint | Method | Description |
|---|---|---|
/api/vibe/similar/:trackId | GET | Get tracks similar to the given track |
/api/vibe/search | POST | Search tracks by text description |
/api/vibe/status | GET | Get embedding progress |
GPU acceleration speeds up audio analysis (mood detection, BPM extraction, vibe embeddings). It is optional -- everything works on CPU, just slower.
nvidia-smi should work)The toolkit is required for any Docker container to access the GPU. Install it once:
Fedora / Nobara / RHEL:
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo && sudo dnf install -y nvidia-container-toolkit && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
Ubuntu / Debian:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list && sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit && sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
# Check NVIDIA driver
nvidia-smi
# Check container toolkit
nvidia-container-runtime --version
All-in-One container:
docker run -d --gpus all -p 3030:3030 -v /path/to/music:/music -v kima_data:/data chevron7locked/kima:latest
Docker Compose:
Uncomment the devices block under audio-analyzer (and optionally audio-analyzer-clap) in docker-compose.yml:
reservations:
memory: 2G
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Then restart: docker compose up -d
# MusiCNN analyzer
docker logs kima_audio_analyzer 2>&1 | grep -i gpu
# CLAP analyzer
docker logs kima_audio_analyzer_clap 2>&1 | grep -i gpu
Expected: TensorFlow GPU detected: ... or CUDA available: True
If you see TensorFlow running on CPU, GPU passthrough is not active.
Kima works beautifully on its own, but it becomes even more powerful when connected to other services.
Connect Kima to your Lidarr instance to request and download new music directly from the app.
What you get:
Request downloads with a single click
Setup:
http://localhost:8686)Kima will automatically configure a webhook in Lidarr to receive notifications when new music is imported.
Networking Note:
The webhook requires Lidarr to be able to reach Kima. By default, Kima uses host.docker.internal:3030 which works automatically when using the provided docker-compose files (they include extra_hosts to enable this on Linux).
If you're using custom Docker networks with static IPs, set the callback URL so Lidarr knows how to reach Kima:
environment:
- KIMA_CALLBACK_URL=http://YOUR_KIMA_IP:3030
Use the IP address that Lidarr can reach. If both containers are on the same Docker network, use Kima's container IP.
Connect to your Audiobookshelf instance to browse and listen to audiobooks within Kima.
What you get:
Setup:
http://localhost:13378)Kima includes built-in Soulseek support for finding rare tracks and one-offs that aren't available through traditional download sources like Lidarr.
Soulseek is a peer-to-peer file sharing network focused on music. Users share their music libraries and can browse/download from each other. Kima connects directly to the Soulseek network -- no additional software (like slskd) is required.
Setup:
How Search Works:
When you search for music in Kima's Discovery tab, Soulseek results appear alongside Last.fm and Deezer results. Each result shows the filename, file size, bitrate, and format (FLAC/MP3). Metadata like artist and album is parsed from the file path structure (typically Artist/Album/01 - Track.flac).
How Download Works:
You can also configure Soulseek as a download source for playlist imports. In Settings > Downloads, set Soulseek as primary or fallback source. When importing a Spotify/Deezer playlist, tracks not found in your library will be searched and downloaded from Soulseek automatically.
Download progress is visible in the Activity Panel (bell icon in the top bar).
Limitations:
Kima implements the OpenSubsonic REST API, making it compatible with any Subsonic client.
Tested clients: Amperfy (iOS), Symfonium, DSub, Ultrasonic
Setup:
http://192.168.1.10:3030)Notes:
p= is a token before checking it against your account password, so the same field works for eithert=/s=) is not supported -- it's a challenge-response scheme that requires the server to hold your token in a reversible form, which Kima no longer does (tokens are hashed at rest, the same way passwords are). A client hard-coded to that scheme will fail to authenticate; every client we've tested falls back to sending the password field instead, which worksapiKeyAuthentication, songLyrics, indexBasedQueue, and getPodcastEpisodetokenInfo, startScan, getScanStatus, search, search2, search3, getUser, getUsers, createUser, updateUser, deleteUser, changePassword, getPlaylists, getPlaylist, createPlaylist, updatePlaylist, deletePlaylist, setRating, getPlayQueue, getPlayQueueByIndex, savePlayQueue, savePlayQueueByIndex, getBookmarks, createBookmark, deleteBookmark, getInternetRadioStations, createInternetRadioStation, updateInternetRadioStation, deleteInternetRadioStation, getAvatar, getShares, createShare, updateShare, deleteShare, getChatMessages, addChatMessage, getVideos, getVideoInfo, getCaptions, jukeboxControl, getTranscodeDecision, getTranscodeStream, hls, getLyricsBySongId, getLyrics, getNowPlaying, getTopSongs, getSongsByGenre, getSimilarSongs, getSimilarSongs2, getMusicDirectory, getPodcasts, getNewestPodcasts, getPodcastEpisode, refreshPodcastsSubsonic route module layout (backend):
backend/src/routes/subsonic/index.ts -- top-level router composition, auth/rate-limit, system endpointslibrary.ts -- artists/albums/tracks browsing and directory traversalsearch.ts -- search/search2/search3, genre/top/similar discoveryplayback.ts -- stream/download/cover-art/scrobble/now-playing plus hls/getTranscodeStreamplaylists.ts -- playlist list/read/create/update/deletequeue.ts -- play queue get/save (ID-based and index-based)starred.ts -- star/unstar, starred lists, setRatingartistInfo.ts / lyrics.ts -- artist metadata and lyric endpointsuserManagement.ts / profile.ts -- user admin endpoints and getUserpodcasts.ts -- podcast subscription and episode endpointscompat.ts -- compatibility/stub endpoints for clients that expect optional APIsWhen adding a Subsonic endpoint:
subsonicError.subsonicOk using existing mapper helpers where possible.backend/src/routes/subsonic/index.ts (preserve catch-all behavior).When you first access Kima, you'll be guided through a setup wizard:
After setup, your home screen displays:
Kima offers two search modes:
Library Search - Find artists, albums, and tracks in your collection. Results are instant and searchable by name.
Discovery Search - Find new music and podcasts you don't own. Powered by Last.fm for music and iTunes for podcasts. From discovery results, you can:
Your listening progress is saved automatically, so you can pause on one device and resume on another.
Exploring the map:
Drift -- journey between two tracks:
Blend -- find the space between multiple tracks:
Keep The Vibe Going (from the player):
Mood Mixer:
From Spotify:
From Deezer:
From YouTube:
In Settings, you can configure:
When using the web interface, these keyboard shortcuts are available during playback:
| Key | Action |
|---|---|
| Space | Play / Pause |
| N | Next track |
| P | Previous track |
| S | Toggle shuffle |
| M | Toggle mute |
| Arrow Up | Volume up |
| Arrow Down | Volume down |
| Arrow Right | Seek forward 10 seconds |
| Arrow Left | Seek backward 10 seconds |
Kima includes a dedicated interface optimized for television displays:
The TV interface is automatically enabled when accessing Kima from an Android TV device. Access it through your TV's web browser.
As an administrator, you can:
Administrators have access to additional settings:
Configure how Kima acquires new music in Settings → Downloads:
Control metadata enrichment in Settings → Cache & Automation:
The Activity Panel provides real-time visibility into downloads and system events:
Access the Activity Panel by clicking the bell icon in the top bar (desktop) or through the menu (mobile).
For programmatic access to Kima:
Authorization header: Bearer YOUR_API_KEYAPI documentation is available at /api-docs when the backend is running (requires authentication in production).
Monitor background job queues at /admin/queues:
Kima consists of several components working together:
┌─────────────────┐
│ Your Browser │
└────────┬────────┘
│
▼
┌─────────────────┐ ┌─────────────────────┐
│ Music Library │◄────────────►│ Frontend │
│ (Your Files) │ │ (Next.js :3030) │
└─────────────────┘ └──────────┬──────────┘
│
▼
┌─────────────────┐ ┌─────────────────────┐
│ Lidarr │◄────────────►│ Backend │
│ (Optional) │ │ (Express.js :3006) │
└─────────────────┘ └──────────┬──────────┘
│
┌─────────────────┐ ┌──────────┴──────────┐
│ Audiobookshelf │◄────────────►│ │
│ (Optional) │ │ ┌───────────────┐ │
└─────────────────┘ │ │ PostgreSQL │ │
│ └───────────────┘ │
│ ┌───────────────┐ │
│ │ Redis │ │
│ └───────────────┘ │
└─────────────────────┘
| Component | Purpose | Default Port |
|---|---|---|
| Frontend | Web interface (Next.js) | 3030 |
| Backend | API server (Express.js) | 3006 |
| PostgreSQL | Database (with pgvector) | 5432 |
| Redis | Caching and job queues | 6379 |
| Audio Analyzer | Mood, BPM, key detection (Essentia MusiCNN) | -- |
| Audio Analyzer CLAP | Vibe similarity embeddings (LAION CLAP) | -- |
Kima is under active development. Here's what's planned:
Contributions and suggestions are welcome.
Kima is released under the GNU General Public License v3.0.
You are free to use, modify, and distribute this software under the terms of the GPL-3.0 license.
Kima wouldn't be possible without these services and projects:
If you encounter issues or have questions:
docker compose logs if relevantBuilt with love for the self-hosted community.
TypeScript
95.2%
Python
2.2%