Kavita Book Server: Host Your Own Ebook, PDF, and Manga Library with Docker
Deploy Kavita digital library using Docker Compose. Read ebooks, PDFs, and manga directly through a modern web interface.
Kavita Book Server: Host Your Own Ebook, PDF, and Manga Library with Docker
Folder Organization and Permissions
Deploying Kavita successfully requires establishing a strict directory structure. Kavita relies on predictable folder hierarchies to automatically group series, parse volumes/chapters, and extract metadata.
Recommended Directory Layout
Prepare a dedicated workspace directory on the VPS (typically /opt/kavita):
mkdir -p /opt/kavita/{config,data/{books,manga,comics}}
This yields the following directory structure:
/opt/kavita/
├── docker-compose.yml
├── config/
└── data/
├── books/
├── manga/
└── comics/
Library Folder Hierarchy Conventions
To ensure Kavita correctly groups media without manual intervention, organize files according to their specific type before importing:
- Ebooks (EPUB, PDF):
data/books/ └── Author Name/ ├── Series Name/ │ ├── Book 1 - Title.epub │ └── Book 2 - Title.epub └── Standalone Book.epub - Manga & Comics (CBZ, CBR):
data/manga/ └── Series Name/ ├── Series Name - v01.cbz ├── Series Name - v02.cbz └── Specials/ └── Series Name - Special 01.cbz
Permissions Configuration
Ensure the host user running Docker (typically UID 1000) has read/write permissions for both the configuration and library folders:
chown -R 1000:1000 /opt/kavita
chmod -R 775 /opt/kavita
Docker Compose Configuration
The following docker-compose.yml configures Kavita to run as a non-root container, binds host storage volumes, and exposes the required port.
Save this file directly to /opt/kavita/docker-compose.yml:
version: '3.8'
services:
kavita:
image: kavitareader/kavita:latest
container_name: kavita
restart: unless-stopped
ports:
- "5000:5000"
environment:
- TZ=Etc/UTC
- PUID=1000
- PGID=1000
volumes:
- ./config:/kavita/config
- ./data/books:/books:ro
- ./data/manga:/manga:ro
- ./data/comics:/comics:ro
Configuration Details
- Image: Uses the official
kavitareader/kavita:latestimage. - Ports: Map internal container port
5000to the host port5000. If port5000is already in use on the host, modify the left side of the colon (e.g.,8080:5000). - Volumes:
./config: Binds the localconfig/directory to/kavita/config. This retains user databases, covers, configuration preferences, and system logs../data/*: Binds the media libraries as read-only (:ro) to protect the source library files from accidental modification or deletion by the container processes.
- PUID/PGID: Runs the internal container process with standard system user privileges, preventing file ownership conflicts.
SQLite Database Engine Setup
Kavita relies on an embedded SQLite database engine. SQLite handles all user data, library indexes, metadata stores, and reading progression markers.
Write-Ahead Logging (WAL) Mode
By default, Kavita initiates SQLite in Write-Ahead Logging (WAL) mode. WAL mode significantly boosts concurrent write performance and reduces database lock operations when the server is scanning large libraries while users are reading.
To prevent corruption, adhere to the following database maintenance guidelines:
- Storage Type: Avoid placing the SQLite file (
/kavita/config/kavita.db) on NFS, SMB, or other network-attached filesystems. SQLite relies on POSIX advisory locks, which are often unstable over network shares. Always use local SSD or NVMe storage. - Backups: Copying
kavita.dbwhile the container is actively writing can result in partial or corrupt backup files. Implement an automated backup script that uses the SQLite.backupAPI or temporarily pauses the container:bash # Safe SQLite Backup Script docker stop kavita cp /opt/kavita/config/kavita.db /opt/kavita/config/backups/kavita_$(date +%F).db docker start kavita
Metadata and OPDS Configuration
Kavita automates library discovery by leveraging embedded metadata files and directory scanner logic.
Metadata Specifications
Ensure your libraries conform to standard metadata schemes to enable Kavita's powerful filter and search capabilities:
| Format | Recommended Metadata Standard | Integration Notes |
|---|---|---|
| Ebooks (EPUB) | OPF metadata (embedded) | Ensure title, creator (author), and series tags are present in the internal OPF file. |
| Manga / Comics | ComicInfo.xml (embedded inside .cbz or .cbr) |
Use ComicTagger or Calibre to write ComicInfo.xml schemas directly inside the archives. |
| File names and folders | Since PDFs lack standardized internal metadata, Kavita uses folder layout parsing to extract series and book titles. |
OPDS Feed Configuration
The Open Publication Distribution System (OPDS) protocol allows external client e-readers (such as Panels, Chunky, Moon+ Reader, or Mihon/Tachiyomi) to parse and download books directly from your VPS-hosted library.
To expose the OPDS endpoint:
- Access the Kavita Web UI (
http://<your-vps-ip>:5000). - Navigate to Settings (gear icon) -> Users.
- Locate your admin profile, and copy the unique OPDS Query URL. The URL adheres to the following pattern:
http://<your-vps-ip>:5000/api/opds/<your-unique-api-key> - Open your preferred reader app on your mobile device or tablet.
- Add a new OPDS catalog/feed, and paste the copied URL.
Secure Reverse Proxy Configuration
Exposing port 5000 directly over the public internet exposes your Kavita instance to brute-force attacks and eavesdropping. Implementing a reverse proxy with SSL termination via Let's Encrypt secures user authentication and data transfers.
Option 1: Caddy (Recommended for Simplicity)
Caddy automatically handles SSL certificate acquisition and renewal. Add the following to your /etc/caddy/Caddyfile:
kavita.yourdomain.com {
reverse_proxy 127.0.0.1:5000
}
Option 2: Nginx
If running Nginx, configure a virtual host block to manage proxy routing and security headers:
server {
listen 80;
listen [::]:80;
server_name kavita.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name kavita.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/kavita.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/kavita.yourdomain.com/privkey.pem;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "no-referrer-when-downgrade";
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSockets support (required for Kavita reader communication)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}