feat(calibre-server): Calibre Content Server as a boot-persistent Quadlet

lscr.io/linuxserver/calibre:9.15.0 (desktop build) running calibre-server
as the s6 main service on internal 8090 -> host 8084, auth-gated with a
plaintext userdb (mode 600) bootstrapped once by the deploy handler.
The desktop GUI is kept on 8080/8182, basic-authed with the same
credential pair via the machine-local env file.
This commit is contained in:
Frank
2026-09-21 12:46:02 -07:00
parent 660db661b6
commit 94cfb90ead
5 changed files with 173 additions and 24 deletions
+39 -23
View File
@@ -39,33 +39,49 @@ gone; the quadlet unit mounts the Containers data root.)
- Read/write access to the library directory. Calibre-Web edits metadata through the database file.
- Ownership matching `PUID=1000` / `PGID=1000` (the `dank` user on Dankserver).
## Optional: Calibre Content Server (not running)
## Calibre Content Server (running)
Calibre includes a built-in HTTP content server (`calibre-server`) that can serve the library via OPDS feeds and a mobile-friendly web interface. This enables:
- OPDS feeds for mobile reading apps (FBReader, Moon+ Reader)
- Mobile-optimized UI via the `--mobile` flag
- Direct Kindle push
Calibre includes a built-in HTTP content server (`calibre-server`) that serves
the library via OPDS feeds and a web interface:
- OPDS feeds for mobile reading apps (FBReader, Moon+ Reader, KyBook)
- Web UI for browsing, metadata editing, and uploads
- Direct device push (from the GUI, not the content server)
To run as a container:
**Live shape (deployed 2026-09-21):** a rootless Quadlet unit
(`dankserver/calibre-server/quadlet/calibre-server.container`), boot-persistent
and self-healing, on `lscr.io/linuxserver/calibre:9.15.0`.
```yaml
services:
calibre-server:
image: lscr.io/linuxserver/calibre:latest
container_name: calibre-server
restart: unless-stopped
environment:
PUID: "1000"
PGID: "1000"
TZ: Etc/UTC
command: calibre-server --daemonize --port 8080 --enable-auth --url-prefix /calibre /books
volumes:
- /opt/calibre-web/books:/books
ports:
- "8084:8080"
```
The image is the current **desktop** build (Selkies web-streamed GUI), not the
headless image the old compose snippet assumed — the dedicated headless images
(ta264/docker-calibre, agentgreen/calibre-server) are dormant (calibre 6.10 /
2017, both below the 9.9.0 content-server fix). The unit therefore runs
`calibre-server` as the image's s6-overlay main service via `Exec=` and keeps
the desktop as a bonus:
Note: The content server runs headless in `--daemonize` mode. It requires the full Calibre package, not a minimal server.
| Surface | Host port | Auth |
|---------|-----------|------|
| Content server (OPDS + web UI) | **8084** (internal 8090) | HTTP Basic, single admin `calibre` in the SQLite userdb |
| Desktop GUI (bonus) | 8080 (HTTPS on 8182) | HTTP Basic, same credential |
Port notes: 8080 inside the container is the image's own nginx (the desktop),
so the content server runs on internal 8090; `CUSTOM_HTTPS_PORT=8182` avoids
Tautulli's host 8181.
**Auth model (calibre 9.x):** users live in a SQLite userdb whose `pw` column
is **plaintext** — the file is mode 600. The single admin account is
bootstrapped once by the deploy handler
(`deploy_calibre_server` in `scripts/deploy-remote.sh`) via
`calibre-server --manage-users -- add`, the password piped on stdin with
`printf` (a trailing newline from `echo` lands in the stored password and
breaks every login). The desktop's basic auth (`CUSTOM_USER`/`CUSTOM_PASSWORD`)
reuses the same pair. Both live in the machine-local env file
`/home/dank/Containers/calibre-server/env` (mode 600); the repo holds no
secrets. `--auth-mode=basic` is pinned: on a non-SSL port the server defaults
to Digest auth, which rejects plain Basic clients with
`400 Unsupported authentication method`.
LAN-only (no vhost, no public DNS). The library is mounted read-write — the
content server writes metadata, the same library Calibre-Web uses.
## See Also