Koito: a self-hosted ListenBrainz-compatible scrobbler for your own data
Koito is a modern, themeable scrobbler that you can use with any program that scrobbles to a custom ListenBrainz URL
At a glance
- What is it?
- Koito is a Go scrobbler that accepts anything speaking the ListenBrainz protocol, stores the history in your own database, and can relay every scrobble onward to a scrobbler you already trust. It is aimed at self-hosters who want their listening data on their own disk.
- Who is it for?
- Adopt Koito if you already run Docker and want your listening history in a database you control, and set up the relay first so a bad main branch cannot cost you scrobbles. Skip it if you need a stable API surface or do not want to run Postgres or SQLite yourself.
- 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 70 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Koito addresses for self-hosters
Scrobbling has an ownership problem. Your listening history accumulates in a service you do not run, in a schema you cannot query, and exporting it later tends to be an afterthought. Koito takes the opposite position: the history lives in a database you operate, and the web UI on top of it is yours to theme. The README frames the target user directly, describing Koito as a ListenBrainz-compatible scrobbler "for self-hosters who want control over their data and insights into their listening habits."
The second half of that sentence matters as much as the first. Koito is not only a sink. The README states it supports relaying to other compatible scrobblers, so you can point your player at Koito and have Koito forward everything onward to whatever already works for you. That turns adoption into a low-risk experiment rather than a migration. If Koito breaks, your existing scrobbler still has the data.
Who is this for, concretely? People running Navidrome or another Subsonic-compatible server, people who already have a Maloja or ListenBrainz history they want to keep, and anyone whose music client lets them set a custom ListenBrainz URL. The topics on the repository list docker, listenbrainz, navidrome, scrobble, scrobbler, self-hosted and subsonic, which is a fair summary of the intended neighbourhood.
How Koito handles a scrobble, from HTTP request to stored row
The repository layout tells you most of the architecture before you read a line of Go. There is cmd/ for the entrypoint, engine/ and internal/ for the application logic, db/ for migrations, queue/ for asynchronous work, imagecache/ for artwork, and client/ for the frontend. The Go module is github.com/gabehf/koito and the HTTP layer is built on github.com/go-chi/chi/v5 with github.com/go-chi/httprate for rate limiting. Database access goes through github.com/jackc/pgx/v5 for PostgreSQL and modernc.org/sqlite for SQLite, with github.com/pressly/goose/v3 handling migrations. Logging is github.com/rs/zerolog.
That dependency list is the real design statement. Koito speaks the ListenBrainz submission protocol on the inbound side, which is why the README can claim compatibility with "anything that scrobbles to ListenBrainz." A client posts a listen, the chi router hands it to the engine, the engine resolves the track against what is already stored, and the row lands in Postgres or SQLite depending on your configuration. Artwork is not fetched inline: the imagecache/ package and the bimg dependency (which needs libvips, not a pure-Go image library) exist because cover art resolution is expensive and gets its own cache. The queue/ package suggests that some of this work is deferred rather than done inside the request.
The Dockerfile confirms the libvips requirement twice. The backend build stage installs libvips-dev and pkg-config, and the final Debian image installs libvips42. If you build Koito yourself rather than pulling the image, you need those system packages present or the binary will not link. That is a genuine constraint, not a packaging detail you can ignore.
Installing Koito with Docker and sending your first scrobble
The README points at an installation guide on koito.io, then offers a minimal Docker Compose file for people who want to skip the reading. The compose file below is copied from the README. It pulls the published image, maps port 4110, and mounts ./koito as the configuration directory.
services:
koito:
image: gabehf/koito:latest
container_name: koito
ports:
- "4110:4110"
volumes:
- ./koito:/etc/koito
restart: unless-stoppedRun docker compose up -d from the directory holding that file. The container exposes 4110, which matches the EXPOSE directive in the Dockerfile, so the web UI should be reachable at http://localhost:4110 once the process starts. If nothing answers, check the logs first: the .env.example file sets KOITO_LOG_LEVEL=debug, and raising the log level is the fastest way to see whether the database connection or the configuration directory is the problem.
The .env.example file also shows KOITO_CONFIG_DIR and TZ. KOITO_CONFIG_DIR is the path the application reads configuration from, which is why the compose file mounts /etc/koito. TZ controls the timezone used when timestamps are interpreted, and it defaults to Etc/UTC in the example. The README does not reproduce the full configuration reference; it links to koito.io/reference/configuration/ and says image sources should be configured before importing data. Treat that page as required reading before you point a client at the instance.
Once Koito is running, the client side is the same as any ListenBrainz target: give your player the base URL of your Koito instance and the token Koito issues, and it will post listens there. The README does not walk through token creation, so the installation guide is where that step lives. For a first real test, play one track and confirm it appears in the Koito UI rather than assuming the request succeeded.
Where Koito is the wrong choice
The README is unusually candid about stability, and it should be taken literally. It states the project is "under active development and still considered 'unstable', which means the main branch can be unusable at times, and some breaking changes may occur." That is a direct warning about the main branch, not about tagged releases, but the distinction only helps you if you track releases deliberately. Anyone who wants a scrobbler they configure once and forget should look elsewhere, or should at minimum pin a tag rather than following latest.
The second limitation is operational. Koito is a server you run, with a database you maintain and migrations you apply. The presence of goose migrations, a Postgres driver and a SQLite driver means there is state to back up and a schema that will change between versions. If you do not already run databases for other services, this is real added surface area for a music history tool.
The third is the image pipeline. Because the build depends on libvips, environments without libvips42 available are harder to support outside the published Docker image. The Makefile's api.build target sets CGO_ENABLED=1, so a static, dependency-free binary is not what this project produces. If your deployment model is a single scratch container with no system libraries, Koito will fight you.
Koito against Maloja and multi-scrobbler
The README names two alternatives, and it is worth being precise about how they differ rather than treating them as interchangeable. The author writes that roughly half the reason for building Koito was "minor/not-so-minor greivances with Maloja," and that a better UI was part of the motivation. Maloja is the closest comparison: a self-hosted scrobbler with its own database and web interface. The practical difference Koito emphasizes is presentation and the relay path, since Koito is designed to sit in front of an existing scrobbler rather than replace it outright.
multi-scrobbler is a different shape of tool. It is a relay and fan-out service: you point sources at it and it forwards to multiple destinations. Koito is a destination first, with relaying as a feature. If your goal is to send one player's scrobbles to five services at once, a dedicated relay is the more direct answer. If your goal is to own the history and see it in a UI you like, with forwarding as a safety net, Koito is the better fit.
The import story is where Koito makes a concrete offer: the README lists import support for Maloja, ListenBrainz, LastFM, and Spotify. That matters if you are leaving one of those, because it means the history you accumulated elsewhere does not have to be abandoned. The README does not document rollback of an import, so back up your database before running one.
Maintenance, licensing and what an upgrade actually costs
The repository is not archived, and the last push was on 2026-07-23. Releases are frequent enough to suggest active work: v0.3.0 and v0.3.1 both landed on 2026-05-27, and v0.3.2 followed on 2026-06-01. The version number itself is the honest signal. A 0.x series with an explicit unstable warning means API and schema changes are expected, and the goose migrations in db/ are how those changes reach your instance.
The upgrade cost is therefore not zero. Pulling a new image can apply migrations to your database, and the README's own warning about breaking changes means you should read release notes before moving between versions. The Makefile gives you the test entry point if you build from source: make api.test runs go test ./... with a 60 second timeout. That is a smoke test, not a guarantee, and the README itself asks for contributions "especially more and more robust testing," which tells you where the project's confidence is thinnest.
Licensing is straightforward and permissive. The repository carries an MIT licence, which permits commercial and private use, modification and redistribution provided the copyright notice and permission notice are retained. That is a statement about the licence text, not legal advice; if you redistribute Koito as part of a product, read the LICENSE file in the repository root yourself. The MIT terms do not impose copyleft obligations on your own code, which is the practical point most adopters care about.
Editorial conclusion
Adopt Koito if you already run Docker and want your listening history in a database you control, and set up the relay first so a bad main branch cannot cost you scrobbles. Skip it if you need a stable API surface or do not want to run Postgres or SQLite yourself. Before committing, read the configuration reference at koito.io and confirm whether your storage backend is supported.
Frequently asked questions
What is Koito and what is it used for?
Koito is a themeable, ListenBrainz-compatible scrobbler that you self-host. It stores your listening history in your own database and can relay scrobbles onward to another compatible scrobbler.
How do I install Koito?
The README gives a minimal Docker Compose file using the image gabehf/koito:latest, mapping port 4110 and mounting ./koito at /etc/koito. It also links to an installation guide at koito.io for anything beyond that.
Which scrobblers and clients work with Koito?
The README states Koito is compatible with anything that scrobbles to a custom ListenBrainz URL. It also lists import support for Maloja, ListenBrainz, LastFM, and Spotify.
Can Koito forward my scrobbles to my existing scrobbler?
Yes. The README describes relaying to other compatible scrobblers and links to a guide for setting up a relay, which the author says was used throughout development.
Is Koito stable enough to replace my current scrobbler?
The README calls the project unstable and warns that the main branch can be unusable at times and that breaking changes may occur. It suggests setting up a relay if you do not want to replace your current scrobbler yet.
Official sources
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.
[](https://hysenlabs.com/projects/gabehf-koito)