Self-hosted service
grimmory-tools/grimmory avatar
grimmory-tools/grimmory

Grimmory: a self-hosted library server that reads, watches and serves

A self-hosted library for your ebooks, comics, and audiobooks

4,707 stars370 forksJavaAGPL-3.0

At a glance

What is it?
Grimmory is a Java and Angular server that keeps ebooks, comics and audiobooks in a MariaDB index and serves them over OPDS, to a Kobo and to KOReader. The BookDrop folder is its sharpest idea and DISK_TYPE=NETWORK is its sharpest edge.
Who is it for?
Adopt Grimmory if you already run Docker and MariaDB, keep books on a local disk, and read on more than one device: a Kobo, KOReader, an OPDS app and the browser reader all draw from the same MariaDB index, and BookDrop gives you a review step instead of silent imports.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • grimmory-tools/grimmory4,707 ★ · +395
Star history
Repository2026-09-132026-11-14
grimmory-tools/grimmory4.3k4.7k

395 Stars in the last 62 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One container, one MariaDB, and a library that lives on your disk

Grimmory answers a narrow question: where does a personal collection of EPUB, PDF, CBZ and M4B files live once you want more than one device reading it. The answer is a server you run yourself. The stack is a Gradle-built Java backend with an Angular frontend compiled into the same jar, a MariaDB database, and a volume whose files stay on the host rather than inside the image.

The supported formats are grouped by category rather than by library features. eBooks are EPUB, MOBI, AZW, AZW3 and FB2. Comics are CBZ, CBR and CB7. Audiobooks are M4B, M4A, MP3 and OPUS. PDF sits in its own documents row. A built-in reader covers PDFs, EPUBs and comics in the browser, with highlights and reading progress recorded, so the common case never leaves the tab.

Two things shape who this suits. Authentication is either local accounts or OIDC, and shelves, progress and preferences are per user, so a household with three readers is a supported shape rather than an accident. Metadata for covers, descriptions, reviews and ratings is pulled from Google Books, Open Library and Amazon, and every field stays editable afterwards, which matters when the automatic match lands on the wrong edition.

How BookDrop turns a mounted directory into a review queue

The importer is a watched folder, and the volume definition for it is only two lines:

yaml
volumes:
  - ./bookdrop:/bookdrop

Once that host directory is mounted at /bookdrop, Grimmory monitors it continuously. New files are detected and parsed, metadata is fetched from Google Books and Open Library, and the result waits for you to review before it joins the library. Files are never added silently, which is the right default when a dropped file is a scanned volume with no usable metadata.

The catch sits in the same design. The README states that a library must be created inside a directory that is actually mounted on the host, for example the /books/ directory in the sample compose file.

Device delivery follows the same host-path rule. Sharing a book to a Kindle, an email address or another Grimmory user, connecting a Kobo, and progress sync with KOReader, all operate on files Grimmory can see. That is why the storage choice below is not a preference.

DISK_TYPE=NETWORK quietly removes delete, move and rename

Set DISK_TYPE=NETWORK and Grimmory runs against an NFS or SMB share. Reading, metadata and sync keep working. Delete, move and rename from the interface are disabled, because Grimmory does not want to make destructive changes on a shared mount.

This is the clearest failure mode in the project, and it is a deliberate one. If your storage is network-backed, you can browse and read but you cannot fix a bad file from the browser; correcting it means mounting the share elsewhere. If your storage is local, DISK_TYPE=LOCAL is the default and nothing changes.

The sample .env ships placeholder database passwords

The environment template that starts the Quick Start sets DB_PASSWORD=ChangeMe_Grimmory_2025! and MYSQL_ROOT_PASSWORD=ChangeMe_MariaDBRoot_2025!. Those are literal values in the sample configuration, not placeholders the tooling replaces. Anyone who copies the .env verbatim and exposes port 6060 to a network has published a MariaDB root password that is written in the project's own README.

The rest of the .env is mostly identity mapping, which is the part that trips people up. APP_USER_ID and APP_GROUP_ID feed the container's USER_ID and GROUP_ID, and separate DB_USER_ID and DB_GROUP_ID do the same for MariaDB, so host and container file ownership has to agree or Grimmory writes files your account cannot read. TZ=Etc/UTC is the shipped default, DATABASE_URL points at jdbc:mariadb://mariadb:3306/grimmory, and API_DOCS_ENABLED defaults to false.

One more detail worth reading twice: the repository default branch is develop, yet published images are built from main as vX.Y.Z plus latest, while nightly builds come from develop. Building from the default branch and tagging it latest are not the same thing.

