Installation
BookOrbit runs in Docker. The only host dependency is Docker itself. The bundled Compose stack starts BookOrbit and PostgreSQL with persistent storage on your server.
Requirements
Section titled “Requirements”| Requirement | Minimum |
|---|---|
| OS | Any Linux distro, macOS, or Windows with WSL2 |
| CPU | 1 core (2+ recommended) |
| RAM | 512 MB free (1 GB+ recommended) |
| Disk | Space for your book files + ~50 MB for the app |
| Docker | Latest stable (install guide) |
| Docker Compose | v2.0+ (included with Docker Desktop) |
Any modern browser works - Chrome, Firefox, Safari, Edge.
Install
Section titled “Install”Create the install folder
Section titled “Create the install folder”Pick a location on your server for BookOrbit to live. The Compose file and default persistent data paths stay inside this folder. You can point BOOKS_HOST_PATH elsewhere if your library already exists in another location.
mkdir bookorbit && cd bookorbitmkdir -p books data/app data/postgrescurl -fsSLo .env https://raw.githubusercontent.com/bookorbit/bookorbit/main/.env.examplecurl -fsSLo docker-compose.yml https://raw.githubusercontent.com/bookorbit/bookorbit/main/docker-compose.ymlEdit .env before starting. At minimum, set these values:
# Docker image to run. The default in the downloaded template is fine for most installs.APP_IMAGE=ghcr.io/bookorbit/bookorbit:latest
# The address you will use to open BookOrbit.# Direct LAN access: http://your-server-ip:3000# Behind a reverse proxy with a domain: https://books.example.comAPP_URL=http://your-server-ip:3000
# The folder on your server where your book files live.# This folder appears as /books inside BookOrbit.BOOKS_HOST_PATH=./books
# POSTGRES_USER and POSTGRES_DB default to "bookorbit" in the downloaded template.# Change POSTGRES_PASSWORD to something strong.POSTGRES_PASSWORD=use-a-strong-random-passwordJWT_SECRET=use-a-long-random-secretSETUP_BOOTSTRAP_TOKEN=use-a-random-setup-tokenStart BookOrbit
Section titled “Start BookOrbit”docker compose up -dDocker will pull the images and start both the database and app. Wait about 30 seconds on first startup.
Complete setup
Section titled “Complete setup”Open your browser and go to http://your-server-ip:3000.
You’ll be redirected to a setup page - create your administrator account here. You’ll need your SETUP_BOOTSTRAP_TOKEN from .env.
Unraid Installation
Section titled “Unraid Installation”BookOrbit is available on Unraid through Community Applications as two templates: bookorbit and bookorbit-db (PostgreSQL + pgvector). Search BookOrbit in the Apps tab to install both.
The two containers need to be able to reach each other. There are two ways to set that up:
- Host IP and Port (default, simplest) - both templates use Unraid’s default
bridgenetwork. Point the app at the database’s container IP and its exposed port. - Custom Docker Network - attach both containers to a shared custom network and reach the database by container name instead of by IP.
Install the database first
Section titled “Install the database first”Search for bookorbit-db in the Apps tab and install it first. Fill in:
- Port - host port used to reach the database,
5432by default. Note this value; you’ll need it for the app’s Postgres Port field - Postgres User / Postgres DB - defaults of
bookorbitare fine - Postgres Password - generate a strong value (see Generating secrets above)
- Database Storage - defaults to
/mnt/user/appdata/bookorbit-db
Apply, and wait about 20-30 seconds for Postgres to finish initializing before starting the app container.
Install the app
Section titled “Install the app”Search for bookorbit and install it. Fill in:
- WebUI Port -
3000by default - Books Folder - point at your existing books share, e.g.
/mnt/user/data/media/books - App URL -
http://your-server-ip:3000, or your reverse-proxy domain - Postgres User / Password / DB - must exactly match what you set on
bookorbit-db - Postgres Host - the
bookorbit-dbcontainer’s IP address (find it under Docker → Container IP), or the container namebookorbit-dbif you’re using a custom network - Postgres Port - must match the Port value you set on
bookorbit-db - JWT Secret and Setup Bootstrap Token - generate your own random values
- PUID / PGID - match the user/group that owns your Books Folder
Less commonly changed variables (Book Dock path, fix-permissions toggle, Node heap size, log level) are available under the template’s Show more settings… section.
Complete setup and updates
Section titled “Complete setup and updates”Once both containers are running, open your App URL and complete setup using your Setup Bootstrap Token, the same as any other install.
After Installation
Section titled “After Installation”You’re in. Next, set up your first library so BookOrbit knows where your books live.
File Permissions
Section titled “File Permissions”On Linux and NAS installs, Docker uses numeric user and group IDs when it reads or writes mounted folders.
BookOrbit runs as PUID:PGID from your .env file. The default is 1000:1000.
PUID=1000PGID=1000BOOKS_HOST_PATH=/mnt/media/booksYour BOOKS_HOST_PATH must be readable by that user. It must also be writable if you want BookOrbit to upload books, finalize Book Dock files, or write metadata back to files.
To check who owns your book folder:
ls -ldn /mnt/media/booksIf your media user is different, set PUID and PGID to that user:
id your-media-userThen restart:
docker compose up -dCommon signs of a permission issue:
- A scan finishes but finds no books
- Uploads fail when saving into
/books - Book Dock cannot finalize files
- Logs mention
EACCES,permission denied, ornot writable
External Database
Section titled “External Database”If you already have a PostgreSQL server running (like on a NAS or a managed service), you can use it instead of the built-in Docker container.
1. Requirements
Section titled “1. Requirements”- PostgreSQL 16 or newer
- Four extensions installed on your database:
uuid-ossp,pg_trgm,unaccent, andvector(pgvector) - A dedicated database and user for BookOrbit
2. Update your environment file
Section titled “2. Update your environment file”Add DATABASE_URL to your .env file, replacing it with your database details:
DATABASE_URL=postgres://bookorbit:your-password@192.168.1.10:5432/bookorbit3. Modify docker-compose.yml
Section titled “3. Modify docker-compose.yml”Remove the postgres: service block entirely. Also remove this block from the app: service because the app no longer waits for the bundled database:
depends_on: postgres: condition: service_healthyConfiguration
Section titled “Configuration”All configuration is done through the .env file. After changing any value, restart the stack:
docker compose up -dEnvironment variables
Section titled “Environment variables”| Variable | Required | Template/default | Description |
|---|---|---|---|
APP_IMAGE | Yes | ghcr.io/bookorbit/bookorbit:latest | Docker image to run |
APP_URL | Yes | - | Full URL where BookOrbit is accessible |
POSTGRES_USER | Yes | bookorbit | Database username |
POSTGRES_PASSWORD | Yes | - | Database password |
POSTGRES_DB | Yes | bookorbit | Database name |
JWT_SECRET | Yes | - | Secret for signing login tokens |
SETUP_BOOTSTRAP_TOKEN | Yes | - | One-time token for initial setup |
APP_PORT | No | 3000 | Host port mapped to the container |
PORT | No | 3000 | Internal container port. Only change if APP_PORT and PORT need to differ |
BOOKS_HOST_PATH | No | ./books | Host folder mounted as /books in the container |
PUID | No | 1000 | UID for files written by the app (useful on NAS) |
PGID | No | 1000 | GID for files written by the app (useful on NAS) |
BOOKORBIT_FIX_PERMISSIONS | No | true | Set to false if your platform manages /data ownership externally |
NODE_MAX_OLD_SPACE_SIZE | No | 2048 in .env | Node.js heap limit in MB. Use auto for container-aware detection or raise it for very large libraries |
LIBRARY_BROWSE_ROOT | No | / | Root shown by the library folder picker. Set to /books to hide other container folders |
BOOK_DOCK_PATH | No | /data/book-dock | Book Dock staging folder inside the container |
DATABASE_URL | No | Built from bundled Postgres settings | Full PostgreSQL connection string for an external database |
POSTGRES_HOST | No | postgres | Database hostname. Override when using an external DB |
POSTGRES_PORT | No | 5432 | Database port |
CLIENT_URL | No | Same as APP_URL | Frontend URL when the client is served from a different domain |
EMAIL_ENCRYPTION_KEY | No | - | Encrypts stored SMTP credentials at rest. Recommended if you configure email |
MIGRATION_ENCRYPTION_KEY | No | - | Encrypts stored migration source credentials. Recommended if you use migrations |
LOG_LEVEL | No | info | Log verbosity. Set to debug for detailed output |
OIDC_ALLOW_LOCAL_ISSUERS | No | false | Allows OIDC discovery on private addresses. Enable only on a trusted network |
CSP_ALLOW_CLOUDFLARE_INSIGHTS | No | false | Allows the Cloudflare Web Analytics beacon in the content security policy |
Reverse proxy
Section titled “Reverse proxy”If you’re using Nginx, Caddy, or Traefik, point it at localhost:3000 and set APP_URL to your public domain with HTTPS.
Caddy example:
books.example.com { reverse_proxy localhost:3000}SMTP and OIDC
Section titled “SMTP and OIDC”Both are configured inside the app - no environment variables needed. Log in as an administrator and go to Settings > Email or Settings > OIDC / SSO.
Updating
Section titled “Updating”docker compose pulldocker compose up -dThe app handles database migrations automatically on startup.
Moving from PostgreSQL 16 to 18
Section titled “Moving from PostgreSQL 16 to 18”The bundled database image moved from pgvector/pgvector:pg16 to pgvector/pgvector:pg18 in BookOrbit v2.2.0. This only affects installs created before that release. If you download a fresh docker-compose.yml, the PostgreSQL 18 container will refuse to start on a data directory that PostgreSQL 16 created, and the container is reported as unhealthy:
FATAL: database files are incompatible with serverDETAIL: The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 18.xPostgreSQL cannot open a data directory written by an older major version, so moving to 18 means dumping the database and restoring it into a fresh one.
1. Stop the stack and back up the current data directory.
docker compose downcp -a data/postgres data/postgres.pg16.bak2. Dump the database from a temporary PostgreSQL 16 container.
set -a && . ./.env && set +a
docker run --rm -d --name bookorbit-pg16 \ -v "$PWD/data/postgres:/var/lib/postgresql/data" \ -e PGDATA=/var/lib/postgresql/data/pgdata \ -e POSTGRES_USER="$POSTGRES_USER" \ -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \ -e POSTGRES_DB="$POSTGRES_DB" \ pgvector/pgvector:pg16
docker exec bookorbit-pg16 pg_dumpall -U "$POSTGRES_USER" > bookorbit-pg16.sqldocker stop bookorbit-pg163. Start PostgreSQL 18 on an empty data directory.
rm -rf data/postgresmkdir -p data/postgresdocker compose up -d postgres4. Restore the dump, then start the rest of the stack.
docker exec -i bookorbit-db psql -U "$POSTGRES_USER" -d postgres < bookorbit-pg16.sqldocker compose up -drole "bookorbit" already exists and database "bookorbit" already exists errors during the restore are expected, because the container creates POSTGRES_USER and POSTGRES_DB before the dump runs. The rest of the restore proceeds normally.
Once BookOrbit is up and your library looks correct, delete data/postgres.pg16.bak and bookorbit-pg16.sql.