A small Rust service for keeping a local Kobo-friendly book library on a Raspberry Pi or other home server.
Open the web page from a phone or computer, upload an EPUB, then open the same page from the Kobo browser and download the converted book. The app is designed for a trusted local network and intentionally does not use pairing keys or accounts.
- Single local web page for upload, download, and deletion.
- EPUB upload from a phone, tablet, or computer.
- Automatic conversion from
.epubto.kepub.epubwith kepubify. - Existing kepubs are detected and stored without running
kepubifyagain. - EPUB metadata extraction for title and author.
- Persistent local library stored under
data/. - QR code for the current page, useful when the page is open on the Kobo and you want to open it on a phone.
- Rust/Axum server with a single compiled binary.
- systemd user service support.
- Docker Compose support.
- Open the app from a phone, tablet, or computer.
- Tap
Choose EPUB. - Pick an
.epubor.kepub.epubfile. - Tap
Upload. - Open the same app page from the Kobo browser.
- Tap
Downloadbeside the book.
The page also includes Delete actions for removing books from the local
library.
Normal EPUB files are converted with kepubify and stored as .kepub.epub
files.
Files are stored without conversion when either of these is true:
- The uploaded filename ends in
.kepub.epub. - The EPUB content already contains Kobo/kepub markers such as
koboSpan.
The app reads EPUB metadata from META-INF/container.xml and the package OPF
file. When metadata is available, the UI shows the book title and author instead
of relying on the filename.
GET /- main app page.POST /upload- upload one EPUB file.GET /api/books- list books used by the UI.GET /books/{id}/download- download one stored kepub.DELETE /api/books/{id}- delete one stored book.GET /qr/page.svg- QR code for the current page URL.
There is no /receive page anymore. The app is intentionally a single-page
flow.
Install a local Linux kepubify binary:
sh scripts/install-kepubify-linux.shBuild and run:
cargo runOpen:
http://SERVER_IP:3001/
For a release build:
cargo build --release
./target/release/kobo-libraryYou can run Kobo Library as a systemd user service on a Raspberry Pi or other
Linux home server. Create ~/.config/systemd/user/kobo-library.service:
[Unit]
Description=Kobo Library
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/path/to/kobo-library
ExecStart=/path/to/kobo-library/target/release/kobo-library
Environment=PORT=3001
Environment=DATA_DIR=/path/to/kobo-library/data
Environment=KEPUBIFY_BIN=/path/to/kobo-library/bin/kepubify
Environment=MAX_UPLOAD_MB=800
Restart=on-failure
[Install]
WantedBy=default.targetUseful commands:
systemctl --user status kobo-library.service
systemctl --user restart kobo-library.service
systemctl --user stop kobo-library.service
systemctl --user enable kobo-library.service
systemctl --user disable --now kobo-library.service
journalctl --user -u kobo-library.service -fAfter rebuilding the release binary, restart the service:
cargo build --release
systemctl --user restart kobo-library.serviceDocker is not currently installed on this Pi, but the project includes a Compose setup for machines that have Docker available:
docker compose up -d --buildThe image downloads the correct kepubify binary for these Linux
architectures:
- x86_64
- ARM64
- ARMv7
- ARMv6
The Compose service mounts local persistent data:
./data:/app/data
Configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
3001 |
HTTP port to listen on. |
DATA_DIR |
./data |
Directory for metadata, uploads, and converted books. |
KEPUBIFY_BIN |
./bin/kepubify if present, otherwise kepubify from PATH |
Converter executable. |
MAX_UPLOAD_MB |
800 |
Maximum request body size in MB. |
Runtime data lives under DATA_DIR:
data/
books.json
books/
uploads/
books.jsonstores library metadata.books/stores the downloadable.kepub.epubfiles.uploads/stores temporary upload files while processing.
Keep data/ if you want to preserve the library across rebuilds or service
restarts.
Common checks:
cargo fmt -- --check
cargo clippy -- -D warnings
cargo test
cargo build --releaseProject structure:
src/
main.rs server startup
config.rs environment configuration
routes.rs HTTP routes and handlers
library.rs upload storage and conversion flow
books.rs metadata persistence and public book models
epub.rs EPUB metadata and kepub detection
conversion.rs kepubify wrapper
error.rs HTTP error mapping
static/
upload.html single app page
style.css UI styling
common.js upload, list, download, delete behavior
Check whether the service is running:
systemctl --user status kobo-library.serviceFollow logs:
journalctl --user -u kobo-library.service -fConfirm the app responds locally:
curl -I http://127.0.0.1:3001/Confirm kepubify is available:
./bin/kepubify --versionIf raspi.local does not resolve from another device, use the server IP address:
http://SERVER_IP:3001/
mDNS requires same-network access, multicast UDP 5353, and client support for
.local hostnames.
Kobo Library has no authentication. Anyone on the reachable network can upload, download, and delete books.
Use it only on a trusted local network. Do not expose it directly to the public internet.