Project Overview
Pagebound is a self-hosted, lightweight e-book library server that turns any remote server into a private media hub. I built it as a dedicated ingestion and delivery engine for personal ePub collections, bridging the gap between desktop file management and resource-constrained e-readers.
The entire system runs as a single Go binary with an embedded Vue 3 frontend, requiring zero runtime dependencies beyond the executable itself.
The Problem
Transferring e-books across personal devices and e-readers is incredibly inconvenient. Relying on proprietary cloud storage solutions like Google Drive creates unwanted ecosystem lock-in and sync overhead. Conversely, using the e-reader’s built-in local file sharing is a high-friction chore: it requires manually enabling a server on the device, ensuring both devices are on the exact same Wi-Fi network, and typing local IP links on a computer just to transfer a file.
Furthermore, standard self-hosted library platforms rely on heavyweight background daemons like Samba or complex database configurations that waste unnecessary memory on a virtual private server (VPS).
The Solution
I designed Pagebound as a single-binary, dual-protocol media gateway optimized for low resource usage, deployment simplicity, and seamless wireless delivery.
- Dual-Protocol Over a Single Port: A single Go server serves two distinct protocols concurrently over one HTTP port. WebDAV handles file ingestion from a desktop machine, and OPDS exposes an XML catalog to e-readers. This creates a frictionless, pull-on-demand workflow where browsing and downloading requires just a few taps directly on the e-reader, completely eliminating the need to manually toggle local file-sharing servers.
- Ingestion-Driven Metadata Extraction: The Go backend hooks into the WebDAV upload pipeline. When a file is uploaded, the backend parses the ePub in-band, extracts metadata from the internal
content.opfXML (title, author, cover art), updates the SQLite database, and persists the file to disk before the upload response returns to the client. - Cozy Web Dashboard: A Vue 3 dashboard built with a warm, library-themed interface lets you browse a virtual bookshelf, search by title or author, upload new books, and download or delete existing ones.
- Zero-Configuration Storage: The application uses an embedded SQLite database that requires no process management, no schema migrations, and no external services. Data is persisted via a Docker named volume, surviving restarts and rebuilds.
Deployment & Infrastructure
- Containerized Deployment: The full stack is encapsulated in a Docker multi-stage build. The frontend is built in a Node stage, the Go binary is compiled with
CGO_ENABLED=0for a static build, and the final runtime image is a minimal Alpine layer, totaling roughly 42 MB. - Single-Command Deploy: With Docker Compose, the entire application can be deployed on any machine with a single command. A named volume persists the book store and database across container lifecycles.
- Tailscale Mesh Access: The server runs on a private VPS connected to a secure Tailscale mesh network. By installing Tailscale directly on the e-reader and other devices, the dashboard and OPDS feeds can be accessed securely from anywhere in the world without exposing public ports.
Key Features
- Seamless Ingestion: Upload books by dragging files into a mapped WebDAV drive via native file managers like macOS Finder, or through the web dashboard upload utility.
- Automatic Metadata Parsing: Every uploaded ePub is parsed for title, author, language, ISBN, publisher, and cover image without manual tagging required.
- OPDS 2.0 Feed: OPDS-compatible e-readers can browse and download books directly from the server with minimal clicks.
- Cover Image Extraction: Book covers are extracted from ePubs and served as static assets in both the dashboard and the OPDS feed.
- Single Binary: The Go backend has zero runtime dependencies with no separate Python runtimes or external database daemons required.
Key Technical Details
- Single-Binary Concurrency: A single Go process routes WebDAV operations, OPDS queries, REST API endpoints, and the embedded Vue frontend over one HTTP port. The WebDAV handler is mounted directly (bypassing Gin) so that non-standard HTTP methods like PROPFIND, MOVE, and LOCK work correctly with macOS Finder.
- On-the-Fly File Hooks: A custom
webdav.FileSystemwrapper intercepts file creation events for.epubfiles. The hook triggers metadata extraction synchronously before the upload response is sent, ensuring the database is always consistent with disk state. - Embedded Frontend Build: The production Vue build is embedded into the Go binary using
//go:embed, producing a single deployable artifact with no separate static file server needed. - Deterministic Book Identification: Each book is identified by the SHA-256 hash of its relative file path, creating a deterministic ID that survives re-uploads without duplication.
- Structured Data Model: Go pointer fields distinguish between missing metadata (nil) and empty values, preserving data fidelity from the source files.