---
title: "Deploy Booklore | (Just Updated) Ebook Library With REST API, Admin Generated at Boot"
description: "Booklore ebook library + MariaDB, OPDS, Kobo sync, REST API, admin set"
category: "Storage"
url: https://railway.com/deploy/booklore-or-just-updated-ebook-library-w
---

# Deploy Booklore | (Just Updated) Ebook Library With REST API, Admin Generated at Boot

Booklore ebook library + MariaDB, OPDS, Kobo sync, REST API, admin set

**[Deploy Booklore | (Just Updated) Ebook Library With REST API, Admin Generated at Boot on Railway](https://railway.com/template/booklore-or-just-updated-ebook-library-w)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/booklore-or-just-updated-ebook-library-w/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Storage

## Template content

### booklore

- **Image:** ghcr.io/booklore-app/booklore:v2.4.0@sha256:f92dae443c38098bf8348742465f6f3b17b619cb8f0e5c2cf195dcdfbb88eaeb
- **Start command:** `/bin/sh -c 'echo "IyBCb29rbG9yZSBvbiBSYWlsd2F5LiBSdW5zIGFzIHJvb3QgKHN0YXJ0IGNvbW1hbmQgcmVwbGFjZXMgdGhlIGltYWdlIEVOVFJZUE9JTlQpLCBzbyBpdAojIGRvZXMgdGhlIGVudHJ5cG9pbnQncyB3b3JrIGl0c2VsZjogdm9sdW1lIGxheW91dCwgb3duZXJzaGlwLCB0aGVuIGhhbmRzIG92ZXIgdG8gdGhlCiMgc3RvY2sgZW50cnlwb2ludC5zaCwgd2hpY2ggc3UtZXhlY3MgdG8gdWlkIDEwMDAuCnNldCAtZQpleHBvcnQgQk9PS0xPUkVfUE9SVD0iJHtQT1JUOi04MDgwfSIgREFUQUJBU0VfVVNFUk5BTUU9Ym9va2xvcmUgREFUQUJBU0VfTkFNRT1ib29rbG9yZSBEQVRBQkFTRV9QT1JUPTMzMDYgVVNFUl9JRD0xMDAwIEdST1VQX0lEPTEwMDAKIyBPbmUgUmFpbHdheSB2b2x1bWUgcGVyIHNlcnZpY2U6IG1vdW50IGl0IGF0IC9kYXRhIGFuZCBwb2ludCB0aGUgdGhyZWUgcGF0aHMgdGhlIGFwcAojIHVzZXMgKGNvbmZpZyArIHRodW1ibmFpbHMsIGxpYnJhcnksIGRyb3AgZm9sZGVyKSBpbnRvIGl0LgpWPS9kYXRhCm1rZGlyIC1wICIkVi9hcHAiICIkVi9ib29rcyIgIiRWL2Jvb2tkcm9wIgpbIC1MIC9hcHAvZGF0YSBdIHx8IHsgcm0gLXJmIC9hcHAvZGF0YTsgbG4gLXMgIiRWL2FwcCIgL2FwcC9kYXRhOyB9ClsgLUwgL2Jvb2tzIF0gfHwgeyBybSAtcmYgL2Jvb2tzOyBsbiAtcyAiJFYvYm9va3MiIC9ib29rczsgfQpbIC1MIC9ib29rZHJvcCBdIHx8IHsgcm0gLXJmIC9ib29rZHJvcDsgbG4gLXMgIiRWL2Jvb2tkcm9wIiAvYm9va2Ryb3A7IH0KY2hvd24gMTAwMDoxMDAwICIkVi9hcHAiICIkVi9ib29rcyIgIiRWL2Jvb2tkcm9wIgplY2hvICJbcmFpbHdheV0gZGF0YV9vd25lcj0kKHN0YXQgLWMgJXU6JWcgJFYvYXBwKSBwb3J0PSRCT09LTE9SRV9QT1JUIGRiPSREQVRBQkFTRV9IT1NUIgojIFdhaXQgZm9yIE1hcmlhREIgKHRoZSBmaXJzdCBib290IG9mIGl0cyB2b2x1bWUgdGFrZXMgYSB3aGlsZSkuCm49MDsgdW50aWwgbmMgLXogLXcyICIkREFUQUJBU0VfSE9TVCIgIiREQVRBQkFTRV9QT1JUIiAyPi9kZXYvbnVsbDsgZG8gbj0kKChuKzEpKTsgaWYgWyAkbiAtZ3QgMTUwIF07IHRoZW4gZWNobyAiW3JhaWx3YXldIGRhdGFiYXNlIG5ldmVyIGFuc3dlcmVkIjsgYnJlYWs7IGZpOyBzbGVlcCAyOyBkb25lCiMgRmlyc3QtYm9vdCBhZG1pbi4gL2FwaS92MS9zZXR1cCBpcyBwdWJsaWMgdW50aWwgdGhlIGZpcnN0IHVzZXIgZXhpc3RzLCBzbyBjcmVhdGUgdGhlCiMgYWRtaW4gdGhlIG1vbWVudCB0aGUgQVBJIGFuc3dlcnMuIExhdGVyIGJvb3RzIGZpbmQgc2V0dXAgY29tcGxldGVkIGFuZCBjaGFuZ2Ugbm90aGluZy4KKAogIFU9Imh0dHA6Ly8xMjcuMC4wLjE6JEJPT0tMT1JFX1BPUlQiOyBpPTAKICB3aGlsZSBbICRpIC1sdCAyNDAgXTsgZG8KICAgIFM9JCh3Z2V0IC1xTy0gIiRVL2FwaS92MS9zZXR1cC9zdGF0dXMiIDI+L2Rldi9udWxsIHx8IHRydWUpCiAgICBjYXNlICIkUyIgaW4KICAgICAgKiciZGF0YSI6dHJ1ZScqKSBlY2hvICJbcmFpbHdheV0gc2V0dXAgYWxyZWFkeSBjb21wbGV0ZWQiOyBleGl0IDAgOzsKICAgICAgKiciZGF0YSI6ZmFsc2UnKikKICAgICAgICBCPSJ7XCJ1c2VybmFtZVwiOlwiYWRtaW5cIixcImVtYWlsXCI6XCIkQk9PS0xPUkVfQURNSU5fRU1BSUxcIixcIm5hbWVcIjpcIkFkbWluXCIsXCJwYXNzd29yZFwiOlwiJEJPT0tMT1JFX0FETUlOX1BBU1NXT1JEXCJ9IgogICAgICAgIGlmIHdnZXQgLXFPLSAtLWhlYWRlciAnQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9qc29uJyAtLXBvc3QtZGF0YSAiJEIiICIkVS9hcGkvdjEvc2V0dXAiID4vZGV2L251bGwgMj4mMTsgdGhlbiBlY2hvICJbcmFpbHdheV0gYWRtaW4gY3JlYXRlZCI7IGV4aXQgMDsgZmkKICAgICAgICBlY2hvICJbcmFpbHdheV0gc2V0dXAgUE9TVCBmYWlsZWQsIHJldHJ5aW5nIiA7OwogICAgZXNhYwogICAgaT0kKChpKzEpKTsgc2xlZXAgMgogIGRvbmUKICBlY2hvICJbcmFpbHdheV0gYWRtaW4gYm9vdHN0cmFwIGdhdmUgdXAiCikgJgpleGVjIGVudHJ5cG9pbnQuc2ggamF2YSAtamFyIC9hcHAvYXBwLmphcgo=" | base64 -d > /tmp/railway-start.sh; exec sh /tmp/railway-start.sh'`
- **Health check:** /
- **Public domain:** Yes

### mariadb

- **Image:** mariadb:11.8@sha256:6422478cb8e159f080fb1d8ccf65101e26fe51385787fde7d16c3b165a331f15
- **Start command:** `/bin/sh -c 'set -e; M=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo max); if [ "$M" != max ]; then B=$((M / 2 / 1048576)); [ "$B" -lt 128 ] && B=128; else B=512; fi; export MARIADB_DATABASE=booklore MARIADB_USER=booklore; echo "[railway] uid=$(id -u) innodbBufferPoolMB=$B owner=$(stat -c %u:%g /var/lib/mysql)"; exec docker-entrypoint.sh mariadbd --datadir=/var/lib/mysql/data --innodb-buffer-pool-size=${B}M --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci'`

## Documentation

# Deploy and Host Booklore on Railway

Booklore is a self-hosted ebook and comic library with multi-user accounts, smart shelves, automatic metadata, a built-in EPUB/PDF/CBZ reader, OPDS and Kobo/KOReader sync, and a REST API. This template deploys Booklore with its own MariaDB database, a persistent volume, and **an admin account generated at deploy time** - nothing to fill in, no setup wizard to click through.

**Easy for an LLM agent to use.** Deploy, read the variables below, log in over the REST API and call it. The first admin user is created by the start command the moment the API answers, so the public setup endpoint is never left open to a stranger.

## About Hosting Booklore

Two services run in one Railway project:

- **booklore** - the official `ghcr.io/booklore-app/booklore` image (pinned by digest). One volume mounted at `/data` holds the app config and thumbnails (`/app/data`), the library folder (`/books`) and the BookDrop import folder (`/bookdrop`); the start command links those three paths into the volume.
- **mariadb** - the official `mariadb` image (pinned by digest) on its own volume. The data directory is a subfolder of the mount, so the filesystem's `lost+found` never lands inside it. The database password is generated and wired into Booklore over the private network.

On first boot the start command waits for MariaDB, lets Booklore run its migrations, then calls the setup endpoint once to create the `admin` user with the generated password. On every later boot setup is already complete and nothing changes, so redeploys are safe. Booklore is a JVM app and idles at about 1 GB of memory.

### Agent contract

- Public URL: `https://RAILWAY_PUBLIC_DOMAIN` (the service domain)
- Private URL: `http://booklore.railway.internal:8080` (database: `mariadb.railway.internal:3306`, user and database `booklore`)
- Credentials: username `admin` (fixed), email in `BOOKLORE_ADMIN_EMAIL`, password in `BOOKLORE_ADMIN_PASSWORD`
- Auth: `POST /api/v1/auth/login` with JSON `{"username","password"}` returns `accessToken` (JWT); send it as `Authorization: Bearer ACCESS_TOKEN`
- Write path: a request with no token is refused, `write without key: 403` (and `read without key: 403`)
- Health: `GET /` returns 200; `GET /api/v1/setup/status` returns `"data":true` once the admin exists

```bash
BASE=https://YOUR-DOMAIN.up.railway.app
TOKEN=$(curl -s "$BASE/api/v1/auth/login" -H 'content-type: application/json' \
  -d "{\"username\":\"admin\",\"password\":\"$BOOKLORE_ADMIN_PASSWORD\"}" | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
curl -s "$BASE/api/v1/setup/status"                       # {"data":true,...}
curl -s "$BASE/api/v1/libraries" -H "Authorization: Bearer $TOKEN"          # []
curl -s -X POST "$BASE/api/v1/libraries" -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"name":"Main","icon":"book","iconType":"PRIME_NG","watch":false,"paths":[{"path":"/books"}]}'
curl -s -o /dev/null -w "read without key: %{http_code}\n" "$BASE/api/v1/libraries"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST "$BASE/api/v1/libraries" \
  -H 'content-type: application/json' -d '{"name":"x","paths":[{"path":"/books"}]}'
# expect: write without key: 403
```

## Why Deploy Booklore on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Booklore on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

- **No setup wizard** - the admin account exists as soon as the service is healthy, with a generated password in your variables.
- **Setup endpoint closed at boot** - the first-user endpoint is public until an admin exists, so the start command creates the admin within seconds of the API starting, before Railway routes traffic to it.
- **Zero configuration** - no variable asks you for anything; the database is wired over the private network.
- **Data survives redeploys** - the library, covers, accounts and database each sit on a volume, verified with a write, a redeploy and a read.

## Common Use Cases

- **Personal ebook and comic library** - upload EPUB, PDF and CBZ files and read them in the browser on any device.
- **E-reader sync server** - OPDS for reading apps, plus Kobo and KOReader progress sync.
- **Agent-managed library** - let an LLM agent create libraries and manage books through the REST API.

## Dependencies for Booklore Hosting

- A volume at `/data` on the `booklore` service and a volume at `/var/lib/mysql` on the `mariadb` service (both included in this template).

### Deployment Dependencies

- [booklore-app/booklore](https://github.com/booklore-app/booklore) - upstream project
- [Booklore documentation](https://booklore.org/docs/getting-started) - setup and features
- [mariadb on Docker Hub](https://hub.docker.com/_/mariadb) - the database image


## Similar templates

- [RustFS | (Just Updated) S3 Object Store, Public URL and Keys Set](https://railway.com/deploy/rustfs-or-just-updated-s3-object-store-p) — S3-compatible object store, keys and public URL preset, volume kept
- [TimescaleDB](https://railway.com/deploy/timescaledb-railway-template) — A PostgreSQL-based time-series database for analytics on Railway.
- [Neo4j 2026.10 Community](https://railway.com/deploy/neo4j-202610-community) — Neo4j graph database with APOC, Browser, Bolt TCP proxy and a volume

Open this page in a browser: https://railway.com/deploy/booklore-or-just-updated-ebook-library-w
