Bookshelf – Self-hosted eBook library that runs on object storage

Aug 25, 2026 06:00 AM - 2 hours ago 4

CI

A self-hosted room for the ebooks you already own. One server-rendered page lists them, filters them pinch a hunt box, serves downloads, and sounds them in the browser — EPUB and PDF, each pinch its ain scholar — arsenic a Cloudflare Worker over R2, aliases arsenic a Node server complete a directory connected disk.

 a searchable database of books pinch covers, a floor plan switcher, and a Continue fastener connected the book being read

Everything supra is npm tally demo — a generated support of public-domain titles, so the screenshots tin beryllium reproduced without uncovering books first.

Node 24 aliases newer, and a Unix-like system: the sync instrumentality finds its image tools with which, truthful Windows is not supported. Covers travel retired amended pinch cwebp and pdftoppm installed — spot publishing, aliases use Docker, which ships both.

git clone https://github.com/murerkinn/bookshelf.git cd bookshelf npm install

Put immoderate books — EPUB aliases PDF — successful books/, past take wherever the library should live.

The shortest path, and the image brings its ain cwebp and pdftoppm so covers travel retired correct without installing thing connected the host.

mkdir books && cp ~/Downloads/*.epub books/ docker constitute tally --rm sync --create docker constitute up -d

The support is past connected http://localhost:3000. Flags walk through, so docker constitute tally --rm sync --force and --dry-run behave arsenic they do locally.

One named volume, library, holds the published books and everything the app writes to them — profiles and reference positions — truthful it is the only point to back up.

The image is built for the filesystem provider: it is simply a Node server, and Cloudflare needs nary container. It runs arsenic a non-root user, and creates /data owned by that personification truthful a named measurement inherits an ownership the app tin write to. If you would alternatively bind-mount a big directory, chown it first:

chown -R 1000:1000 /srv/bookshelf

On a instrumentality you own, without Docker

No relationship anywhere. Point bookshelf.config.json astatine a directory, people into it, and tally the app:

npm tally sync -- --create # builds library/, past publishes it to shelf-data/ npm tally build npm commencement -w @bookshelf/app

library/ is the character the sync instrumentality builds; directory is wherever it publishes to, and it holds the books being served. Keep it retired of the repository — shelf-data/ already is.

Needs a Cloudflare relationship and npx wrangler login. Two checked-in files carry this project's ain bucket and Worker sanction and are meant to beryllium edited — see the R2 provider.

npm tally sync -- --create # creates the bucket, past uploads to it npm tally deploy

The 2 must work together astir which bucket holds the library, aliases the app will serve an quiet shelf. They are checked against each different earlier thing uploads, and a mismatch is reported alternatively than published through.

No books to hand, aliases want thing worthy screenshotting? Nine generated public-domain titles — 8 EPUBs and a PDF, truthful some readers are 1 click from the support — downloading nothing:

npm tally demo # writes them into books/

Then people and service by whichever way above. The titles and authors are real works agelong retired of copyright truthful a support of them looks for illustration a shelf; the prose inside is placeholder. Note that the config successful this repository points astatine R2, so npm tally sync goes location unless you alteration it.

Anything reachable by strangers should garbage to beryllium changed:

Storage keeps serving and stops accepting. Profiles cannot beryllium added, renamed or deleted, and reference positions spell backmost to surviving successful the browser — the same degradation arsenic a supplier that cannot write, because to the app it is the same situation. Switching betwixt existing profiles still works; that is simply a cookie, not a change.

It is enforced wherever the penning happens, not by hiding the forms, truthful posting the actions straight gets the aforesaid refusal.

There is nary authentication. Anyone who tin scope the app tin publication and download the full library, truthful put it connected a web you spot aliases behind something that asks who is calling. See Not done yet.

All of these tally from the repository root; Turborepo builds immoderate the task depends connected first.

npm tally dev # section dev server, against the section R2 bucket npm tally sync # build the room and upload it to the bucket npm tally build # build each workspace npm tally check-types # typecheck each workspace npm tally preview # build + tally the Worker locally npm tally deploy # build + deploy to Cloudflare Workers npm test # the trial suite npm tally lint # biome, crossed the repo

npm tally cf-typegen -w @bookshelf/app regenerates cloudflare-env.d.ts after editing wrangler.jsonc.

Publishing a library the sync tool, its flags, and covers
The room format what ends up successful the bucket, and why it is regenerable
Storage providers the contract, and what the 2 shipped ones tin each do
Cloudflare R2 configuration, deploying, publishing locally
Filesystem running it connected your ain instrumentality aliases a VPS
Profiles who is reading, and wherever they sewage to
Reading successful the browser how a section reaches the page
Architecture ports, adapters, and the creation root
The demo library how the nationalist support is built, and really to rebuild it
  • There is nary authentication. Anyone pinch the URL tin publication and download the whole room — and prime immoderate floor plan while doing it. Profiles are a measurement to keep housemates' bookmarks apart, not a measurement to support anyone out.
  • Two devices reference arsenic 1 floor plan astatine the aforesaid clip is last-write-wins.

See CONTRIBUTING.md. The short version: Node 24, npm install, and npm tally lint, npm tally check-types and npm trial earlier you push. The tests scope the packages and the app's work furniture but not its pages, truthful opportunity what you ran arsenic well.

Storage providers are the hold constituent and do not person to unrecorded here: a package published by anyone tin beryllium installed and named successful the config.

MIT — spot LICENSE.

That covers the code. It says thing astir the books you put successful a library built pinch it, whose copyright is betwixt you and their publishers.

More