Installation
Trove runs either in Docker with PostgreSQL, or natively on an Ubuntu or Debian server with systemd. Both use port 6060.
Docker Compose
You need Docker (v20.10+) and Docker Compose (v2.0+). Allow 2 GB of RAM, 4 GB or more for large libraries.
Step 1: Create the folders
mkdir -p ~/trove/{data,books,bookdrop,postgres}
cd ~/trove
| Folder | Purpose |
|---|---|
postgres | Database files. Back this up. |
data | Covers, thumbnails, uploaded fonts and icons, and other files Trove creates |
books | Your book library |
bookdrop | Drop books here to import them (see Bookdrop) |
Step 2: Create .env
# Application
APP_USER_ID=1000
APP_GROUP_ID=1000
TZ=Etc/UTC
# Database
DATABASE_URL=jdbc:postgresql://postgres:5432/trove
DB_USER=trove
DB_PASSWORD=ChangeMe_TroveApp_2026!
POSTGRES_DB=trove
# Storage: LOCAL (default), or NETWORK to stop Trove ever modifying book files
DISK_TYPE=LOCAL
Change the password before you start. Set APP_USER_ID and APP_GROUP_ID to the owner of your folders (id -u, id -g).
Step 3: Create docker-compose.yml
services:
trove:
image: ghcr.io/x2-consult/trove:latest
container_name: trove
environment:
- USER_ID=${APP_USER_ID}
- GROUP_ID=${APP_GROUP_ID}
- TZ=${TZ}
- DATABASE_URL=${DATABASE_URL}
- DATABASE_USERNAME=${DB_USER}
- DATABASE_PASSWORD=${DB_PASSWORD}
- DISK_TYPE=${DISK_TYPE}
depends_on:
postgres:
condition: service_healthy
ports:
- "6060:6060"
volumes:
- ./data:/app/data
- ./books:/books
- ./bookdrop:/bookdrop
restart: unless-stopped
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
- TZ=${TZ}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: [ "CMD", "pg_isready", "-U", "${DB_USER}", "-d", "${POSTGRES_DB}" ]
interval: 5s
timeout: 5s
retries: 10
For production, pin a release tag instead of latest, for example ghcr.io/x2-consult/trove:v1.2.3.
Podman Quadlet and Helm examples are in the example-podman and example-chart folders of the repository.
Step 4: Start it
docker compose up -d
docker compose logs -f trove
Open http://localhost:6060, or http://YOUR_SERVER_IP:6060 from another device, and create your admin account
(see Admin account).
Native install
On an Ubuntu or Debian server with systemd:
git clone -b master https://github.com/X2-Consult/trove.git
cd trove
./install.sh
master is the released version; develop has the latest changes before they're released.
The installer asks whether this is a production or development machine, then sets up Java 25, PostgreSQL, a
trove database and user, and the trove systemd service. It can also configure Caddy or nginx with a
Let's Encrypt certificate if you give it a domain name.
| What | Where |
|---|---|
| App | /opt/trove |
Data: config (covers and other files Trove creates), library and bookdrop | /srv/trove |
| Settings file (database password, allowed origins, version) | /etc/trove/trove.env |
| Service | systemctl status trove, logs with journalctl -u trove -f |
To update, run ./deploy.sh in /opt/trove, or use Update now in the app (admins only). Trove keeps running while the update builds, and is only unavailable while it restarts at the end, usually under a minute. Database changes are applied as it starts.
Books on a network share
Your book folders can be on a NAS. Mount the share on the host first (NFS or SMB/CIFS), then point Trove at it: in Docker, mount it into the container as a volume like ./books above; natively, add the mounted folder when you create the library. Trove recognises network filesystems and adjusts how it watches and writes to them. See Network storage for the details and the DISK_TYPE and NETWORK_POLL_SECONDS settings.
Moving from BookLore
- Native installs: run
scripts/migrate-to-trove.sh --dry-runto see what will change, thenscripts/migrate-to-trove.sh. It keeps your library, users and reading progress, and can roll back. - Docker: change the image to
ghcr.io/x2-consult/troveand keep your volumes and database settings. If your compose file never setDATABASE_URLorDATABASE_USERNAME, addDATABASE_NAME=bookloreandDATABASE_USERNAME=booklore, because the defaults are nowtrove. - Kobo and KOReader devices keep syncing without re-pairing, and
BOOKLORE_*environment variables still work.
Troubleshooting
Trove won't start
docker compose logs trove postgres # Docker
journalctl -u trove -n 100 # native
Common causes: a wrong volume path, port 6060 already in use, a database password that doesn't match, or folder permissions.
Database connection errors
Check PostgreSQL is healthy with docker compose ps postgres, and that DATABASE_USERNAME and
DATABASE_PASSWORD match POSTGRES_USER and POSTGRES_PASSWORD.
Permission errors
Set APP_USER_ID/APP_GROUP_ID to the folders' owner, or fix the ownership:
sudo chown -R $USER:$USER ~/trove/data ~/trove/books ~/trove/bookdrop
Port already in use
Find what's using it with sudo lsof -i :6060, or map another host port, for example "8080:6060".
Natively, set TROVE_PORT in /etc/trove/trove.env.
Can't reach it from another device
sudo ufw allow 6060/tcp # UFW
sudo firewall-cmd --permanent --add-port=6060/tcp && sudo firewall-cmd --reload # firewalld
The site refuses connections, but Trove is running
If curl http://localhost:6060/api/v1/healthcheck on the server answers but browsers get "connection refused" (a Trove tab may show a 504), something in front of Trove is blocking you. A common cause is fail2ban with a rule that bans addresses after many "not found" responses: everything in your home shares one public address, so a Kobo sync or a page of missing covers can ban the whole house. See Kobo troubleshooting for how to exempt your network.
Next steps
- Admin account: create the administrator account
- Your first library: point Trove at your books
- Bookdrop: import books by dropping them in a folder
- Metadata settings: choose where covers and book details come from