
grimmory-self-hosted-library
Expert knowledge for setting up, configuring, and extending Grimmory — a self-hosted book library manager supporting EPUBs, PDFs, comics, Kobo sync, OPDS, and multi-user management.
- Installs
- 1.3K
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/reason-machines/trending-skills --skill grimmory-self-hosted-library- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads grimmory-self-hosted-library automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Grimmory Self-Hosted Library Manager
Skill by ara.so — Daily 2026 Skills collection.
Grimmory is a self-hosted application (successor to BookLore) for managing your entire book collection. It supports EPUBs, PDFs, MOBIs, AZW/AZW3, and comics (CBZ/CBR/CB7), with a built-in browser reader, annotations, Kobo/OPDS sync, KOReader progress sync, metadata enrichment, and multi-user support.
Installation
Requirements
- Docker and Docker Compose
Step 1: Create .env
# Application
APP_USER_ID=1000
APP_GROUP_ID=1000
TZ=Etc/UTC
# Database
DATABASE_URL=jdbc:mariadb://mariadb:3306/grimmory
DB_USER=grimmory
DB_PASSWORD=${DB_PASSWORD}
# Storage: LOCAL (default) or NETWORK
DISK_TYPE=LOCAL
# MariaDB
DB_USER_ID=1000
DB_GROUP_ID=1000
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE=grimmory
Step 2: Create docker-compose.yml
services:
grimmory:
image: grimmory/grimmory:latest
# Alternative registry: ghcr.io/grimmory-tools/grimmory:latest
container_name: grimmory
environment:
- USER_ID=${APP_USER_ID}
- GROUP_ID=${APP_GROUP_ID}
- TZ=${TZ}
- DATABASE_URL=${DATABASE_URL}
- DATABASE_USERNAME=${DB_USER}
- DATABASE_PASSWORD=${DB_PASSWORD}
- DISK_TYPE=${DISK_TYPE}
depends_on:
mariadb:
condition: service_healthy
ports:
- "6060:6060"
volumes:
- ./data:/app/data
- ./books:/books
- ./bookdrop:/bookdrop
healthcheck:
test: wget -q -O - http://localhost:6060/api/v1/healthcheck
interval: 60s
retries: 5
start_period: 60s
timeout: 10s
restart: unless-stopped
mariadb:
image: lscr.io/linuxserver/mariadb:11.4.5
container_name: mariadb
environment:
- PUID=${DB_USER_ID}
- PGID=${DB_GROUP_ID}
- TZ=${TZ}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- ./mariadb/config:/config
restart: unless-stopped
healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 10
Step 3: Launch
docker compose up -d
# View logs
docker compose logs -f grimmory
# Check health
curl http://localhost:6060/api/v1/healthcheck
Open http://localhost:6060 and create your admin account.
Volume Layout
./data/ # App data, thumbnails, user config
./books/ # Your book files (mounted at /books)
./bookdrop/ # Drop-zone for auto-import (mounted at /bookdrop)
./mariadb/ # MariaDB data
Environment Variables Reference
| Variable | Description | Default |
|---|---|---|
USER_ID | UID for the app process | 1000 |
GROUP_ID | GID for the app process | 1000 |
TZ | Timezone string | Etc/UTC |
DATABASE_URL | JDBC connection string | required |
DATABASE_USERNAME | DB username | required |
DATABASE_PASSWORD | DB password | required |
DISK_TYPE | LOCAL or NETWORK | LOCAL |
Supported Book Formats
| Category | Formats |
|---|---|
| eBooks | EPUB, MOBI, AZW, AZW3 |
| Documents | |
| Comics | CBZ, CBR, CB7 |
BookDrop (Auto-Import)
Drop files into ./bookdrop/ on your host. Grimmory watches the folder, extracts metadata from Google Books and Open Library, and queues books for review.
./bookdrop/
my-novel.epub ← dropped here
another-book.pdf ← dropped here
Flow:
- Watch — Grimmory monitors
/bookdropcontinuously - Detect — New files are picked up and parsed
- Enrich — Metadata fetched from Google Books / Open Library
- Import — Review in UI, adjust if needed, confirm import
Volume mapping required in docker-compose.yml:
volumes:
- ./bookdrop:/bookdrop
Network Storage Mode
For NFS, SMB, or other network-mounted filesystems, set DISK_TYPE=NETWORK. This disables destructive UI operations (delete, move, rename) to protect shared mounts while keeping reading, metadata, and sync fully functional.
# .env
DISK_TYPE=NETWORK
Java Backend — Key Patterns
Grimmory is a Java application (Spring Boot + MariaDB). When contributing or extending:
Project Structure (typical Spring Boot layout)
src/main/java/
com/grimmory/
config/ # Spring configuration classes
controller/ # REST API controllers
service/ # Business logic
repository/ # JPA repositories
model/ # JPA entities
dto/ # Data transfer objects
REST API — Base Path
All endpoints are under /api/v1/:
# Health check
GET http://localhost:6060/api/v1/healthcheck
# Books
GET http://localhost:6060/api/v1/books
GET http://localhost:6060/api/v1/books/{id}
POST http://localhost:6060/api/v1/books
PUT http://localhost:6060/api/v1/books/{id}
DELETE http://localhost:6060/api/v1/books/{id}
# Shelves
GET http://localhost:6060/api/v1/shelves
POST http://localhost:6060/api/v1/shelves
# OPDS catalog (for compatible reader apps)
GET http://localhost:6060/opds
Example: Querying the API with Java (OkHttp)
import okhttp3.*;
import com.fasterxml.jackson.databind.ObjectMapper;
public class GrimmoryClient {
private final OkHttpClient http = new OkHttpClient();
private final ObjectMapper mapper = new ObjectMapper();
private final String baseUrl;
private final String token;
public GrimmoryClient(String baseUrl, String token) {
this.baseUrl = baseUrl;
this.token = token;
}
public String getBooks() throws Exception {
Request request = new Request.Builder()
.url(baseUrl + "/api/v1/books")
.header("Authorization", "Bearer " + token)
.build();
try (Response response = http.newCall(request).execute()) {
return response.body().string();
}
}
}
Example: Spring Boot Controller Pattern
@RestController
@RequestMapping("/api/v1/books")
@RequiredArgsConstructor
public class BookController {
private final BookService bookService;
@GetMapping
public ResponseEntity<Page<BookDto>> getAllBooks(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String search) {
return ResponseEntity.ok(bookService.findAll(page, size, search));
}
@GetMapping("/{id}")
public ResponseEntity<BookDto> getBook(@PathVariable Long id) {
return ResponseEntity.ok(bookService.findById(id));
}
@PostMapping
public ResponseEntity<BookDto> createBook(@RequestBody @Valid CreateBookRequest request) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(bookService.create(request));
}
@PutMapping("/{id}/metadata")
public ResponseEntity<BookDto> updateMetadata(
@PathVariable Long id,
@RequestBody @Valid UpdateMetadataRequest request) {
return ResponseEntity.ok(bookService.updateMetadata(id, request));
}
}
Example: JPA Entity Pattern
@Entity
@Table(name = "books")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
private String author;
private String isbn;
private String format; // EPUB, PDF, CBZ, etc.
@Column(name = "file_path")
private String filePath;
@Column(name = "cover_path")
private String coverPath;
@Column(name = "reading_progress")
private Double readingProgress;
@ManyToMany
@JoinTable(
name = "book_shelf",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "shelf_id")
)
private Set<Shelf> shelves = new HashSet<>();
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
Example: Service with Metadata Enrichment
@Service
@RequiredArgsConstructor
public class MetadataService {
private final GoogleBooksClient googleBooksClient;
private final OpenLibraryClient openLibraryClient;
private final BookRepository bookRepository;
public BookDto enrichMetadata(Long bookId) {
Book book = bookRepository.findById(bookId)
.orElseThrow(() -> new BookNotFoundException(bookId));
// Try Google Books first
Optional<BookMetadata> metadata = googleBooksClient.search(book.getTitle(), book.getAuthor());
// Fall back to Open Library
if (metadata.isEmpty()) {
metadata = openLibraryClient.search(book.getIsbn());
}
metadata.ifPresent(m -> {
book.setDescription(m.getDescription());
book.setCoverUrl(m.getCoverUrl());
book.setPublisher(m.getPublisher());
book.setPublishedDate(m.getPublishedDate());
bookRepository.save(book);
});
return BookDto.from(book);
}
}
OPDS Integration
Connect any OPDS-compatible reader app (Kybook, Chunky, Moon+ Reader, etc.) using:
http://<your-host>:6060/opds
Authenticate with your Grimmory username and password when prompted.
Kobo / KOReader Sync
- Kobo: Connect via the device sync feature in Grimmory settings. The app exposes a sync endpoint compatible with Kobo's API.
- KOReader: Configure KOReader's sync plugin to point to your Grimmory instance URL.
Multi-User & Authentication
Local Authentication
Create users from the admin panel at http://localhost:6060. Each user has isolated shelves, reading progress, and preferences.
OIDC Authentication
Configure via environment variables (refer to full documentation at https://grimmory.org/docs/getting-started for OIDC-specific variables such as OIDC_ISSUER_URI, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET).
Building from Source
# Clone the repository
git clone https://github.com/grimmory-tools/grimmory.git
cd grimmory
# Build with Maven
./mvnw clean package -DskipTests
# Or build Docker image locally
docker build -t grimmory:local .
# Use local build in docker-compose.yml
# Comment out 'image' and uncomment 'build: .'
Common Docker Commands
# Start services
docker compose up -d
# Stop services
docker compose down
# View app logs
docker compose logs -f grimmory
# View DB logs
docker compose logs -f mariadb
# Restart only the app
docker compose restart grimmory
# Pull latest image and redeploy
docker compose pull && docker compose up -d
# Open a shell inside the container
docker exec -it grimmory /bin/bash
# Database shell
docker exec -it mariadb mariadb -u grimmory -p grimmory
Troubleshooting
Container won't start — DB connection refused
# Check MariaDB health
docker compose ps mariadb
# Should show "healthy". If not:
docker compose logs mariadb
# Ensure DATABASE_URL host matches the service name: mariadb:3306
Books not appearing after BookDrop
# Verify file permissions — UID/GID must match APP_USER_ID/APP_GROUP_ID
ls -la ./bookdrop/
# Check app logs for detection events
docker compose logs -f grimmory | grep -i bookdrop
Permission denied on ./books or ./data
# Set ownership to match APP_USER_ID / APP_GROUP_ID
sudo chown -R 1000:1000 ./books ./data ./bookdrop
OPDS not accessible from reader app
# Confirm port 6060 is reachable from your device
curl http://<host-ip>:6060/api/v1/healthcheck
# Check firewall rules if on a remote server
High memory usage
MariaDB and Grimmory together require at minimum ~512 MB RAM. For large libraries (10k+ books), allocate 1–2 GB.
Metadata not enriching
Google Books and Open Library require outbound internet access from the container. Verify DNS and network:
docker exec -it grimmory curl -s "https://www.googleapis.com/books/v1/volumes?q=test"
Contributing
Before opening a pull request:
- Open an issue and get maintainer approval
- Include screenshots/video proof and pasted test output
- Follow backend and frontend conventions in
CONTRIBUTING.md - AI-assisted code is allowed but you must run, test, and understand every line
# Run tests before submitting
./mvnw test
# Check code style
./mvnw checkstyle:check
Links
- GitHub: https://github.com/grimmory-tools/grimmory
- Docker Hub: https://hub.docker.com/r/grimmory/grimmory
- GHCR:
ghcr.io/grimmory-tools/grimmory - Discord: https://discord.gg/FwqHeFWk
- Docs: https://grimmory.org/docs/getting-started
- License: AGPL-3.0
FAQ
What does the grimmory-self-hosted-library skill do?
Expert knowledge for setting up, configuring, and extending Grimmory — a self-hosted book library manager supporting EPUBs, PDFs, comics, Kobo sync, OPDS, and multi-user management.
How do I install grimmory-self-hosted-library?
Run npx skills add https://github.com/reason-machines/trending-skills --skill grimmory-self-hosted-library in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/grimmory-self-hosted-library/ (personal) or .claude/skills/grimmory-self-hosted-library/ (project).
Which AI agents can use grimmory-self-hosted-library?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is grimmory-self-hosted-library safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains grimmory-self-hosted-library?
It is published by reason-machines in reason-machines/trending-skills. It has 1,275 recorded installs.
More from reason-machines/trending-skills
openclaw-control-center
reason-machines/trending-skills
Local-first, security-first control center for OpenClaw agents — visibility dashboard with readonly defaults, token attribution, collaboration tracing, and safe write operations.
inkos-multi-agent-novel-writing
reason-machines/trending-skills
Multi-agent CLI system for autonomous novel writing, auditing, and revision with human review gates
claude-hud-statusline
reason-machines/trending-skills
A Claude Code plugin that displays a real-time HUD showing context usage, active tools, running agents, and todo progress in your terminal statusline.
understand-anything-knowledge-graph
reason-machines/trending-skills
Turn any codebase into an interactive knowledge graph using Claude Code skills — explore, search, and ask questions about any project visually.
agency-agents-ai-specialists
reason-machines/trending-skills
A collection of specialized AI agent personalities for Claude Code, Cursor, Aider, Windsurf, and other AI coding tools — covering engineering, design, marketing, sales, and more.
agent-browser-automation
reason-machines/trending-skills
Headless browser automation CLI for AI agents using native Rust binary with Chrome DevTools Protocol
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.