Work/Case study
Shelf
A self-hosted personal library: books, e-books, audiobooks, and the readers who share them.
AGPL-licensed library server on .NET 10 and Blazor. Paper books, e-books, and audiobooks as one book each, with a reading log, quotes, loans, and yearly goals; a full-screen page-turning reader, an audiobook player, Kobo and KOReader sync, and books found and fetched the way Sonarr fetches shows; shelves read from a photo of the spines, nested places, a trash and undo, and a compact view for 70,000 books. Shelf 2.14, one Docker container, SQLite inside.
.NET 10 · ASP.NET Core · Blazor · EF Core · SQLite · epub.js · .NET Aspire · Docker · Playwright
What it is
Shelf keeps a household’s books in one place: the copies on paper, the e-books, and the audiobooks, one book at a time. A paperback, its EPUB, and its audiobook are the same book with one reading log, and Continue in the e-book or Continue in the audiobook carries the place across. Each reader signs in to a shelf of their own; no reader sees another’s books, notes, or reading log unless they open their shelf, and even then only the titles.
Good self-hosted book apps already exist, and each is built around one kind of book: Calibre-Web for e-books, Audiobookshelf for audiobooks, Goodreads and StoryGraph for the reading log on someone else’s server. Shelf is for a library that is all of these at once, on your own hardware.
It started in October 2026 as a first project back on current .NET and grew into a full self-hosted library. Shelf 2.0 reorganized it around a Home page that leads with the book you were last in, a library led by covers, and Insights that tell the year’s story; 2.1 through 2.14 followed within days. It speaks English, Spanish, French, and German.
- Documentationdoodersrage.github.io/shelf
- Running in my homelabshelf.robmcd.name
- Source on GitHub
- Changelog
The catalog
Series, editions, translators, narrators, where each copy sits, its condition, who recommended it, ratings, reviews, and tags. Add a book from an ISBN typed, scanned with a phone’s camera, or shared from another app; Open Library, Google Books, and Hardcover fill in the details, including series and number. Saved searches, hand-made collections, reading lists shared by link, new books in your series, and duplicates merged in a click.
Tidy up makes one author of names written several ways and takes series out of titles. Insights cover goals for books, pages, and hours, a year of the days you read and listened, milestones, and a picture of the year to share.
Places nest as rooms, bookcases, shelves, and boxes, each with a QR label, and a move is packed and unpacked by scanning. A scanning run reads barcodes one after another, checks a shelf for what is missing, or reads the titles off a photo of the spines with Tesseract and matches them to the catalog. Deleted books wait 30 days in a trash, a change to many books is undone as one, and every book keeps a year of its own history. A compact table holds every match and draws only the lines in view, and Go to (Ctrl K) reaches any page, book, author, series, or place from the keyboard.
Shelf 2.8 through 2.12 grew a reading life around the catalog: rereads that keep every read, a daily reading journal, links between editions and the books that answer them, goals of your own, an Up next queue, Reading DNA, a calendar, a reference desk with citations, rules that act on books as they are added or finished, and plain-language questions answered from your own shelf.
Reading and listening
EPUBs open in a page-turning reader built on epub.js, as Audiobookshelf’s does: tap, swipe, or use the keys across chapters, with the place kept to the word (an EPUB CFI) so it opens where you stopped on any device. The reader fills the window, with a tap in the middle bringing back the bars, a contents list by the book’s own chapter names, and text settings as taps that come up from the bottom on a phone. PDFs, comics (manga too), and Kindle files read in the browser as well, with highlights, notes, a dictionary, read aloud, a recap when you come back, search inside every book, Tesseract OCR for scanned PDFs, and offline reading.
Audiobooks — one file or a folder of tracks — play as one book with chapters, bookmarks, speeds from 0.5× to 3×, a sleep timer, lock-screen controls, and a mini player that keeps going while you browse. Read-along marks each word of the e-book as the audiobook reads it, podcasts play in the same player, and Audiobookshelf’s phone apps sign in to Shelf as to their own server, with downloads, Android Auto, and CarPlay.
Lending and sharing
A book can be lent to another reader, who reads or listens with their own place and highlights while the owner’s stay private. Loans have due dates and reminders, readers can open their shelf so others can ask to borrow, children get accounts of their own, and book clubs talk in one place. Two shelves on different servers can trade e-books and audiobooks with a key, keeping the furthest place.
Notifications go to a phone or computer, or through ntfy or Gotify, when a book is lent or asked for, a loan is due, a series has a new book, or a club talks; webhooks reach Home Assistant, Discord, and Slack.
Bringing books in
A new shelf opens on a welcome that asks where your books are now: Audiobookshelf, Calibre, Goodreads or StoryGraph, a folder of files, by hand, or free books from Project Gutenberg and LibriVox. Large imports show their progress on every page and can be stopped, and Shelf can keep in step with an Audiobookshelf library, bringing across what is added there each day.
Find new books searches Open Library for books you don’t have yet and puts them on a want list; follow an author and their new books are offered daily. With Jackett or Prowlarr to search and Deluge, qBittorrent, or SABnzbd to download, Shelf finds and fetches a wanted book the way Sonarr fetches shows, and the finished file lands on the book. For a book with no file, Where to get it points to a free copy, Open Library, WorldCat, or Libby.
Devices and services
Kobo e-readers sync with Shelf as with their own store. KOReader downloads from the OPDS catalog, trades its place with Shelf’s reader to the word, and sends its highlights and reading statistics back. Kindles get books by email and their highlights come across; highlights go to and come from Readwise, and reading status runs both ways with Hardcover.
Running it
One docker run with a single volume, on amd64 or arm64, so it runs on a Raspberry Pi or a NAS; the first account becomes the admin. Templates cover Unraid, TrueNAS, and CasaOS, and the install guide covers running from source, updating, and putting Shelf on the internet behind a reverse proxy.
Sign-in covers passwords, authenticator codes, passkeys, and OpenID Connect providers such as Authentik, Authelia, Keycloak, or Pocket ID, with an activity log and API tokens for scripts and Home Assistant. Shelf 2.4.1 was a security sweep: addresses readers hand the server can’t reach the local network or a cloud metadata service, reset links only go to the shelf’s own address, and forwarded headers are trusted only from known proxies. Every shelf backs itself up nightly, with copies to a second folder or S3-compatible storage; each backup is read back and proved before it is sent, one book can be brought back from it alone, and a weekly check reads every stored file. Server health shows admins what needs them first.
How it is built
ASP.NET Core with Blazor pages and a JSON API (OpenAPI in Development), EF Core migrations on SQLite, epub.js for the reader, and .NET Aspire for local development with logs and traces. Releases follow semantic versioning with a Keep a Changelog file, and publish amd64 and arm64 images to ghcr.io/doodersrage/shelf. Playwright drives the reader, player, lending, offline mode, and an accessibility audit of every page in both themes, in Chromium and Firefox, on each push.