Sign inSign up

dangahagan/shelf

By dangahagan

Updated about 2 hours ago

Self-hosted home library catalog — barcode & shelf-photo scanning, metadata, lending, offline PWA

Image
0

6.6K

dangahagan/shelf repository overview

Shelf

A self-hosted home library catalog — scan barcodes or photograph whole shelves, and Shelf fetches metadata and cover art, tracks lending, series and reading, and works offline in a bookstore. Single Docker container, SQLite, no cloud.

Photo Intake demo — a shelf photo is analyzed by AI vision and the books are imported with covers and metadata

Photo Intake: snap a shelf, AI reads the spines, books land in your library with covers and metadata.

Quick Start

mkdir -p shelf-data
docker run -d \
  --name shelf \
  -p 18888:18888 \
  -v ./shelf-data:/data:z \
  dangahagan/shelf:latest

Open https://localhost:18888 and create your admin account via the setup wizard. That's it.

Note: Shelf uses HTTPS with a self-signed certificate generated on first run. Your browser will show a certificate warning — this is expected. Click through to proceed.

Create a docker-compose.yml:

services:
  shelf:
    image: dangahagan/shelf:latest
    container_name: shelf
    ports:
      - "18888:18888"
    environment:
      - CERT_SAN=${CERT_SAN:-DNS:shelf,DNS:localhost}
    volumes:
      - ./data:/data:z
    restart: unless-stopped

Then run:

docker compose up -d
Environment Variables

Create a .env file in the same directory as your docker-compose.yml:

# Add your machine's hostname or IP so other devices on your network
# can access Shelf without certificate warnings
CERT_SAN=IP:192.168.1.100,DNS:shelf,DNS:localhost
VariableDefaultDescription
CERT_SANDNS:shelf,DNS:localhostTLS certificate Subject Alternative Names. Add your machine's IP or hostname so other devices can connect
SECRET_KEY(auto-generated)JWT signing key. If unset, generated at data/signing.key (0600) on first start; an existing key from before 0.30 is moved there from the database on the first start after upgrading, so sessions survive. Set it explicitly to run several instances against one database
SHELF_ENCRYPTION_KEY(auto-generated)Encryption key for stored API credentials. Auto-generated at /data/encryption.key if not set. Set it explicitly (e.g. openssl rand -hex 32) so the data directory alone can't decrypt credentials
SHELF_TRUST_PROXY(unset)Set to 1 when running behind a reverse proxy so client IPs are read from proxy headers

Persistent Data

All data is stored in a single volume mounted at /data:

data/
  shelf.db        — SQLite database (your entire catalog)
  covers/         — cached cover images
  certs/          — auto-generated TLS certificates
  encryption.key  — key for API credentials stored in the DB
                    (unless SHELF_ENCRYPTION_KEY is set)
  signing.key     — signs login sessions (unless SECRET_KEY is set)

Keep both key files out of anything you share. The database itself holds no key material.

Backups: Copy the data/ directory, or use the built-in backup/restore feature in Settings — with an optional passphrase, backup downloads are AES-encrypted and safe to store off-site.

Screenshots

BrowseScan (Add Mode)
BrowseScan
Scan (Lend Mode)Item Detail
LendDetail
StatsAdmin Logs
StatsLogs
Valuation ReportBrowse (Tag Filter)
Valuation ReportTag Filter

Features

Scanning and Cataloging
  • Camera barcode scanning on mobile — tap to scan ISBNs and UPCs, on iPhone and iPad as well as Android (EAN-13, EAN-8, UPC-A, UPC-E). Also available from an item's edit form, for correcting a single wrong ISBN
  • USB/Bluetooth scanner support — works with any scanner that sends Enter after the barcode
  • Photo intake — bulk-add from a photo of your shelves; a vision model (Anthropic API, any OpenAI-compatible endpoint, or fully local Ollama) reads the spines and you confirm before import. Rows typed DVD or Video Game are looked up on TMDb or IGDB at confirm, on an exact title match
  • Title search — search Open Library, TMDb, or IGDB by title when you don't have a barcode
  • Add by hand — a title is the only field required, for anything no lookup can find: a self-published book, a burned CD, a niche import, something you made yourself. Every media type, no barcode needed, and reachable from Home, from an empty title search, from a mistyped barcode, and from any item's page
  • Cascading metadata lookup — Open Library, Hardcover, Google Books, and more
  • Cover art pipeline — automatically fetches covers from multiple sources, with manual upload or a pasted image URL as a fallback, plus a cover review queue for everything the automatic sweep cannot reach (discs, games, music) with a remembered "not available" verdict
  • Store Mode (offline PWA) — scan in a bookstore with no signal and get an instant Owned / On wishlist / Not in library verdict; unknown books queue on-device and land on your wishlist when back online
