Grimmory
Self-hosted digital library — successor to BookLore, with smart shelves, metadata, Kobo/KOReader sync, OPDS support, and a built-in reader.
Smart Shelves — Rule-based filtering, tagging, and full-text search
Built-in Reader — In-browser reading for PDFs, EPUBs, and comics with annotations and progress tracking
Metadata Lookup — Book info from Google Books, Open Library, and Amazon (fully editable)
Device Sync — Kobo compatibility, OPDS support, and KOReader synchronization
BookDrop — Drop files into a watched folder; Grimmory detects, enriches metadata, and queues for review
Multi-User — Individual shelves and preferences with local or OIDC authentication
One-Click Sharing — Send books to Kindle, email, or other users
Supported formats: EPUB, MOBI, AZW, AZW3, FB2, PDF, CBZ, CBR, CB7, M4B, M4A, MP3, OPUS
Tag
Description
Best For
latest
Upstream Binary . Built from official release.
Most users — recommended.
Parts
Service
Image
Role
grimmory
ghcr.io/daemonless/grimmory:latest
docs
mariadb
ghcr.io/daemonless/mariadb:latest
docs
Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions, including the security model for host vs. container privileges.
Deployment
Database Where Grimmory keeps its library. The default runs its own MariaDB.
Built-in MariaDB Your own MariaDB
grimmory:latest · mariadb:latest — data in /containers /grimmory/data, /containers /grimmory/books, /containers /grimmory/bookdrop, /containers /grimmory/mariadb
A MariaDB of its own, on the stack's private network.
1. Save as .env and fill in what is empty:
# Grimmory for FreeBSD (Daemonless)
# Copy to .env and edit.
# Where data lives on the host
DATA_LOCATION=/containers /grimmory/data
EBOOKS_PATH=/containers /grimmory/books
BOOKDROP_PATH=/containers /grimmory/bookdrop
DB_DATA_LOCATION=/containers /grimmory/mariadb
# Database passwords. Change these to random passwords!
DB_PASSWORD=grimmory
DB_ROOT_PASSWORD=grimmory-root
# Timezone (TZ identifier)
TZ=UTC
2. Save as compose.yaml:
name : grimmory
services :
grimmory :
image : ghcr.io/daemonless/grimmory:latest
container_name : grimmory
network_mode : host
environment :
- PUID=1000
- PGID=1000
- TZ=${TZ:-Etc/UTC}
- DATABASE_URL=jdbc:mariadb://${MARIADB_HOSTNAME:-127.0.0.1}:${MARIADB_PORT:-3306}/${MARIADB_DATABASE:-grimmory}
- DATABASE_USERNAME=${MARIADB_USER:-grimmory}
- DATABASE_PASSWORD=${DB_PASSWORD}
- SWAGGER_ENABLED=false
- FORCE_DISABLE_OIDC=false
volumes :
- ${DATA_LOCATION}:/app/data
- ${EBOOKS_PATH}:/books
- ${BOOKDROP_PATH}:/bookdrop
depends_on :
- mariadb
restart : unless-stopped
mariadb :
image : ghcr.io/daemonless/mariadb:latest
container_name : grimmory-mariadb
network_mode : host
environment :
- PUID=1000
- PGID=1000
- TZ=${TZ:-Etc/UTC}
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=grimmory
- MYSQL_USER=grimmory
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes :
- ${DB_DATA_LOCATION}:/config
restart : unless-stopped
Then run podman-compose up -d.
grimmory:latest · your own database — data in /containers /grimmory/data, /containers /grimmory/books, /containers /grimmory/bookdrop
A MariaDB you already run. Create the database and a user with all privileges on it first.
1. Save as .env and fill in Host, Port, Database name, User, Password:
# Grimmory for FreeBSD (Daemonless)
# Copy to .env and edit.
# Where data lives on the host
DATA_LOCATION=/containers /grimmory/data
EBOOKS_PATH=/containers /grimmory/books
BOOKDROP_PATH=/containers /grimmory/bookdrop
DB_DATA_LOCATION=/containers /grimmory/mariadb
# Database passwords. Change these to random passwords!
DB_PASSWORD= # Password
DB_ROOT_PASSWORD=grimmory-root
# Timezone (TZ identifier)
TZ=UTC
# Database: Your own MariaDB
MARIADB_HOSTNAME= # Host
MARIADB_PORT=3306 # Port
MARIADB_DATABASE=grimmory # Database name
MARIADB_USER=grimmory # User
2. Save as compose.yaml:
name : grimmory
services :
grimmory :
image : ghcr.io/daemonless/grimmory:latest
container_name : grimmory
network_mode : host
environment :
- PUID=1000
- PGID=1000
- TZ=${TZ:-Etc/UTC}
- DATABASE_URL=jdbc:mariadb://${MARIADB_HOSTNAME:-127.0.0.1}:${MARIADB_PORT:-3306}/${MARIADB_DATABASE:-grimmory}
- DATABASE_USERNAME=${MARIADB_USER:-grimmory}
- DATABASE_PASSWORD=${DB_PASSWORD}
- SWAGGER_ENABLED=false
- FORCE_DISABLE_OIDC=false
volumes :
- ${DATA_LOCATION}:/app/data
- ${EBOOKS_PATH}:/books
- ${BOOKDROP_PATH}:/bookdrop
restart : unless-stopped
Then run podman-compose up -d.
Database Where Grimmory keeps its library. The default runs its own MariaDB.
Built-in MariaDB Your own MariaDB
grimmory:latest · mariadb:latest — data in /containers /grimmory/data, /containers /grimmory/books, /containers /grimmory/bookdrop, /containers /grimmory/mariadb
A MariaDB of its own, on the stack's private network.
# .env
DIRECTOR_PROJECT=grimmory
PUID=1000
PGID=1000
TZ=UTC
DATABASE_URL=jdbc:mariadb://${MARIADB_HOSTNAME:-127.0.0.1}:${MARIADB_PORT:-3306}/${MARIADB_DATABASE:-grimmory}
DATABASE_USERNAME=grimmory
DATABASE_PASSWORD=${DB_PASSWORD}
SWAGGER_ENABLED=false
FORCE_DISABLE_OIDC=false
DATA_LOCATION=/containers/grimmory/data
EBOOKS_PATH=/containers/grimmory/books
BOOKDROP_PATH=/containers/grimmory/bookdrop
DB_DATA_LOCATION=/containers/grimmory/mariadb
DB_PASSWORD=grimmory
DB_ROOT_PASSWORD=grimmory-root
# appjail-director.yml
options :
- alias :
- ip4_inherit :
services :
grimmory :
name : grimmory
options :
- container : 'args:--pull'
oci :
user : root
environment :
- PUID : !ENV '${PUID}'
- PGID : !ENV '${PGID}'
- TZ : !ENV '${TZ}'
- DATABASE_URL : !ENV '${DATABASE_URL}'
- DATABASE_USERNAME : !ENV '${DATABASE_USERNAME}'
- DATABASE_PASSWORD : !ENV '${DATABASE_PASSWORD}'
- SWAGGER_ENABLED : !ENV '${SWAGGER_ENABLED}'
- FORCE_DISABLE_OIDC : !ENV '${FORCE_DISABLE_OIDC}'
- DATA_LOCATION : !ENV '${DATA_LOCATION}'
- EBOOKS_PATH : !ENV '${EBOOKS_PATH}'
- BOOKDROP_PATH : !ENV '${BOOKDROP_PATH}'
- DB_DATA_LOCATION : !ENV '${DB_DATA_LOCATION}'
- DB_PASSWORD : !ENV '${DB_PASSWORD}'
- DB_ROOT_PASSWORD : !ENV '${DB_ROOT_PASSWORD}'
volumes :
- GRIMMORY_DATA_PATH : /app/data
- BOOKS_PATH : /books
- GRIMMORY_BOOKDROP_PATH : /bookdrop
mariadb :
name : mariadb
options :
- from : ghcr.io/daemonless/mariadb:latest
- template : !ENV '${PWD}/template.conf'
oci :
environment :
- PUID : "1000"
- PGID : "1000"
- TZ : !ENV '${TZ}'
- MYSQL_ROOT_PASSWORD : !ENV '${DB_ROOT_PASSWORD}'
- MYSQL_DATABASE : "grimmory"
- MYSQL_USER : "grimmory"
- MYSQL_PASSWORD : !ENV '${DB_PASSWORD}'
volumes :
- db_data : /config
volumes :
GRIMMORY_DATA_PATH :
device : '/containers /grimmory/data '
BOOKS_PATH :
device : '/path/to/books '
GRIMMORY_BOOKDROP_PATH :
device : '/containers /grimmory/bookdrop '
db_data :
device : !ENV '${DB_DATA_LOCATION}'
# Makejail
ARG tag=latest
OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless /grimmory:${tag}
Save the files above, then run appjail-director up.
grimmory:latest · your own database — data in /containers /grimmory/data, /containers /grimmory/books, /containers /grimmory/bookdrop
A MariaDB you already run. Create the database and a user with all privileges on it first.
# .env
DIRECTOR_PROJECT=grimmory
PUID=1000
PGID=1000
TZ=UTC
DATABASE_URL=jdbc:mariadb://${MARIADB_HOSTNAME:-127.0.0.1}:${MARIADB_PORT:-3306}/${MARIADB_DATABASE:-grimmory}
DATABASE_USERNAME=grimmory
DATABASE_PASSWORD=${DB_PASSWORD}
SWAGGER_ENABLED=false
FORCE_DISABLE_OIDC=false
DATA_LOCATION=/containers/grimmory/data
EBOOKS_PATH=/containers/grimmory/books
BOOKDROP_PATH=/containers/grimmory/bookdrop
DB_DATA_LOCATION=/containers/grimmory/mariadb
DB_PASSWORD=
DB_ROOT_PASSWORD=grimmory-root
# appjail-director.yml
options :
- alias :
- ip4_inherit :
services :
grimmory :
name : grimmory
options :
- container : 'args:--pull'
oci :
user : root
environment :
- PUID : !ENV '${PUID}'
- PGID : !ENV '${PGID}'
- TZ : !ENV '${TZ}'
- DATABASE_URL : !ENV '${DATABASE_URL}'
- DATABASE_USERNAME : !ENV '${DATABASE_USERNAME}'
- DATABASE_PASSWORD : !ENV '${DATABASE_PASSWORD}'
- SWAGGER_ENABLED : !ENV '${SWAGGER_ENABLED}'
- FORCE_DISABLE_OIDC : !ENV '${FORCE_DISABLE_OIDC}'
- DATA_LOCATION : !ENV '${DATA_LOCATION}'
- EBOOKS_PATH : !ENV '${EBOOKS_PATH}'
- BOOKDROP_PATH : !ENV '${BOOKDROP_PATH}'
- DB_DATA_LOCATION : !ENV '${DB_DATA_LOCATION}'
- DB_PASSWORD : !ENV '${DB_PASSWORD}'
- DB_ROOT_PASSWORD : !ENV '${DB_ROOT_PASSWORD}'
volumes :
- GRIMMORY_DATA_PATH : /app/data
- BOOKS_PATH : /books
- GRIMMORY_BOOKDROP_PATH : /bookdrop
volumes :
GRIMMORY_DATA_PATH :
device : '/containers /grimmory/data '
BOOKS_PATH :
device : '/path/to/books '
GRIMMORY_BOOKDROP_PATH :
device : '/containers /grimmory/bookdrop '
# Makejail
ARG tag=latest
OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless /grimmory:${tag}
Save the files above, then run appjail-director up.
Interactive Configuration
Parameters
Environment Variables
Variable
Default
Description
PUID
1000
PGID
1000
TZ
Etc/UTC
Timezone
DATABASE_URL
jdbc:mariadb://${MARIADB_HOSTNAME:-127.0.0.1}:${MARIADB_PORT:-3306}/${MARIADB_DATABASE:-grimmory}
MariaDB JDBC URL (e.g., jdbc:mariadb://127.0.0.1:3306/grimmory)
DATABASE_USERNAME
grimmory
Database username
DATABASE_PASSWORD
grimmory
Database password
SWAGGER_ENABLED
false
Enable Swagger UI (default: false)
FORCE_DISABLE_OIDC
false
Force-disable OIDC authentication (default: false)
DATA_LOCATION
/containers/grimmory/data
Path to store Grimmory configuration and application data
EBOOKS_PATH
/containers/grimmory/books
Path to your ebook library
BOOKDROP_PATH
/containers/grimmory/bookdrop
Drop folder watched for automatic imports
DB_DATA_LOCATION
/containers/grimmory/mariadb
Path to store MariaDB data files
MARIADB_HOSTNAME
``
Where Grimmory finds MariaDB (fjord sets this for the built-in one; 127.0.0.1 on host networking)
MARIADB_PORT
``
MariaDB port (your own MariaDB)
MARIADB_DATABASE
``
Database name (your own MariaDB)
MARIADB_USER
``
Database user (your own MariaDB)
DB_PASSWORD
grimmory
Database password (grimmory user)
DB_ROOT_PASSWORD
grimmory-root
MariaDB root password (built-in MariaDB only)
JAVA_TOOL_OPTIONS
``
Additional JVM options (e.g., -Xmx256m)
Volumes
Path
Description
/app/data
Configuration and application data
/books
Book library directory
/bookdrop
Drop folder for automatic imports
Implementation Details
Architectures: amd64
User: bsd (UID/GID set via PUID/PGID ). Defaults to 1000:1000.
Base: Built on ghcr.io/daemonless /base (FreeBSD 15.1).
Need help? Join our Discord community.