Deployment guide

BookLore

Overview

BookLore is an open-source, self-hosted digital library for people and teams with a serious book collection. Point it at your EPUB, PDF and comic files and it organizes them into libraries and smart shelves and fetches cover art and rich metadata. Every member of your household or organization gets their own account, shelves and reading progress.

The SecureAnyCloud image installs BookLore 2.3.1 natively and assembles the whole stack for you. BookLore is a single Spring Boot application with the Angular web console compiled into the same artifact, so one tuned Java service serves both the REST API and the browser interface on 127.0.0.1:6060. nginx sits in front on port 80, and MariaDB on the loopback interface holds library, user and reading-progress data.

What you get:

  • BookLore 2.3.1 built from the pinned upstream release tag (/opt/booklore/src)
  • booklore.service: the Spring Boot application and embedded Angular console on 127.0.0.1:6060
  • MariaDB 10.11 for library, user and reading-progress data, bound to the loopback interface
  • nginx.service reverse proxy on TCP 80, tuned for BookLore's large headers, streaming uploads and progress websockets, and ready for TLS
  • booklore-firstboot.service, which generates every per-instance secret and claims the administrator on first boot
  • A dedicated 64 GiB data volume mounted at /var/lib/booklore for books, covers and the auto-import folder. The library grows on its own disk and can be expanded or snapshotted independently of the operating system.
  • A built-in browser reader for EPUB, PDF and comic formats, with bookmarks and per-user position tracking
  • An OPDS catalog plus Kobo and KOReader sync, so you can carry the same library onto a dedicated e-reader
  • No default account. BookLore creates its first administrator through a one-time setup call that the API accepts only while no users exist. On the first boot of every instance, a one-shot service generates a unique database password and administrator password and claims the administrator through that same endpoint before nginx ever serves port 80. It also clears the inherited token signing key so the instance signs with a key of its own. Finally, it proves that the new credentials authenticate, that a forged token is rejected and that the setup endpoint is closed.
  • Temurin JDK 25 runtime, with the build-only Node.js toolchain removed from the shipped image
  • The complete AGPL-3.0 corresponding source, shipped on the instance
  • Ubuntu 24.04 LTS base, latest patches, unattended security upgrades enabled
  • 24/7 SecureAnyCloud support, 24h response SLA

Prerequisites

You need an active Azure subscription, an SSH key, and a VNet with a subnet.

The recommended VM size is Standard_B2s (2 vCPU, 4 GB RAM). The whole stack is memory-tuned to run comfortably in 4 GB with no swap at all. The Java heap is pinned rather than left to claim a percentage of RAM, and MariaDB's buffers are capped to match. Move to a larger size if you plan to import very large collections or serve many concurrent readers.

1

Deploy from the Azure Portal

Search the Azure Marketplace for BookLore, choose the plan, and create the VM.

In the networking step, attach a network security group (NSG) that allows inbound TCP 22 (SSH) and TCP 80 (the web console) from your client networks only. Put a TLS reverse proxy in front of port 80 for production.

No other port needs to be open. MariaDB and the application's own port 6060 both listen on the loopback interface, so nginx is the only ingress.

2

Deploy from the Azure CLI

RG="booklore-prod"; LOCATION="eastus"; VM_NAME="booklore-01" IMAGE_URN="sac1647283583153:booklore-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name bl-vnet --address-prefix 10.113.0.0/16 --subnet-name bl-subnet --subnet-prefix 10.113.1.0/24 az network nsg create -g "$RG" --name bl-nsg az network nsg rule create -g "$RG" --nsg-name bl-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name bl-nsg --name allow-web --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:booklore-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name bl-vnet --subnet bl-subnet --nsg bl-nsg --public-ip-sku Standard

The library data disk is part of the image, so it is created and mounted for you automatically. You do not need to attach an extra disk.

3

Connect via SSH

ssh azureuser@<vm-ip>

All services start automatically on first boot, after booklore-firstboot.service has generated this instance's secrets and claimed the administrator. First boot typically completes within a minute of the VM reporting ready.

4

Verify the Services

sudo systemctl is-active mariadb booklore nginx
sudo test -f /var/lib/sac/data/booklore-firstboot.done && echo FIRSTBOOT_DONE
sudo ss -tlnH | awk '{print $1, $4}' | sort -u