8 Scan Modes
ModeWhat it does
AddScan barcodes to add items with full metadata lookup
WishlistScan at a bookstore to save items you want
LendSelect a borrower, then scan items to check them out
ReturnScan items to check them back in
MoveSelect a target location, then batch-scan items to relocate them
InventorySelect a location, scan everything there, then check for missing items. Counts physical copies, so a book with copies in two rooms is expected in both; a scan that cannot say which copy reports instead of moving one
LookupScan to check if an item is in your collection — no changes made
Quick RateScan to mark items as read/completed
Media Types
  • Books, audiobooks, eBooks, magazines, DVDs, Blu-rays, vinyl, cassettes, CDs, digital music, comics, manga, kids' books, and video games
  • Music by release, not by title — search MusicBrainz by title, artist, barcode or catalogue number and catalogue the exact pressing: country, date, label, catalogue number, packaging, and real track lists across multiple discs. Two pressings of one album stay distinct and link to each other
  • Periodicals as publication plus issue — a magazine run is one publication with many issues; a 977 barcode resolves it from its ISSN
  • Link physical and digital formats together, and connect a novel to its audiobook or its film adaptation as a related-media group
  • Video game support with IGDB metadata and 30+ platforms (Atari 2600 to PS5)
Collection Management
  • Home overview — totals, what is lent out, missing covers, a media-type breakdown and recent additions, with Browse kept for searching and bulk editing
  • Filter and search by media type, location, reading status, ownership, lending status, and custom tags
  • Shelf Fill — keep one shelf selected and scan item after item onto it; Arrange drags the copies on a shelf into the order they really sit in, or sorts them by title, creator, series, release or issue
  • Physical copies — own two of something and track them apart: add a copy on the item page, give each its own location, condition, acquired date, source, price, provenance and barcode, and remove one when it goes. Removing the copy marked primary promotes the next one and the item's location follows it
  • Reading tracking — want-to-read, reading, and read with start/finish dates
  • Series tracking — grouped by series with position numbers, gap detection, and one-click "add missing volumes to wishlist" via Hardcover; series synopses, plus rename/merge/disband from the series card
  • Stats dashboard — books read per year, collection growth, top authors, and value-over-time charts
  • Locations — organize by room, shelf, or any system you like, and nest them: a shelf inside a bookcase inside a room
  • Checkout system — lend to borrowers and track who has what, with overdue badges and an optional daily reminder digest (ntfy/webhook)
  • Wishlist — a list of what you want, alongside your catalog; an item can also be neither owned nor wished for, keeping its reading history
  • Public share links — read-only wishlist or collection pages for gift ideas, revocable anytime
  • Goodreads & StoryGraph import — upload your export as-is; format auto-detected, covers fetched automatically
  • Custom tags — free-form tags (signed, first-edition, …) with a tag filter on Browse
  • Bulk editing — select items in Browse to move them, change type or reading status, add to or remove from the wishlist, or set and clear series in one go
  • Valuation report — location-grouped, print-ready collection value report for insurance (via ISBNdb)
  • Display currency — 20 currencies for every value surface (formatting, not conversion)
  • CSV import/export, plus a portable archive — export the whole collection as one zip including physical copies and cover art and merge it into any Shelf instance without refetching a cover
Multi-User
  • Admin — full control: settings, users, locations, sync, bulk ops, logs
  • Editor — add/edit/delete items, scan, manage covers, checkout/checkin
  • Viewer — browse, search, reading status, export, view stats

Optional Integrations

Shelf works fully out of the box with no API keys. These optional integrations add extra features — configure them in the Settings page after setup:

ServiceWhat it addsFree?
HardcoverReading status sync, richer metadata, series gap checks, Discover pageYes
AudiobookshelfSync selected audiobook libraries, link physical + digital formatsYes
IGDB (Twitch)Video game metadata, cover art, platform info — on UPC scan, title search, and Photo Intake confirmYes
RomMSync a self-hosted RomM server's digital game libraryYes
KomgaSync a self-hosted Komga server's digital comics and mangaYes
TMDbDVD/Blu-ray metadata — from UPC barcodes, title search, and Photo Intake confirmYes
ISBNdbCollection valuation with market pricesPaid
AnthropicPhoto Intake spine recognition (best accuracy)Pay-per-use
OpenAI-compatiblePhoto Intake via any OpenAI Chat Completions endpoint (OpenAI, OpenRouter, vLLM, LM Studio…)Pay-per-use / free
OllamaPhoto Intake with a fully local vision modelFree

Updating

docker compose pull
docker compose up -d

Or with docker run:

docker pull dangahagan/shelf:latest
docker stop shelf && docker rm shelf
docker run -d --name shelf -p 18888:18888 -v ./shelf-data:/data:z dangahagan/shelf:latest

Your data in the /data volume is preserved across updates.

Tags

TagDescription
latestLatest stable release
betaLatest beta — may have rough edges
x.yLatest patch of a minor line (e.g., 0.10)
x.y.zSpecific version (e.g., 0.10.1)

Tech Stack

LayerTechnology
BackendPython 3.12, FastAPI, SQLite (WAL mode)
FrontendJinja2, HTMX, Alpine.js, Tailwind CSS
Authbcrypt, JWT in HTTP-only secure cookies
ContainerNon-root user, self-signed HTTPS

Tag summary

Content type

Image

Digest

sha256:409743ede

Size

68.9 MB

Last updated

about 2 hours ago

docker pull dangahagan/shelf