Bringing it up: compose file, then a real first import

Images ship from two registries, grimmory/grimmory on Docker Hub and ghcr.io/grimmory-tools/grimmory on GitHub Container Registry, and either can be pinned to a specific version tag. Copy or adapt deploy/compose/docker-compose.yml rather than assembling your own, because that file carries the volume mounts the library depends on. Helm and Podman Quadlet examples live under deploy/helm and deploy/podman if compose is not your packaging format.

Start it:

bash
docker compose up -d

Open http://localhost:6060, create the admin account, and create your first library inside a mounted directory. Then drop one file into the mounted bookdrop directory and confirm it appears in the review queue with metadata attached before importing anything in bulk.

Set API_DOCS_ENABLED=true while you are setting things up and the endpoints are published at http://localhost:6060/api/docs, with the machine-readable spec at http://localhost:6060/api/openapi.json. Leave it off afterwards.

Developers do not need compose at all. The root Justfile mirrors the backend and frontend recipes:

bash
just               # Show root + api + ui recipes
just test          # Run backend and frontend tests
just api test      # Run backend tests only
just ui dev        # Start the frontend dev server

The workspace pins [email protected] and requires Node 24 or newer.

OPDS, Kobo and KOReader rather than a desktop sync client

Where Calibre is a desktop library manager that owns the files and converts between formats, Grimmory serves. It exposes OPDS, which is the standard catalogue protocol for reading apps, connects a Kobo, and syncs progress with KOReader. A Kobo connects directly; anything OPDS-compatible can browse the shelves it defines.

That difference decides who wins. If your library is scattered across a laptop, a desktop and a NAS, and you want format conversion as the primary job, Calibre's model fits. If the books are already on a server you control and you read on a Kobo, a browser and a phone, the OPDS approach fits and the conversion question never comes up.

Audiobookshelf and BookLore are the closer comparisons since both are server-side.

Heap dumps for the OOM case, off by default

When the JVM runs out of memory there is no diagnostic output unless you ask for it. Heap dumps are disabled by default, and enabling them means adding JDK_JAVA_OPTIONS=-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/data to the environment of the grimmory service in the compose file. The JVM then writes a PID-specific .hprof file into the mounted /app/data directory. The project warns to remove the option afterwards, because those files can be large and can hold sensitive application data.

The database is where upgrade risk sits. The Quick Start points to the full documentation for upgrade guides, so pin an image tag rather than tracking latest across a major version.

The repository is not archived, and the last push was on 2026-11-02. The licence is AGPL-3.0, which carries weight if you modify the server and pass it to people outside your household.

Editorial conclusion

Adopt Grimmory if you already run Docker and MariaDB, keep books on a local disk, and read on more than one device: a Kobo, KOReader, an OPDS app and the browser reader all draw from the same MariaDB index, and BookDrop gives you a review step instead of silent imports. Skip it if your library lives on NFS or SMB and you expect to rename or delete files from the interface, because DISK_TYPE=NETWORK removes exactly those operations, and skip it if format conversion is the job you actually need. Before deploying, confirm three things: that every library path sits inside a host mount, that you have replaced DB_PASSWORD and MYSQL_ROOT_PASSWORD from the sample .env, and that the reading app you care about authenticates against the instance rather than expecting an unlisted local database.

Frequently asked questions

Is Grimmory self-hosted?

Yes. Grimmory runs on your own hardware as a Docker container backed by MariaDB, with the library files stored in a directory you mount from the host.

What is Grimmory?

Grimmory is a self-hosted digital library for ebooks, comics and audiobooks, with a built-in browser reader for PDF, EPUB and comics, per-user shelves and OPDS access for external reading apps.

How do I install Grimmory?

You need Docker and Docker Compose, a .env file with database credentials, and a docker-compose.yml adapted from deploy/compose/docker-compose.yml. Start it with docker compose up -d and open http://localhost:6060 to create the admin account.

What is an alternative to Grimmory?

Calibre takes a different approach: it is a desktop library manager that owns and converts files, while Grimmory leaves files on a mounted volume and serves them over OPDS, to a Kobo or to KOReader.

Official sources

  1. grimmory-tools/grimmory on GitHub
  2. License: AGPL-3.0
  3. Project website
  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/grimmory-tools-grimmory.svg)](https://hysenlabs.com/projects/grimmory-tools-grimmory)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/grimmory-tools-grimmory.svg)](https://hysenlabs.com/projects/grimmory-tools-grimmory)

Was this useful?