Expected output: all three units are active and first boot has completed. Only SSH and nginx are on a public address, while the application and the database stay on loopback:

active active active FIRSTBOOT_DONE LISTEN 0.0.0.0:22 LISTEN 0.0.0.0:80 LISTEN 127.0.0.1:3306 LISTEN 127.0.0.53%lo:53 LISTEN 127.0.0.54:53 LISTEN [::]:22 LISTEN [::]:80 LISTEN [::ffff:127.0.0.1]:6060

Only SSH and nginx are on a public address. MariaDB is on 127.0.0.1:3306, and the two port 53 entries are the local systemd-resolved DNS stub.

The application itself is on [::ffff:127.0.0.1]:6060. This is the IPv4-mapped form of 127.0.0.1, so port 6060 is reachable only from the VM itself and nginx is the sole ingress.

5

Retrieve Your Administrator Credentials

Every instance generates its own administrator password and database password on first boot and writes them to a root-only file:

sudo ls -l /root/booklore-credentials.txt
sudo cat /root/booklore-credentials.txt

The file is mode 600, owned by root. It contains the console URL, the administrator username and password, and the database credentials.

Sign in with those values. Then change the administrator email address and password under Settings > Users.

6

Confirm There Is No Default Login

BookLore creates its first administrator through a one-time setup endpoint that the API accepts only while no users exist. This image claims that administrator during first boot, before nginx starts, so nobody can reach your instance and claim it first. Confirm that the setup endpoint is closed:

curl -s http://127.0.0.1/api/v1/setup/status

Expected output (setup has already been completed on this instance):

{"data":true,"message":"Initial setup has already been completed.","status":200,"timestamp":"..."}

A protected endpoint must refuse an anonymous caller and a forged token:

curl -s -o /dev/null -w 'anonymous: %{http_code}\n' http://127.0.0.1/api/v1/users/me
curl -s -o /dev/null -w 'forged token: %{http_code}\n' http://127.0.0.1/api/v1/users/me \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJub2JvZHkifQ.not-a-valid-signature'

Expected output:

anonymous: 403 forged token: 401
7

Check the Application and the Library Volume

curl -s http://127.0.0.1/api/v1/healthcheck df -h /var/lib/booklore free -m swapon --show

The health endpoint reports the running version, and the dedicated 64 GiB library volume is mounted. The whole stack sits comfortably inside 4 GB. swapon --show prints nothing because the image ships with no swap at all.

8

Sign in to the Console

Browse to http://<vm-ip>/ and sign in with the administrator username and password from the credentials file.

9

Add Your Books

Create a library from the Libraries section of the sidebar and point it at a folder on the data volume, for example /var/lib/booklore/books. Copy your EPUB, PDF or CBZ files there. BookLore scans them, extracts titles, authors and covers, and shelves them.

sudo install -d -o booklore -g booklore -m 0750 /var/lib/booklore/books ls -ld /var/lib/booklore/books /var/lib/booklore/bookdrop

Then copy your own files in and hand them to the booklore user. Replace the source path with your own:

