induwara.lk
Tool deep-diveself-hostingopen-sourcecloudflare

A self-hosted ebook library with no database at all

Bookshelf runs a self-hosted ebook library on object storage with no database. The architecture is the interesting part, and the missing auth is the trap.

Induwara Ashinsana5 min read
GitHub repository page for Bookshelf showing a self-hosted ebook library interface
Image: GitHub

A self-hosted ebook library normally means a server, a database, a backup job, and a small monthly bill you forget about until it fails. Bookshelf, an MIT-licensed project by murerkinn that surfaced on Hacker News, removes one of those four. There is no database. The books, the covers, and the reading positions all live in the object store.

That single decision is worth more attention than the app itself. I want to look at why, and at the one line in the README that you must not skip.


🗄️ The "no database" claim is literal, not marketing

Most self-hosted media apps keep files on disk and metadata in SQLite or Postgres. Bookshelf keeps both in the storage provider. The README puts it plainly: "No database." State lives where the files live.

What that buys you:

  • Backup equals sync. Copying the bucket copies the whole application state. No pg_dump, no restore-order problems.
  • Nothing to migrate. No schema versions to reconcile when you upgrade.
  • Stateless compute. The server renders pages and reads objects. Kill it, redeploy it, nothing is lost.

What it costs you is honest, and the project says so:

"Two devices reading as one profile at the same time is last-write-wins."

Object stores do not give you transactions. If your phone and your laptop both update a reading position, one of them loses. For a personal library that is a shrug. For anything with concurrent writers it is a design ceiling, and you should notice where that ceiling sits before you copy the pattern into your own project.


☁️ Two storage backends, and why the R2 one matters here

Bookshelf ships two providers, with a documented extension point so others can be published as separate packages.

Provider Where it runs Good for
Cloudflare R2 Deploys as a Worker via npx wrangler login No server to maintain, global edge delivery
Filesystem (fs) A directory on disk or a VPS Full local control, works offline, no cloud account

The R2 path is the one I'd point a Sri Lankan reader at first, for a reason that has nothing to do with ebooks. Object storage bills you for what you store and what you request. Traditional VPS hosting bills you for a machine that runs whether or not anyone visits, in dollars, on a card, every month. When your hobby budget is in rupees, the difference between "pay per byte stored" and "pay per month regardless" changes which projects you can afford to keep alive for years instead of weeks.

I am deliberately not quoting Cloudflare's numbers here, because pricing pages move and the repo does not state them. Read the current R2 pricing page yourself before you commit. The architectural point stands either way: compute you can switch off, storage you keep.


🚨 Read this before you deploy it anywhere public

The README does not hide this, and neither will I:

"There is no authentication. Anyone with the URL can read and download the whole library."

A Worker URL is not a secret. It is guessable, it ends up in logs, it gets shared in a WhatsApp group and then it is public forever. If you deploy Bookshelf as-is on a public hostname, you have published your library.

There is a partial mitigation shipped in the box, and a real one that is not:

  1. BOOKSHELF_READ_ONLY=1 — the project's read-only mode. This stops writes. It does not stop reading or downloading, so it is not access control.
  2. Put an identity layer in front of it. Cloudflare Zero Trust / Access sits ahead of a Worker and requires a login before any request reaches your app. That is the actual fix for a public deployment.
  3. Or don't expose it. Filesystem provider, bound to your LAN or reached over Tailscale. For a personal library this is the least work and the least risk.

There is also a copyright dimension worth stating out loud. A private library of books you own is your business. A publicly reachable URL that serves those files to anyone is distribution, and that is a different conversation with a different set of consequences. Keep it behind a login.


🛠️ What you actually need to run it

Requirement Detail
Node.js 24 or newer
OS Unix-like only — not Windows
Formats EPUB and PDF, each with its own in-browser reader
Optional binaries cwebp and pdftoppm, for cover optimisation
Docker Bundles both binaries and uses a named volume
Build tooling Turborepo, Biome
Licence MIT

The Docker path exists precisely so you don't have to chase cwebp and pdftoppm on your distro. Take it. The feature set beyond that is small and sensible: search and filter, downloads, profile switching for multiple readers in one household, and reading-position tracking. There is a public demo carrying nine generated public-domain titles, so you can see the interface before you install anything.

One practical note if you go the R2 route: scanned PDFs are enormous, and in an object store size is the bill. Running them through a PDF compressor before upload is a five-second step that pays rent for years. Our converters are free and need no account.


💡 What this means for you

Key takeaway: The transferable idea is not the ebook reader. It is that a whole class of small app — read-heavy, single-writer, personal — does not need a database, and dropping it removes most of the operational work that kills side projects.

If you are a student or a small-team builder here, three concrete takes:

  • Steal the architecture, not just the app. A read-heavy personal tool with one writer can put its state in object storage. Your backup story becomes "copy the bucket" and your ops burden goes close to zero.
  • Know the ceiling. Last-write-wins is fine for one person and wrong for a team. Choose it knowingly, not by accident.
  • Treat "no auth" as a deployment blocker, not a to-do item. The gap between a private library and a public file server is one URL.

The version of this I'd run: filesystem provider, Docker, on hardware I already own, reachable only over a private network. Compressed PDFs, EPUBs where I have them. No monthly bill, no exposed bucket, and a backup that is a single folder copy.

#self-hosting#open-source#cloudflare
IA

Induwara Ashinsana

Information Systems student at UCSC and Executive Director at Ryzera Technologies. Writes about software, AI, and what it means for builders in Sri Lanka.

About the author →

Keep reading