Serve an OPDS 1.1–compliant book catalog from a directory. No database, no Calibre—just point dir2opds at a folder and use any OPDS client to browse and download your books.
- What is OPDS?
- Features
- Quick start
- Installation
- Usage
- Caching
- Pagination
- Compatible clients
- Documentation
- Contributing
- License
- Acknowledgments
OPDS (Open Publication Distribution System) is a standard for cataloging and distributing digital publications. OPDS clients (ebook readers, apps) can discover, browse, and download books from an OPDS server. dir2opds turns a plain directory tree into such a server.
- OPDS 1.1 compliant — Works with standard ebook readers and OPDS clients
- No database — Reads directly from your filesystem; no Calibre or extra setup
- Flexible layout — Organize by folders; optional metadata from EPUB/PDF
- Search — Optional filename search (OpenSearch)
- Covers — Optional
cover.jpg/folder.jpgas catalog covers, or extract covers from EPUB files - Pagination — Configurable page size for large catalogs
- Caching — ETag/Last-Modified for conditional requests, gzip compression
- Health endpoint —
/healthendpoint for monitoring and load balancers - Structured Logging — Uses
log/slogfor JSON (default) or text logging - Multiple formats — EPUB, PDF, MOBI, AZW3, and more via configurable MIME types
- Lightweight — Single binary; suitable for headless servers and containers
Using Docker (replace v1.8.0 with the latest release if desired):
docker run -d -p 8080:8080 -v ./books:/books --name dir2opds ghcr.io/dubyte/dir2opds:v1.8.0Then open http://localhost:8080 in an OPDS client or browser.
Using Go:
go install github.com/dubyte/dir2opds@latest
dir2opds -dir /path/to/books -port 8080Tip: For best client compatibility, use folders that contain either only subfolders (navigation) or only book files (acquisition), not mixed.
go install github.com/dubyte/dir2opds@latestFor other installation methods (Docker, Podman, pre-built binaries, etc.), see INSTALLATION.md.
Default: serve ./books on http://0.0.0.0:8080.
dir2opds -dir /path/to/books -port 8080| Flag | Description |
|---|---|
-calibre |
Hide files stored by Calibre |
-debug |
Log requests |
-dir |
Directory with books (default: ./books) |
-enable-cache |
Enable ETag/Last-Modified headers for conditional requests (bandwidth optimization) |
-extract-metadata |
Extract title/author from EPUB and PDF, and covers from EPUB |
-gzip |
Enable gzip compression for responses (reduces bandwidth) |
-hide-dot-files |
Hide files whose names start with a dot |
-host |
Listen address (default: 0.0.0.0) |
-log-format |
Log format: json (default), text |
-mime-map |
Custom MIME types, e.g. .mobi:application/x-mobipocket-ebook,.azw3:application/vnd.amazon.ebook |
-no-cache |
Add response headers to disable client caching |
-page-size |
Number of entries per page (default: 50, max: 200) |
-port |
Listen port (default: 8080) |
-search |
Enable basic filename search |
-show-covers |
Use cover.jpg or folder.jpg as catalog covers |
-sort |
Sort entries: name, date, or size (default: name) |
-url |
The base URL used for absolute links in the feed (e.g., https://opds.example.com) |
For the best experience, use these flags:
dir2opds -dir /path/to/books -extract-metadata -enable-cache -gzipThis enables:
- Metadata extraction — Shows book titles and authors instead of filenames, plus cover thumbnails
- Caching — Reduces bandwidth with ETag/Last-Modified headers
- Gzip compression — Further reduces bandwidth for large catalogs
For public servers, also set the base URL:
dir2opds -dir /path/to/books -extract-metadata -enable-cache -gzip -url https://opds.example.comdir2opds provides two caching-related options with different use cases:
Clients use their default caching behavior. No special headers are sent.
Forces clients to always fetch fresh data from the server. Useful for:
- Frequently changing libraries (adding/removing books often)
- Ensuring clients always see the latest catalog
dir2opds -dir /books -no-cacheThis adds the following headers to every response:
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Enables bandwidth optimization through HTTP conditional requests. Useful for:
- Large static libraries that rarely change
- Reducing bandwidth when clients re-fetch the same catalog
- Mobile clients on metered connections
dir2opds -dir /books -enable-cacheThis adds the following headers to responses:
ETag: "<hash>"
Last-Modified: <timestamp>
Clients can then send conditional requests:
If-None-Match: "<hash>"
If-Modified-Since: <timestamp>
If the catalog hasn't changed, the server responds with 304 Not Modified (no body), saving bandwidth.
Using both -no-cache and -enable-cache is not recommended. -no-cache prevents clients from caching anything, so the 304 optimization from -enable-cache would never be used.
For large libraries, dir2opds paginates catalog feeds to improve performance and reduce bandwidth.
- Feeds are split into pages with a configurable number of entries per page
- Each page includes navigation links (
first,previous,next,last) - Clients can request specific pages via the
?page=Nquery parameter
# Default: 50 entries per page
dir2opds -dir /books
# Custom page size: 100 entries per page
dir2opds -dir /books -page-size 100
# Maximum page size: 200 entries
dir2opds -dir /books -page-size 200When pagination is active, feeds include navigation links:
<feed>
<link rel="first" href="/?page=1" type="..."/>
<link rel="previous" href="/?page=1" type="..."/>
<link rel="next" href="/?page=3" type="..."/>
<link rel="last" href="/?page=10" type="..."/>
<!-- entries -->
</feed>Clients can navigate pages directly:
GET / # Page 1 (default)
GET /?page=2 # Page 2
GET /mybook?page=1 # Page 1 of /mybook
These OPDS clients have been tested with dir2opds:
| Client | Platform | Notes |
|---|---|---|
| Moon+ Reader | Android | Tested |
| Cantook | iPhone | Tested |
| KYBook 3 | iOS | Enable Settings → Apps → KyBook 3 → Local Network. Older app may not show the prompt; enable manually. |
Contributions are welcome. Please read CONTRIBUTING.md for license agreements, development setup, and pull request process.
This project is licensed under the GNU General Public License v3.0. See LICENSE for the full text.
- @clach04 — Testing and reporting missing content type for comics.
- @masked-owl — Reporting the HTTP path traversal security issue.
- @mufeedali — Update to push image to ghcr.io.
- @kulak — Add podman support.
- @thenktor - init files and Makefile improvements.
- @rockavoldy — For the docker command example.