sudo cp ~/my-collection/*.epub /var/lib/booklore/books/
sudo chown -R booklore:booklore /var/lib/booklore/books

You can also drop files into the watched auto-import folder at /var/lib/booklore/bookdrop, or upload them from the browser with the upload button in the toolbar.

The All Books view shows the shelved collection with covers. Use the filter panel to narrow by author, format, read status, publisher or year.

10

Read in the Browser

Click any book to open it in the built-in reader. EPUB, PDF and comic formats all render in the browser, with per-user reading position, bookmarks and a progress indicator. You can stop on one device and resume on another.

11

Settings, Users and Sync

Settings exposes reader preferences per format, metadata provider configuration, user administration, email delivery, OIDC single sign-on and the task manager.

Create an account for each reader under Settings > Users and give them only the permissions they need. Each user keeps their own shelves and reading progress.

BookLore also serves an OPDS catalog and supports Kobo and KOReader sync, so the same library is reachable from a dedicated e-reader. Metadata fetching from providers such as Amazon, GoodReads, Google and Open Library requires outbound internet access from the VM. It is optional and disabled until you configure it.

12

Server Components

Component Detail

Application booklore.service, Spring Boot on Temurin JDK 25, 127.0.0.1:6060

Web console Angular 21, compiled into the same artifact and served by the application

Database MariaDB 10.11, 127.0.0.1:3306, database and role booklore

Reverse proxy nginx.service on TCP 80

Library volume 64 GiB disk mounted at /var/lib/booklore (books/, data/, bookdrop/)

Application jar /opt/booklore/app.jar

Environment file /etc/booklore/booklore.env (mode 640, generated at first boot)

Credentials /root/booklore-credentials.txt (mode 600)

Corresponding source /opt/booklore/src and /usr/share/booklore/source/

The Java heap is pinned at 1 GB and MariaDB's buffer pool at 192 MB, so the two never contend on a 2 vCPU instance.

If you move to a larger VM and want to raise the heap, edit ExecStart in /etc/systemd/system/booklore.service. Then run sudo systemctl daemon-reload && sudo systemctl restart booklore.

13

The AGPL-3.0 Source Offer

BookLore is licensed under the GNU Affero General Public License version 3. Section 13 requires that anyone interacting with the program over a network is offered the corresponding source of the running version. This instance satisfies that on disk, with no network access required:

cat /usr/share/doc/booklore/SOURCE-OFFER.txt cd /usr/share/booklore/source && sha256sum -c booklore-2.3.1-source.tar.gz.sha256

The complete unmodified v2.3.1 source tree that produced the running jar is at /opt/booklore/src, a pristine release tarball is at /usr/share/booklore/source/, the license text is at /usr/share/doc/booklore/LICENSE, and the written offer names all of them.

SecureAnyCloud makes no modifications to BookLore itself. This image supplies only configuration, systemd units, an nginx vhost and the first-boot credential bootstrap.

One packaging decision is worth noting. The upstream container vendors a prebuilt unrar binary whose license forbids reuse of its code, so this image deliberately omits it. BookLore detects its absence and falls back to its bundled pure-Java RAR reader, so EPUB, PDF and CBZ are unaffected. If you need RAR5-era CBR files, install a RAR reader yourself and point BOOKLORE_UNRAR_BIN at it.

14

Configuration and Logs

A quick health check of the whole stack:

sudo systemctl is-active mariadb booklore nginx
sudo nginx -t

For deeper investigation, read the service journals. They print a lot of output, so run them on their own:

sudo systemctl status booklore
sudo journalctl -u booklore -n 100 --no-pager sudo journalctl -u booklore-firstboot --no-pager

The application reads its environment from /etc/booklore/booklore.env. After editing it, restart the service with sudo systemctl restart booklore.

Database maintenance reads this instance's password straight from the credentials file, so you have nothing to type or paste:

sudo mariadb -u booklore \
  -p"$(sudo sed -n 's/^booklore\.db\.pass=//p' /root/booklore-credentials.txt)" \
  booklore -e "SELECT COUNT(*) AS books FROM book;"

To back up the library, snapshot the data volume and dump the database the same way:

sudo mariadb-dump -u booklore \
  -p"$(sudo sed -n 's/^booklore\.db\.pass=//p' /root/booklore-credentials.txt)" \
  booklore > ~/booklore-backup.sql

Troubleshooting

The console does not load. Check that booklore.service is active and that first boot completed: sudo systemctl is-active booklore && sudo journalctl -u booklore-firstboot --no-pager. The application waits for MariaDB and applies its schema migrations on first start, which takes around thirty seconds on a Standard_B2s.

A sign-in attempt returns "A data conflict occurred". BookLore issues a refresh token whose validity timestamps have one-second resolution, and stores it under a uniqueness constraint. Two sign-ins for the same account inside the same second therefore collide. Wait a second and sign in again.

Sign-in returns "Too many failed login attempts". BookLore rate-limits five failed attempts per source address and per username within fifteen minutes. Wait for the window to pass, or retrieve the current password from /root/booklore-credentials.txt.

Uploads or large books fail. The nginx vhost allows request bodies up to 4 GB and streams them straight through. If you front the instance with another proxy or a load balancer, raise the equivalent limits there too.

Metadata lookups return nothing. Metadata providers are third-party services reached over the internet. Confirm that the VM has outbound access and that the provider is enabled under Settings > Metadata.

Support

24/7 technical support is included with this image. Contact sales@secureanycloud.com for help with deployment, library and shelf organization, metadata providers, OPDS, Kobo and KOReader sync, bulk imports, user administration, TLS and custom domains, version upgrades, performance tuning and MariaDB administration.

Deploy on Azure

Launch BookLore on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure