← All projects
project write-up

๐Ÿ“š Calibre + Kindle Setup

Complete

An ebook pipeline where finding the book is the only manual step.

Automated ebook pipeline, search to Kindle ยท CWA ยท Prowlarr ยท Deluge ยท rclone

28
Input Formats
15 min
Sync Interval
~30 sec
Manual Effort / Book
2
Readers

Why I built it

I read on a Kindle, and for years the path from finding a book to actually reading it ran through Calibre on my desktop. Calibre is powerful, but the workflow around it was manual at every step. Every book meant sitting at the PC and downloading a file, opening Calibre, importing it, fixing the metadata, converting the format if the Kindle didn't like it, then emailing it to the device. Calibre-Web put a browser front end on the library, which helped for browsing, but it didn't fix the part that mattered. Calibre desktop was still the required middleman for getting books in.

The goal was a pipeline where finding a book is the only manual step. Everything after that, including download, transfer, conversion, cleanup, and delivery to the Kindle, should happen on its own.

What it is

The pipeline runs across three machines: a seedbox, my home server, and whatever browser I happen to be in.

on the seedbox Shelfmark search & grab Prowlarr indexer manager Books folder completed download on my home server rclone sync every 15 minutes CWA convert & import Kindle one-click delivery
Shelfmark search is the only manual step, roughly thirty seconds

It starts in Shelfmark, a self-hosted book search UI running on my server. I search for a title, and Shelfmark queries a private tracker through Prowlarr, my indexer manager. I pick a release and grab it. That's the entire manual portion, maybe thirty seconds.

From there the torrent lands in Deluge on a remote seedbox with a books label attached. The label routes the completed download into a dedicated books folder automatically. Back on my home server, a scheduled rclone script runs every fifteen minutes and copies anything new from that folder into a local ingest directory. It copies rather than moves, so the original files stay on the seedbox and keep seeding for ratio.

Calibre-Web Automated watches that ingest directory. When a file appears, CWA converts it to EPUB if it isn't one already, runs its EPUB fixer, checks for duplicates, and imports it into the library. The ingest folder empties itself as files are processed. A few minutes after I clicked grab in Shelfmark, the book is sitting in the library in a browser tab, cover and all.

The last step is metadata. Auto-fetch is on, but results for books with common titles can land on the wrong edition, so I review each new arrival in the browser and correct the title, author, series, and category tag where needed. Then it goes to the Kindle with one click.

Why CWA over the alternatives

The evaluation came down to five options: keeping Calibre plus Calibre-Web, Booklore, Kavita, Audiobookshelf, or building something custom.

Keeping the status quo was out because Calibre desktop was the whole problem. Booklore was a strong contender with a nicer UI, but it has no format conversion, and conversion was non-negotiable for a Kindle-centered library. It also drags in a MariaDB dependency and is a younger project. Kavita came from the comic and manga world, and its better metadata sits behind a monthly subscription. Audiobookshelf already runs in my homelab for audiobooks, but its ebook support is basic by its own admission. A custom build was tempting and got far enough to be scoped, roughly six to eight working sessions to an MVP, before I admitted that CWA already covered the workflow I was about to build.

CWA won because it replaces both halves of the old setup at once. It creates and manages its own Calibre database with no desktop app anywhere, it bundles the full Calibre conversion engine inside the container, and its metadata editing writes changes back into the EPUB file itself instead of only updating the UI database. That last one matters more than it sounds. With stock Calibre-Web, the library could show a corrected title while the file on disk, the one that actually gets emailed to the Kindle, still carried the wrong one. With CWA, what I see in the browser is what arrives on the device. It's also GPL v3, so if it ever falls short I can fork it or lift its EPUB fixer for a custom build.

Format conversion, and why it mattered

Books don't arrive in a tidy uniform format. Sources hand you EPUB, MOBI, AZW3, and older formats, and the Kindle delivery path wants a clean EPUB. Before CWA, any file in the wrong format meant a trip through Calibre's converter by hand. Now the conversion target is set once in the admin panel and every incoming file that isn't an EPUB gets converted during ingest without me knowing about it.

The EPUB fixer is the quieter half of the same feature. Amazon's delivery service rejects files over problems you'd never spot by reading them: missing UTF-8 encoding declarations, broken internal links, invalid language tags, malformed XML. CWA runs the fixer on every book automatically and backs up the original first. Since it went in, "the Kindle silently didn't receive the book" stopped being a thing I troubleshoot.

Send to Kindle, and the second reader

Delivery is plain email under the hood. CWA has one SMTP configuration at the admin level, a Gmail account with an app password, and that single setup handles all outgoing mail. Separately, each user profile has its own send-to-eReader address. My profile points at my Kindle address, my wife's profile points at hers.

That split is what makes the library work for two people without any extra machinery. She logs into CWA with her own account, browses the same library, and sends a book to her own Kindle. She also has her own Shelfmark account, so she can grab books herself and they flow through the same pipeline. The only shared prerequisite is that the sending address has to be on each Amazon account's approved sender list, which is a one-time setting on Amazon's side.

The hiccups

Metadata providers are a mixed bag. Amazon's provider is broken upstream, since Amazon actively blocks CWA's scraper, and there's no fix. Google Books works but rate-limits hard, and once it starts returning 429s it stays sulking for a few hours. Hardcover ended up as the primary provider, configured through an API token, with Google as the fallback for the gaps.

Direct database edits need a WAL checkpoint. I locked myself out of the admin account at one point and recovered by writing a new password hash straight into CWA's SQLite database. The edit didn't take at first, because SQLite's write-ahead log hadn't flushed. The fix is running a WAL checkpoint after the update and then restarting the container. Now it's in my notes forever.

An rsync merged my ebooks and audiobooks. During the migration of the library onto my current server, a sync accidentally combined the ebook and audiobook folders into one share. Untangling them by hand and keeping them as strictly separate shares afterward was the lesson.

The ingest watcher isn't instant everywhere. In the original deployment on a Windows-based Docker host, filesystem notifications weren't reliable, so CWA fell back to polling the ingest folder. That added a few seconds of delay before a dropped file was noticed. In practice it never mattered, since nobody is staring at the folder waiting, and the later move to a Linux server made it moot anyway.

The setup has also moved once. It was first deployed on a Windows machine running Docker, then migrated to my Unraid server along with the rest of the homelab. The migration is where the share merge above happened, but CWA itself moved cleanly. Its whole state lives in the library folder and one config directory, so pointing a fresh container at both brought everything back.

What I'd do differently

I'd have skipped the custom-build detour in the evaluation. I spent real effort scoping an app I was never going to need, because CWA already existed and did the job. The better habit, and one I've since applied elsewhere, is to exhaust the existing open source options thoroughly before letting "I could build this" get momentum.

← All projects