Self-hosted OPDS ebook server — turn any folder into a digital librar
Go
118
141 commits
updated Sep 4, 2026
dir2opds is a self-hosted OPDS ebook server that turns any folder into a digital library. No database, no Calibre—just point it at your book collection and browse or download via any OPDS-compatible ebook reader or web browser.
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 is a lightweight, self-hosted OPDS server that turns a plain directory tree into a personal digital library—no database or complex setup required.
dir2opds is ideal for anyone who wants a self-hosted digital library without the complexity of Calibre or other database-driven solutions. It is perfect for:
cover.jpg / folder.jpg as catalog covers, or extract covers from EPUB files/health endpoint for monitoring and load balancerslog/slog for JSON (default) or text loggingUsing Docker (replace v1.11.0 with the latest release if desired):
docker run -d -p 8080:8080 -v ./books:/books --name dir2opds ghcr.io/dubyte/dir2opds:v1.11.0
Then open `http://localhost:8080` in an OPDS client or browser.
**Using Go:**
```bash
go install github.com/dubyte/dir2opds@latest
dir2opds -dir /path/to/books -port 8080
Tip: For best client compatibility, use folders that contain either only subfolders (navigation) or only book files (acquisition), not mixed. This keeps your self-hosted digital library well-organized and easy to browse.
go install github.com/dubyte/dir2opds@latest
For 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 |
|---|---|
-hide-calibre-files | Hide files stored by Calibre (default: true). The old -calibre flag still works but will show a deprecation warning. |
-debug | Log requests |
-dir | Directory with books (default: ./books) |
-enable-cache | Enable ETag/Last-Modified headers for conditional requests (bandwidth optimization) |
-enable-html | Enable web-friendly HTML view for browsers |
-extract-metadata | Extract title/author/description/series/subjects from EPUB, title/author from PDF, and covers from EPUB (default: true) |
-gzip | Enable gzip compression for responses (reduces bandwidth) |
-hide-dot-files | Hide files whose names start with a dot (default: true) |
-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 |
-no-pagination | Disable pagination and show all entries in a single feed |
-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 (default: true) |
-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) |
If you need the old behavior where all files are shown and no metadata is extracted:
dir2opds -dir /path/to/books -hide-calibre-files=false -hide-dot-files=false -extract-metadata=false -show-covers=false
For public servers, also set the base URL:
dir2opds -dir /path/to/books -url https://opds.example.com
dir2opds provides two caching-related options with different use cases:
Clients use their default caching behavior. No special headers are sent.
-no-cache — Disable CachingForces clients to always fetch fresh data from the server. Useful for:
dir2opds -dir /books -no-cache
This adds the following headers to every response:
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
-enable-cache — Enable Conditional RequestsEnables bandwidth optimization through HTTP conditional requests. Useful for:
dir2opds -dir /books -enable-cache
This 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.
first, previous, next, last)?page=N query 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 200
# Disable pagination: show all entries
dir2opds -dir /books -no-pagination
For small libraries or when using clients that work better with complete feeds, you can disable pagination entirely:
dir2opds -dir /books -no-pagination
When -no-pagination is set:
?page=N query parameter is ignoredWhen 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.
Go
96.5%
Makefile
2.2%
Self-hosted OPDS ebook server — turn any folder into a digital librar
Go
118
141 commits
updated Sep 4, 2026
dir2opds is a self-hosted OPDS ebook server that turns any folder into a digital library. No database, no Calibre—just point it at your book collection and browse or download via any OPDS-compatible ebook reader or web browser.
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 is a lightweight, self-hosted OPDS server that turns a plain directory tree into a personal digital library—no database or complex setup required.
dir2opds is ideal for anyone who wants a self-hosted digital library without the complexity of Calibre or other database-driven solutions. It is perfect for:
cover.jpg / folder.jpg as catalog covers, or extract covers from EPUB files/health endpoint for monitoring and load balancerslog/slog for JSON (default) or text loggingUsing Docker (replace v1.11.0 with the latest release if desired):
docker run -d -p 8080:8080 -v ./books:/books --name dir2opds ghcr.io/dubyte/dir2opds:v1.11.0
Then open `http://localhost:8080` in an OPDS client or browser.
**Using Go:**
```bash
go install github.com/dubyte/dir2opds@latest
dir2opds -dir /path/to/books -port 8080
Tip: For best client compatibility, use folders that contain either only subfolders (navigation) or only book files (acquisition), not mixed. This keeps your self-hosted digital library well-organized and easy to browse.
go install github.com/dubyte/dir2opds@latest
For 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 |
|---|---|
-hide-calibre-files | Hide files stored by Calibre (default: true). The old -calibre flag still works but will show a deprecation warning. |
-debug | Log requests |
-dir | Directory with books (default: ./books) |
-enable-cache | Enable ETag/Last-Modified headers for conditional requests (bandwidth optimization) |
-enable-html | Enable web-friendly HTML view for browsers |
-extract-metadata | Extract title/author/description/series/subjects from EPUB, title/author from PDF, and covers from EPUB (default: true) |
-gzip | Enable gzip compression for responses (reduces bandwidth) |
-hide-dot-files | Hide files whose names start with a dot (default: true) |
-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 |
-no-pagination | Disable pagination and show all entries in a single feed |
-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 (default: true) |
-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) |
If you need the old behavior where all files are shown and no metadata is extracted:
dir2opds -dir /path/to/books -hide-calibre-files=false -hide-dot-files=false -extract-metadata=false -show-covers=false
For public servers, also set the base URL:
dir2opds -dir /path/to/books -url https://opds.example.com
dir2opds provides two caching-related options with different use cases:
Clients use their default caching behavior. No special headers are sent.
-no-cache — Disable CachingForces clients to always fetch fresh data from the server. Useful for:
dir2opds -dir /books -no-cache
This adds the following headers to every response:
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
-enable-cache — Enable Conditional RequestsEnables bandwidth optimization through HTTP conditional requests. Useful for:
dir2opds -dir /books -enable-cache
This 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.
first, previous, next, last)?page=N query 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 200
# Disable pagination: show all entries
dir2opds -dir /books -no-pagination
For small libraries or when using clients that work better with complete feeds, you can disable pagination entirely:
dir2opds -dir /books -no-pagination
When -no-pagination is set:
?page=N query parameter is ignoredWhen 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.
Go
96.5%
Makefile
2.2%