A self-hosted EPUB web reader with multi-user support, OPDS browsing, KOReader sync, and a built-in dictionary lookup — all in a single lightweight Node.js container.
| Library | Reader | Table of Contents |
|---|---|---|
![]() |
![]() |
![]() |
| Search | Dictionary | Settings | OPDS |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
- EPUB reader — powered by epub.js, paginated layout with custom fonts, themes, and status bar
- Multi-user — JWT-based authentication, per-user library and reading progress
- Shelves — organise books into custom shelves
- Reading progress — automatically saved; synced across devices via KOReader
- KOReader sync — built-in KOSync-compatible server; also connects to an external KOSync server
- OPDS browser — browse and download books from any OPDS catalogue
- OPDS shelf sync — bulk-download an entire OPDS folder into a shelf
- Dictionary lookup — local StarDict dictionaries (
.ifo/.idx/.dict) - PWA — installable on desktop and mobile (requires network connection to use)
- Multilingual UI — English, Slovenian, German, Spanish, French, Italian, Portuguese
- Light / Dark / E-ink themes
cp docker-compose.sample.yaml docker-compose.yaml# Generate a secret:
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# …or:
openssl rand -hex 64Paste the output into docker-compose.yaml as the value of JWT_SECRET.
docker compose up -dCodexa is now running at http://localhost:3000.
Register the first account — there is no default admin password.
All configuration is via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
JWT_SECRET |
yes | — | Long random string (≥ 64 chars). Changing it invalidates all sessions. |
PORT |
no | 3000 |
TCP port the server listens on |
DATA_DIR |
no | ./data |
Path to persistent data (books, covers, fonts, DB) |
CORS_ORIGIN |
no | (same-origin) | Allowed CORS origin, e.g. https://books.example.com |
data/
├── codexa.db # SQLite database (WAL mode)
├── books/ # Uploaded EPUB files
├── covers/ # Extracted cover images
├── fonts/ # User-uploaded fonts
└── dictionaries/ # StarDict dictionary files (.ifo / .idx / .dict)
Mount this directory as a Docker volume to persist all data across container updates.
docker compose pull
docker compose up -dThe database schema is migrated automatically on startup.
Codexa includes a built-in KOSync-compatible server.
In KOReader:
- Go to Tools → KOReader Sync
- Set Custom sync server to your Codexa URL (e.g.
https://books.example.com) - Log in with the same credentials you use on Codexa
Optionally, you can also connect to an external KOSync server in Settings → KOReader Sync.
Navigate to OPDS Browser in the sidebar.
Add any OPDS-compatible catalogue (Calibre-Web, Komga, Kavita, Ubooquity, Bookwyrm…) in Settings → OPDS Servers.
Place StarDict dictionary files in data/dictionaries/.
Supported extensions: .ifo, .idx, .dict, .dict.dz
Enable and reorder dictionaries in Reader → Settings → Dictionaries.
Shortcuts work when focus is not inside a text input.
| Key | Action |
|---|---|
→ / Space / Page Down |
Next page |
← / Page Up |
Previous page |
| Key | Action |
|---|---|
K |
Open / close Table of Contents |
I |
Open / close in-book Search |
S |
Open / close Reader Settings |
Esc |
Close open panel — or return to Library |
| Key | Action |
|---|---|
F |
Toggle fullscreen |
git clone https://github.com/thehijacker/codexa.git
cd codexa
cp .env.example .env
# Edit .env and set JWT_SECRET
npm install
npm startRequires Node.js ≥ 18.
Codexa serves plain HTTP. Put it behind nginx, Caddy, or Traefik for HTTPS.
Minimal nginx example:
server {
listen 443 ssl;
server_name books.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 200M;
}
}AGPL-3.0 © Andrej Kralj
You are free to use, modify, and self-host this software. If you distribute a modified version (including over a network), you must release the source under the same licence.






