Skip to content

Ebooks & audiobooks — Audiobookshelf + Calibre-Web-Automated

Two purpose-built stacks, split because no single self-hosted app does both media types well and syncs cleanly to Kobo + Kindle + Supernote:

Stack URL Handles
Audio audiobookshelf (ghcr.io/advplyr/audiobookshelf) ab.fmm.house audiobooks + podcasts
Text calibre-web-automated (ghcr.io/crocodilestick/calibre-web-automated) eb.fmm.house ebooks + all e-reader device sync

Both are label-fronted through Traefik on the proxy network (no host port), pinned + Diun-watched like the rest of the fleet. Runtime data (media, DBs, config) is gitignored under each stack dir.

Why two apps

Audiobookshelf is audiobook-first; its ebook support is basic and its only ebook→device path is email (Send-to-Kindle), which Kobo can't consume. Its own docs point you to a dedicated ebook server. Calibre-Web-Automated (the actively-developed CWA fork of Calibre-Web) owns the ebook side: native Kobo sync, Send-to-Kindle, OPDS, and a built-in KOReader progress-sync plugin. Kavita was evaluated and rejected (no audiobooks; .NET).

First-run

  • Audiobookshelf (ab.fmm.house): first visit creates the root/admin account in the browser. Add libraries pointing at /audiobooks and /podcasts. Runs as root (official image has no PUID/PGID); media dirs are stacks/audiobookshelf/{audiobooks,podcasts}.
  • Calibre-Web-Automated (eb.fmm.house): default login admin / admin123 — change it immediately (Admin → Edit Users). CWA auto-creates an empty Calibre library in /calibre-library on first boot. Runs as PUID/PGID 1000:1000.

Adding books (CWA)

Drop any of ~28 formats into stacks/calibre-web-automated/ingest/ (/cwa-book-ingest). CWA auto-converts to EPUB, imports into the library with metadata, and deletes the source file. Or upload via the web UI. EPUB is required for Kobo sync and cleanest for Send-to-Kindle.

Device sync

Kobo — native OTA sync (best path)

CWA impersonates the Kobo store so the reader syncs books and reading position over the air. 1. In CWA: Admin → Edit User → OAuth & API Integrations → Kobo Sync Token → create; enable Kobo sync and Proxy unknown requests to Kobo Store. 2. On the Kobo, point the sync API at the generated https://eb.fmm.house/kobo/<token> URL (via the Kobo's .kobo/Kobo/Kobo eReader.conf api_endpoint, or the setup helper CWA prints). 3. EPUB only; CWA converts to kepub on the fly.

Reverse-proxy gotcha: if synced URLs come back as http:// or wrong host, Calibre-Web built them from bad proxy headers. Traefik forwards X-Forwarded-Proto/Host correctly by default; if it ever misbehaves, set the server external URL in CWA settings to https://eb.fmm.house.

Kindle — Send-to-Kindle (no live sync exists)

Kindle has no self-hosted sync protocol. CWA emails individual books to your @kindle.com address (it cleans the EPUB for Amazon's converter first).

This needs a real external SMTP relay — Postmoogle can't do it. Postmoogle (fmm.house:25) is an inbound email→Matrix sink with no outbound relay, and this box can't send to the public internet on :25 anyway (residential egress, ISP-blocked :25, no sending reputation — see docs/runbooks/email-to-matrix.md). A book "sent to" @kindle.com through it would not deliver.

  1. Provision an authenticated submission relay (a transactional/email provider on :587 — e.g. SES, Postmark, Mailgun, SMTP2GO, Fastmail, or a Gmail app-password) with a verified From address.
  2. Configure SMTP in CWA (Admin → Edit Basic Configuration → Email server) pointing at that relay.
  3. Add the From address to Amazon's Approved Personal Document E-mail List.
  4. Per book: Send to Kindle. Recent Kindles accept EPUB directly.

If you'd rather not run a relay: Kobo native sync (above) needs no email at all, or use Amazon's own Send to Kindle web/app to push files manually.

Supernote — KOReader + OPDS

Supernote has no native OPDS, but sideload the KOReader APK (adb install over USB) and point it at CWA: 1. KOReader → OPDS catalog → add https://eb.fmm.house/opds (basic-auth with your CWA user). Browse + download the whole library. 2. KOReader → Progress sync: use CWA's built-in KOReader sync (install the CWA KOReader plugin, disable KOReader's native Progress Sync, log in with your CWA server + account) so reading position round-trips. Komga/Kavita OPDS would also work here, but CWA keeps everything in one place.

Notifications → Matrix (Postmoogle)

Neither app has forgejo-style event-notification email; wiring is limited to what each can emit. Both mailboxes (eb@fmm.house, ab@fmm.house) are claimed in Matrix with a shared SMTP password stored in each stack's gitignored .env. AUTH verified against Postmoogle (235).

CWA → eb@fmm.house (wired + verified)

No extra_hosts pin needed (same as forgejo/ocis): the container resolves mail.fmm.house to the routable LAN 192.168.86.49 A record — glibc sorts it ahead of the unroutable fd00:6176 ULA, so smtplib connects to .49:25 in ~0.01s (verified from inside the container: STARTTLS + AUTH 235). The old vaporware host-gateway pin was only needed under the pre-2026‑07‑19 Tailscale-IPv6 AAAA, which didn't fail cleanly. Calibre-Web stores SMTP config encrypted in app.db, so it can't be injected headlessly — enter once in the UI (Admin → Edit E-mail Server Settings), values from stacks/calibre-web-automated/.env:

Field Value
SMTP hostname mail.fmm.house
SMTP port 25
Encryption STARTTLS
SMTP login eb@fmm.house
SMTP password (SMTP_PASSWORD in .env)
From e-mail eb@fmm.house

Set your CWA account's e-mail to a claimed mailbox (e.g. albert@fmm.house) so Save settings and send Test E-Mail lands in that Matrix room. CWA only emits password-reset/test mail — no event stream.

ABS → ab@fmm.house (needs Apprise — not yet wired)

Audiobookshelf has no SMTP notification path (its SMTP is send-to-ereader only, being sunset). Event notifications (podcast episode, backup done/failed) go through Apprise, which needs a small Apprise-API container. Intended route: ABS → Apprise → Postmoogle (mailto as ab@fmm.house) → Matrix, reusing the validated credential. Not built yet — an audiobook-first library fires little.

Notes / gotchas

  • Both images are off Docker Hub (ghcr) per fleet convention; Diun watches ^\d+\.\d+\.\d+$ (ABS) and ^v\d+\.\d+\.\d+$ (CWA).
  • Backups: neither stack's data is backed up yet — same deferred off-box backup gap as the rest of the fleet (see docs/runbooks/monitoring.md). The Calibre library (metadata.db + books) and the ABS config/metadata SQLite DBs are the things worth capturing when that lands.
  • Whispersync-style read-along (audio highlighting ebook text) is out of scope here — that's a separate app (Storyteller), not wired up.