komf: fetching metadata and covers for a Komga or Kavita comic library
Komga and Kavita metadata fetcher
At a glance
- What is it?
- Komf is a Kotlin service that fills in metadata and cover art for a digital comic library hosted on Komga or Kavita, either on a schedule, on an event from the server, or manually through a browser extension. It is a companion service rather than a library, and it runs as a JAR or a container.
- Who is it for?
- Adopt komf if you run Komga or Kavita and want your library enriched with provider metadata, publisher tags and cover art without doing it by hand, since the event listener and the API update modes cover both the automatic and the one-off cases. Keep away if you need a hosted service, since this is a container or JAR you run yourself and point at your own Komga or Kavita instance.
- 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 26 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A companion service for a comic server you already run
Komf, the Komga and Kavita Metadata Fetcher, is a tool that fetches metadata and thumbnails for a digital comic book library. It can automatically pick up added series and update their metadata and thumbnail, and you can also manually search and identify series, or match the entire library or a single series.
The framing matters, because komf is not a comic server. Komga and Kavita are the servers, and komf talks to whichever one you use. The README says you can download the latest version from the releases page of the repository.
The integration story is a browser extension, which is the piece that makes the manual path pleasant. A browser web extension lets you configure komf and identify series directly from the Komga or Kavita web interface, and there are builds for Chrome and for Firefox. There is also a deprecated Komf userscript that is still functional but will not receive new updates, which is a useful signal about how the project prefers you to work.
The feature set is broad, and the configuration file is the documentation. There are per-server settings, a metadata update section with a default profile, a library type that can be MANGA, NOVEL, COMIC or WEBTOON with the comment that it is a hint to help better match book numbers, and update modes where API and COMIC_INFO can be used together.
The project is MIT licensed, the primary language is Kotlin, the default branch is master, and the last push was on 2026-09-27. The release list shows 2.0.0 and 2.0.1 both on 2026-09-01 and 2.1.0 on 2026-09-27, so the 2.x line is new and the 2.0.0 to 2.0.1 gap of two hours suggests a packaging fix rather than a feature release.
The configuration file is where the behaviour lives
The example application.yml is long and its comments are the actual documentation. The Komga block starts with the connection details, and every one of them can be overridden by an environment variable, which is what makes the Docker deployment possible:
komga:
baseUri: http://localhost:25600 #or env:KOMF_KOMGA_BASE_URI
komgaUser: [email protected] #or env:KOMF_KOMGA_USER
komgaPassword: admin #or env:KOMF_KOMGA_PASSWORD
eventListener:
enabled: false # if disabled will not connect to komga and won't pick up newly added entries
metadataLibraryFilter: [ ] # listen to all events if empty
metadataSeriesExcludeFilter: [ ]
notificationsLibraryFilter: [ ] # Will send notifications if any notification source is enabled. If empty will send notifications for all librariesThat eventListener block is the heart of the automatic mode. When it is disabled the service does not connect to Komga and does not pick up newly added entries. When it is enabled, the three filters scope what it listens to, and an empty filter means all events, all series except the excluded ones, and notifications for all libraries.
The metadataUpdate section is where you decide what actually gets written:
metadataUpdate:
default:
libraryType: "MANGA" # Can be "MANGA", "NOVEL", "COMIC" or "WEBTOON". Hint to help better match book numbers
updateModes: [ API ] # can use multiple options at once. available options are API, COMIC_INFO
aggregate: false # if enabled will search and aggregate metadata from all configured providers
mergeTags: false # if true and aggregate is enabled will merge tags from all providers
mergeGenres: false # if true and aggregate is enabled will merge genres from all providers
bookCovers: false # update book thumbnails
seriesCovers: false # update series thumbnails
overrideExistingCovers: true # if false will upload but not select new cover if another cover already exists
overrideComicInfo: false # Replace existing ComicInfo file. If false, only append additional dataThe defaults are conservative and worth understanding before you run it. Aggregate is off, so by default a provider is chosen rather than all of them being merged. Covers are off, so the first run does not touch your artwork. And overrideExistingCovers is true while overrideComicInfo is false, which means a new cover can be uploaded and selected, but an existing ComicInfo file is only appended to rather than replaced.
The postProcessing section goes further, with options to update the series title and the language to use, falling back to the first matching title, to use alternative title types and their languages, to order the books using the parsed volume or chapter number, to add a score tag in a format like score: 8, to override the reading direction with one of LEFT_TO_RIGHT, RIGHT_TO_LEFT, VERTICAL or WEBTOON, to set a default series language in BCP 47 format, and to add publisher tags in the format Original Publisher: Shueisha.
Running it as a container
The Docker Compose example is the shortest path to a running service, and it shows the environment variable scheme:
version: "3.7"
services:
komf:
image: sndxr/komf:latest
container_name: komf
ports:
- "8085:8085"
user: "1000:1000"
environment:
- KOMF_KOMGA_BASE_URI=http://komga:25600
- [email protected]
- KOMF_KOMGA_PASSWORD=admin
- KOMF_KAVITA_BASE_URI=http://kavita:5000
- KOMF_KAVITA_API_KEY=16707507-d05d-4696-b126-c3976ae14ffb
- KOMF_LOG_LEVEL=INFOThe port mapping is 8085 to 8085, the container runs as user 1000 to 1000 rather than root, and the volume mount points at a host directory holding the application.yml and the database file, with a comment saying exactly that. The restart policy is unless-stopped.
There is also a plain docker create form, which is the one to use if you are not using compose:
docker create \
--name komf \
-p 8085:8085 \
-u 1000:1000 \
-e KOMF_KOMGA_BASE_URI=http://komga:25600 \
-e [email protected] \
-e KOMF_KOMGA_PASSWORD=admin \
-e KOMF_KAVITA_BASE_URI=http://kavita:5000 \
-e KOMF_KAVITA_API_KEY=16707507-d05d-4696-b126-c3976ae14ffb \
-e KOMF_LOG_LEVEL=INFO \
-v /path/to/config:/config \
--restart unless-stopped \
sndxr/komf:latestThe network steps that follow are the part people miss. If you do not already have a Komga or Kavita network you need to create one with docker network create my_network, then attach the media server and komf to it with docker network connect for each, and finally start the container with docker start komf. The reason is that the base URIs point at service names inside a shared network, so the two containers have to be able to resolve each other by name.
Building and running the JAR directly
The build is a Gradle command, and the output lands in a directory:
The build command is ./gradlew :komf-app:clean :komf-app:shadowjar, and the output is in komf-app/build/libs. The shadow jar is what the Docker image copies, and the name of that artefact appears in the Dockerfile with the copy and the entry point:
The Dockerfile copies that shadow jar into the image, sets the locale environment variable to en_US.UTF-8 and the config directory variable to /config, uses the jar as the entry point, and exposes port 8085.
The README documents running it with a JAR in two steps. You need Java 17 or higher on the system, and you run the jar with the path to the config directory as its argument, in the form java -jar followed by the jar name and the config path.
Note the two different jar names, the komf-1.0-SNAPSHOT-all.jar in the running instructions and the komf-app-1.0.0-SNAPSHOT-all.jar in the Dockerfile, and note that the image itself is built on a Java 21 runtime while the standalone instructions ask for Java 17 or higher. The Dockerfile also declares a locale of en_US.UTF-8 and the config directory environment variable pointing at the mounted volume.
The image is built with multi-architecture base stages, one for amd64 and one for arm64, and there is a separate base for the older 17 runtime. The build stage installs pipx and then apprise through it, and apprise is the notification mechanism that the notificationsLibraryFilter and the notification configuration refer to.
The module layout, and what the repository is made of
The top level of the repository is a Kotlin multi-module Gradle build, and the module names describe the structure of the service.
There is komf-app, which is the application module that produces the shadow jar, komf-core, which is the domain logic, komf-client, which talks to the metadata providers, komf-api-models, which holds the shared data models, komf-mediaserver, which is the layer that knows about Komga and Kavita, and komf-notifications, which is the notification side. There is also Komelia, a directory with a capital K, and a kotlin-js-store directory, which is where the browser extension code lives.
The build files at the top are build.gradle.kts, settings.gradle.kts, gradle.properties and the gradle wrapper with gradlew and gradlew.bat for Windows. There is a .gitmodules file, a .idea directory for IntelliJ, and a Dockerfile at the root.
The kotlin-js-store module is worth singling out, because it explains the browser extension approach. Rather than asking you to copy a token or paste an identifier into a web form, the extension runs in the page where the series is listed and talks to the komf service directly, so identifying a series is a click in the Komga or Kavita interface.
That is the design of the whole tool in one line. The Komelia and core modules hold the matching logic, the mediaserver module abstracts over the two server implementations, the client module holds the provider integrations, and the extensions make the manual path as painless as the automatic one.
Where komf is the wrong choice
The first wrong case is a library that is not hosted on Komga or Kavita. The whole service is built around those two servers, with an event listener that connects to them and a metadata update section that writes back to them. If you use a different comic server, there is nothing here for you, and the parts that would be reusable, the provider clients and the aggregation logic, are not exposed as a library.
The second is a library you want to keep exactly as it is. Metadata fetching and cover replacement rewrite files on disk in your library, and even with the conservative defaults the setting exists to upload a new cover and select it. Enabling aggregate and the merge flags takes that further, and the ComicInfo handling has a mode that replaces the existing file rather than appending to it. If your library is a curated archive rather than a working collection, that is the wrong tool.
The third is a privacy-sensitive library. The aggregate mode searches and merges metadata from all configured providers, which means your series list goes out to each of them, and the service is configured with your server credentials in environment variables and in the configuration file. That is normal for a tool like this and worth stating explicitly for a shared or hosted setup.
The fourth is a server that already enriches itself. Both Komga and Kavita have their own metadata capabilities, and some comic managers bundle a metadata plugin. If your existing setup already pulls metadata and covers, komf is a second system doing the same job with a different configuration surface.
The alternative worth weighing is doing the enrichment by hand with the provider tools directly, which is slow but leaves nothing running and nothing rewriting files. For a small library that is often the honest choice, and komf earns its keep when the library is large enough that manual matching stops being practical.
Editorial conclusion
Adopt komf if you run Komga or Kavita and want your library enriched with provider metadata, publisher tags and cover art without doing it by hand, since the event listener and the API update modes cover both the automatic and the one-off cases. Keep away if you need a hosted service, since this is a container or JAR you run yourself and point at your own Komga or Kavita instance. Before you deploy it, read the metadataUpdate section of the sample application.yml, because the default library type is MANGA and the defaults do not write covers or titles, and note the release line moved to 2.x on 2026-09-01 with 2.1.0 published on 2026-09-27.
Frequently asked questions
What does komf do for a Komga or Kavita library?
It fetches metadata and cover images for the series in your library, either automatically when new entries are added, or manually when you search and identify series, and it can match an entire library or a single series. It also writes ComicInfo data when the COMIC_INFO update mode is enabled.
How do I run komf with Docker Compose?
Use the image sndxr/komf:latest, map port 8085 to 8085, run it as user 1000 to 1000, and pass the Komga and Kavita connection details as environment variables such as KOMF_KOMGA_BASE_URI and KOMF_KAVITA_API_KEY. Mount a host directory containing application.yml and the database file at /config, and set the restart policy to unless-stopped.
What is the default behaviour of the komf metadata update settings?
Aggregate is off, so a provider is chosen rather than all of them being merged, book and series covers are off, overrideExistingCovers is true, and overrideComicInfo is false, which means an existing ComicInfo file is only appended to rather than replaced. The library type defaults to MANGA.
How do I build komf from source and run it without Docker?
Run ./gradlew :komf-app:clean :komf-app:shadowjar, and the output is in komf-app/build/libs. You need Java 17 or higher, and you run the jar with the path to your config directory as its argument, for example java -jar komf-1.0-SNAPSHOT-all.jar with the config path.
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/snd-r-komf)Was this useful?