Calibre-Web is a lightweight web front end for a Calibre ebook library. It gives you a clean browser interface for browsing, reading, and downloading books, plus OPDS feeds for reader apps, Kobo device sync, send-to-eReader, multi-user accounts with per-user shelves, and in-browser metadata editing. It reads and writes a standard Calibre metadata.db, so the same library stays compatible with desktop Calibre.
This guide installs Calibre-Web 0.6.27 natively in a Python virtual environment on Ubuntu 24.04 LTS, runs it under a dedicated systemd service account, and publishes it behind Nginx with a Let's Encrypt certificate.
What You Will Build
- Calibre-Web 0.6.27 in an isolated virtualenv at
/opt/calibre-web - A Calibre library at
/srv/calibre/library - Calibre's command-line tools (
calibredb,ebook-convert) for library creation and format conversion - A hardened systemd unit listening only on
127.0.0.1:8083 - Nginx reverse proxy with TLS, upload limits sized for ebooks, and Kobo-friendly proxy buffers
- Nightly backups of the library and the Calibre-Web settings database
Server Sizing
Calibre-Web itself is light. Format conversion through ebook-convert is what drives CPU and RAM usage.
| Use case | vCPU | RAM | Disk | Notes |
|---|---|---|---|---|
| Personal library, browsing and OPDS only | 1 | 1 GB | 20 GB | Add 1 GB swap; skip heavy conversions |
| Household library with conversion and Kobo sync | 2 | 2 GB | 40 GB+ | Recommended starting point |
| Large library (20k+ titles) or several active users | 2 to 4 | 4 GB | 100 GB+ | Covers and conversions add up fast |
Disk is usually the deciding factor. Budget the size of your existing Calibre folder plus roughly 25% headroom for covers, converted formats, and backups.
Prerequisites
- A RamNode VPS running Ubuntu 24.04 LTS
- Root or sudo access over SSH
- A DNS
Arecord (andAAAAif you use IPv6) pointing a hostname such asbooks.example.comat the VPS - An existing Calibre library to import (optional; this guide also creates an empty one)
Replace books.example.com throughout with your hostname.
Step 1: Prepare the System
Update packages and install base tooling:
sudo apt update && sudo apt -y full-upgrade
sudo apt -y install python3 python3-venv python3-dev build-essential \
sqlite3 nginx certbot python3-certbot-nginx ufw unattended-upgradesEnable automatic security updates:
sudo dpkg-reconfigure -plow unattended-upgradesConfigure the firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw statusOn 1 GB plans, add swap so conversions do not trigger the OOM killer:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabStep 2: Install Calibre Command-Line Tools
Calibre-Web needs calibredb to create a new library and ebook-convert for format conversion. The Ubuntu package is the simplest option and receives updates through apt:
sudo apt -y install --no-install-recommends calibre
ebook-convert --version--no-install-recommends keeps the install lean, but it still pulls in a sizable set of Qt libraries. That is expected.
Want the newest Calibre? The official installer from calibre-ebook.com ships Calibre 9.x into
/opt/calibre. Calibre-Web has supported Calibre 9 since 0.6.26. If you use it, installlibegl1 libopengl0 libxcb-cursor0first and point Calibre-Web at/opt/calibre/ebook-convertin Step 7.
Step 3: Create the Service Account and Directories
sudo useradd --system --home-dir /var/lib/calibre-web --create-home \
--shell /usr/sbin/nologin calibreweb
sudo mkdir -p /opt/calibre-web /srv/calibre/library
sudo chown calibreweb:calibreweb /opt/calibre-web /srv/calibre/library
sudo chmod 750 /var/lib/calibre-web /srv/calibre/library| Path | Purpose |
|---|---|
/opt/calibre-web/venv | Python virtualenv with Calibre-Web and dependencies |
/var/lib/calibre-web | Settings database (app.db), log, and service home directory |
/srv/calibre/library | Calibre library (metadata.db plus book folders) |
Step 4: Install Calibre-Web
Create the virtualenv and install the package from PyPI with the optional feature sets most self-hosters want:
sudo -u calibreweb python3 -m venv /opt/calibre-web/venv
sudo -u calibreweb /opt/calibre-web/venv/bin/pip install --upgrade pip wheel
sudo -u calibreweb /opt/calibre-web/venv/bin/pip install "calibreweb[metadata,kobo,comics]==0.6.27"| Extra | Adds |
|---|---|
metadata | Online metadata providers for fetching covers and descriptions |
kobo | Kobo device sync |
comics | CBZ/CBR cover extraction and comic reader support |
Other extras exist (ldap, oauth, gdrive, gmail, goodreads). Add them only if you will use them, since each brings more dependencies.
0.6.27 replaced the bleach sanitizer with nh3. A fresh pip install handles that automatically. If you are upgrading an older venv, confirm nh3 is present:
sudo -u calibreweb /opt/calibre-web/venv/bin/pip show nh3Step 5: Create or Import the Library
Option A: Start with an empty library
sudo -u calibreweb calibredb list --with-library /srv/calibre/library
ls -l /srv/calibre/library/metadata.dbRunning any calibredb command against an empty directory creates a fresh metadata.db.
Option B: Import an existing library
From your workstation, copy the entire Calibre library folder (the one containing metadata.db):
rsync -avh --progress ~/Calibre\ Library/ user@books.example.com:/tmp/library/Then on the VPS:
sudo rsync -a /tmp/library/ /srv/calibre/library/
sudo chown -R calibreweb:calibreweb /srv/calibre/library
sudo rm -rf /tmp/libraryDo not let desktop Calibre and Calibre-Web write to the same
metadata.dbat the same time (for example over a network share or sync tool). SQLite locking over those paths is unreliable and can corrupt the database. Pick one writer, or sync one direction only.
Step 6: Set the Admin Password Before First Launch
Calibre-Web ships with a default admin / admin123 login. Initialize the settings database and replace that password from the CLI so the default credentials are never reachable over the network:
sudo -u calibreweb env CALIBRE_DBPATH=/var/lib/calibre-web \
/opt/calibre-web/venv/bin/cps -s 'admin:REPLACE_WITH_A_STRONG_PASSWORD'Clear it from your shell history afterward:
history -d $(history 1 | awk '{print $1}')Step 7: Create the systemd Service
sudo pico /etc/systemd/system/calibre-web.service[Unit]
Description=Calibre-Web ebook server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=calibreweb
Group=calibreweb
Environment=CALIBRE_DBPATH=/var/lib/calibre-web
Environment=HOME=/var/lib/calibre-web
WorkingDirectory=/var/lib/calibre-web
ExecStart=/opt/calibre-web/venv/bin/cps -i 127.0.0.1
Restart=on-failure
RestartSec=5
# Hardening
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
LockPersonality=true
ReadWritePaths=/var/lib/calibre-web /srv/calibre
[Install]
WantedBy=multi-user.target-i 127.0.0.1 binds Calibre-Web to loopback so it is only reachable through Nginx. HOME points Calibre's own config directory at a writable path inside the sandbox, which ebook-convert needs.
Start it:
sudo systemctl daemon-reload
sudo systemctl enable --now calibre-web
sudo systemctl status calibre-web --no-pager
curl -sI http://127.0.0.1:8083/login | head -n1You should see HTTP/1.1 200 OK.
Step 8: Configure Nginx and TLS
Define a login rate limit in the http context:
sudo pico /etc/nginx/conf.d/ratelimit-calibreweb.conflimit_req_zone $binary_remote_addr zone=cw_login:10m rate=10r/m;Create the site:
sudo pico /etc/nginx/sites-available/calibre-webserver {
listen 80;
listen [::]:80;
server_name books.example.com;
# Ebook uploads can be large (PDFs, comics)
client_max_body_size 500M;
# Kobo sync sends large headers and responses
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
location /login {
limit_req zone=cw_login burst=5 nodelay;
proxy_pass http://127.0.0.1:8083;
include /etc/nginx/snippets/calibre-web-proxy.conf;
}
location / {
proxy_pass http://127.0.0.1:8083;
include /etc/nginx/snippets/calibre-web-proxy.conf;
}
}Shared proxy headers:
sudo pico /etc/nginx/snippets/calibre-web-proxy.confproxy_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;
proxy_set_header X-Scheme $scheme;
proxy_read_timeout 300s;The long read timeout keeps conversions and large Kobo syncs from being cut off.
Enable the site and request a certificate:
sudo ln -s /etc/nginx/sites-available/calibre-web /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d books.example.com --redirect -m you@example.com --agree-tos --no-eff-emailCertbot rewrites the server block for HTTPS and installs a renewal timer. Confirm it:
sudo systemctl list-timers | grep certbot
sudo certbot renew --dry-runStep 9: First-Run Configuration
Browse to https://books.example.com and log in as admin with the password from Step 6.
- Database configuration: set the Calibre library location to
/srv/calibre/libraryand save. - Admin > Edit Basic Configuration > Feature Configuration:
- Enable Uploads if you want to add books through the browser.
- Restrict Allowed Upload Fileformats to what you actually use (for example
epub,pdf,mobi,azw3,cbz).
- External binaries:
- Path to converter:
/usr/bin/ebook-convert(or/opt/calibre/ebook-convertif you used the official installer) - Leave the unrar path empty unless you need CBR support. If you do,
sudo apt install unrarand set/usr/bin/unrar.
- Path to converter:
- Security settings: enable Limit failed login attempts and set a session protection level of Strong.
- Admin > Edit User admin: change the username from
adminto something less guessable.
Optional: Kobo Sync
- Admin > Edit Basic Configuration > Feature Configuration: enable Kobo sync and Proxy unknown requests to Kobo Store.
- Set Server External Port to
443. - As each user, open Profile, generate a Kobo sync token, and copy the API endpoint into the
api_endpoint=line of.kobo/Kobo/Kobo eReader.confon the device.
0.6.27 rewrites the library_sync URL in the Kobo init response, so sync now works correctly behind a reverse proxy without extra Nginx rewrites.
Optional: kepubify
Kobo devices render .kepub files better than plain EPUB. Install kepubify and point Calibre-Web at it:
KV=$(curl -s https://api.github.com/repos/pgaskin/kepubify/releases/latest | grep -oP '"tag_name": "\K[^"]+')
sudo curl -L -o /usr/local/bin/kepubify \
"https://github.com/pgaskin/kepubify/releases/download/${KV}/kepubify-linux-64bit"
sudo chmod 755 /usr/local/bin/kepubify
kepubify --versionSet the kepubify path to /usr/local/bin/kepubify under External binaries.
OPDS for Reader Apps
Calibre-Web exposes an OPDS catalog at:
https://books.example.com/opdsAdd that URL in KOReader, Readest, Moon+ Reader, or any OPDS client, using a regular Calibre-Web account. Create a dedicated low-privilege user for each device rather than sharing the admin login.
Step 10: Backups
Two things matter: the Calibre library (metadata.db plus book files) and Calibre-Web's app.db (users, shelves, Kobo tokens, settings). Use SQLite's online backup so you never copy a database mid-write.
sudo pico /usr/local/bin/calibre-web-backup#!/usr/bin/env bash
set -euo pipefail
DEST=/var/backups/calibre-web
STAMP=$(date +%F)
KEEP_DAYS=14
mkdir -p "$DEST/$STAMP"
sqlite3 /srv/calibre/library/metadata.db ".backup '$DEST/$STAMP/metadata.db'"
sqlite3 /var/lib/calibre-web/app.db ".backup '$DEST/$STAMP/app.db'"
tar --exclude='metadata.db' -czf "$DEST/$STAMP/library-files.tar.gz" -C /srv/calibre library
find "$DEST" -mindepth 1 -maxdepth 1 -type d -mtime +$KEEP_DAYS -exec rm -rf {} +sudo chmod 750 /usr/local/bin/calibre-web-backup
echo '30 3 * * * root /usr/local/bin/calibre-web-backup' | sudo tee /etc/cron.d/calibre-web-backup
sudo /usr/local/bin/calibre-web-backup
ls -lh /var/backups/calibre-web/$(date +%F)Ship /var/backups/calibre-web off the server with restic, rclone, or rsync to a second RamNode VPS or object storage. A backup on the same disk is not a backup.
Updating Calibre-Web
Read the release notes first, since some releases add required dependencies (0.6.27 required nh3).
sudo /usr/local/bin/calibre-web-backup
sudo systemctl stop calibre-web
sudo -u calibreweb /opt/calibre-web/venv/bin/pip install --upgrade "calibreweb[metadata,kobo,comics]==NEW_VERSION"
sudo systemctl start calibre-web
sudo journalctl -u calibre-web -n 50 --no-pagerDisable the in-app updater (Admin > Edit Basic Configuration > Feature Configuration) so updates only happen through pip, where you control the version.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
DB location is not valid | Path must be the folder containing metadata.db, and calibreweb must own it: sudo chown -R calibreweb:calibreweb /srv/calibre/library |
502 Bad Gateway | Service is down. Check sudo journalctl -u calibre-web -n 100 --no-pager |
Uploads fail with 413 Request Entity Too Large | Raise client_max_body_size in the Nginx site and reload |
| Conversion hangs or fails | Verify the converter path; check free -h for memory pressure; confirm HOME is set in the unit so Calibre can write its config |
Downloads failing with metadata conversion enabled | Known issue when the calibre binary path is invalid. Correct the path under External binaries |
| Kobo sync errors or partial syncs | Confirm the Nginx proxy buffer settings, external port 443, and that the device token belongs to the right user |
Kobo sync fails with AttributeError in rate limiter | Known 0.6.27 bug fixed in nightly; update when the next release ships |
| Forgot admin password | sudo systemctl stop calibre-web then rerun the cps -s 'user:newpass' command from Step 6 with CALIBRE_DBPATH set, then start the service |
/author pages very slow on huge libraries | Known performance issue with 150k+ titles. Prefer search and shelves for very large catalogs |
Logs live in two places:
sudo journalctl -u calibre-web -f
sudo tail -f /var/lib/calibre-web/calibre-web.logSecurity Checklist
- Default
admin123password replaced before the service ever listened on the network - Admin account renamed
- Service bound to
127.0.0.1, reachable only through Nginx over HTTPS - Login rate limited at Nginx and failed-login limiting enabled in-app
- Uploads restricted to required formats; unrar left unset unless needed
- Regular users have no admin, upload, or delete rights unless explicitly granted
- Off-server backups tested with a restore at least once
Next Steps
- Pair Calibre-Web with a document workflow: drop new files into a watched folder and add them with
calibredb add --with-library /srv/calibre/libraryfrom a cron job or systemd path unit. - Put the instance behind your SSO with the
oauthorldapextras, or use the new reverse proxy header login added in 0.6.27 with its shared secret header. - If you also self-host feeds, see the Miniflux and FreshRSS guides to round out a personal reading stack.
