A stand-alone, dockerized FastAPI service that implements an OPDS 2.0 feed for Open Library, backed by pyopds2_openlibrary.
app/
main.py # FastAPI app + exception handlers
logger.py # Central logging factory (stdout only)
config/
__init__.py # Media types, env vars, featured subjects
exceptions/
__init__.py # OPDSException, EditionNotFound, UpstreamError
routes/
opds.py # All route handlers (/, /search, /books/*)
docker/
Dockerfile # Docker image definition
docker-compose.yml # Compose service definition
scripts/
configure.sh # Generates .env from defaults
Interactive docs: /docs (Swagger UI) · /redoc
- Docker + Docker Compose — for the recommended Docker workflow
- Python 3.12+ — only needed for local development without Docker
Alternatively Podman can be used. In that case, aardvark-dns is required.
git clone https://github.com/ArchiveLabs/opds.openlibrary.org.git
cd opds.openlibrary.org./scripts/configure.shThis creates a .env file with sensible defaults. Edit it to override any values before starting the service.
docker compose up --buildThe service is available at http://localhost:8080.
To run in the background:
docker compose up --build -ddocker compose downManaged via .env (generated by configure.sh). All are optional.
| Variable | Default | Description |
|---|---|---|
OPDS_BASE_URL |
(falls back to request base URL) | Public URL of this OPDS service, used in self-referencing links. When unset, the base URL is inferred from each incoming request (suitable for local dev). Set this when running behind a reverse proxy. |
OL_BASE_URL |
https://openlibrary.org |
OpenLibrary backend URL for API calls and alternate links. |
OL_USER_AGENT |
OPDSBot/1.0 (opds.openlibrary.org; opds@openlibrary.org) |
User-Agent sent with every request to OpenLibrary. |
OL_REQUEST_TIMEOUT |
30.0 |
Timeout in seconds for requests to the OpenLibrary API. |
# 1. Create and activate a virtual environment
python -m venv env
source env/bin/activate # Windows: env\Scripts\activate
# 2. Install dependencies
pip install -r requirements.txt
# 3. Start the server
uvicorn app.main:app --reload --port 8080# Homepage catalog
curl -s http://localhost:8080/ | python -m json.tool | head -30
# Search
curl -s "http://localhost:8080/search?query=Python&limit=5" | python -m json.tool | head -30
# Single edition
curl -s http://localhost:8080/books/OL7353617M | python -m json.tool | head -30
# 404 — non-existent edition
curl -s http://localhost:8080/books/OL0000000M
# → {"detail": "Edition not found: OL0000000M"}pip install pytest httpx
pytest -vAll tests are offline — network calls to OpenLibrary are mocked.
| Exception | HTTP status | Cause |
|---|---|---|
EditionNotFound |
404 | Edition OLID not found in OpenLibrary |
UpstreamError |
502 | OpenLibrary returned an error or is unreachable |
All errors are logged to stdout by the service logger.