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.
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:
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.- 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.
- 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.