BookLore
Deploy BookLore on Kubernetes using the official ghcr.io/booklore-app/booklore container image.
A self-hosted eBook library management application with metadata fetching, OPDS support,
and a modern web-based reader for managing your personal digital book collection.
BookLore stores relational state in MariaDB and uses separate filesystem domains: /app/data for application data,
settings, and caches; /books for the physical library; and optional /bookdrop for imports. A recoverable snapshot
must keep MariaDB and these volumes consistent.
Chart References
Key Features
- MariaDB bundled – HelmForge MariaDB subchart handles the database automatically
- External database – connect to an existing MariaDB/MySQL instance instead
- Triple PVC architecture –
/app/data(metadata),/books(library files),/bookdrop(imports) - OIDC guardrail – force-disable upstream OIDC while it is configured through the BookLore interface
- Remote/proxy auth – header-based authentication for reverse proxy setups
- NetworkPolicy – egress constrained to kube-system (DNS) and MariaDB only
- Gateway API – optional HTTPRoute support for clusters using Gateway API
- External Secrets – optional ExternalSecret items for secrets management
- HPA guardrails – validation prevents multi-replica scaling with ReadWriteOnce storage
Architecture
The Service targets the Deployment’s named http port. app.port configures
the actual BOOKLORE_PORT listener and all health probes; service.port
remains an independent Kubernetes-facing port. BookLore connects to the
bundled or external MariaDB service and mounts separate data, books, and
optional BookDrop volumes.
The official health endpoint is /api/v1/healthcheck. The default
Recreate strategy prevents overlapping mounts during upgrades that use
ReadWriteOnce storage.
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install booklore helmforge/booklore -f values.yaml
OCI registry:
helm install booklore oci://ghcr.io/helmforgedev/helm/booklore -f values.yaml
Deployment Examples
# values.yaml -- BookLore basic setup with bundled MariaDB
# MariaDB subchart handles the database automatically.
persistence:
data:
enabled: true
size: 5Gi
books:
enabled: true
size: 50Gi# values.yaml -- Production BookLore with TLS, tuning, and BookDrop
app:
rootLogLevel: WARN
logLevel: INFO
persistence:
data:
enabled: true
size: 10Gi
storageClass: longhorn
books:
enabled: true
size: 200Gi
storageClass: longhorn
bookdrop:
enabled: true
size: 10Gi
storageClass: longhorn
resources:
requests:
memory: 512Mi
cpu: 250m
limits:
memory: 1Gi
cpu: '1'
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: booklore.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: booklore-tls
hosts:
- booklore.example.com
mariadb:
standalone:
persistence:
enabled: true
size: 5Gi
storageClass: longhorn
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
cpu: 500m# values.yaml -- BookLore with external MariaDB/MySQL
mariadb:
enabled: false
database:
external:
host: mariadb.example.com
port: '3306'
name: booklore
username: booklore
existingSecret: booklore-db-credentials
existingSecretPasswordKey: password
persistence:
data:
enabled: true
size: 5Gi
books:
enabled: true
size: 100Gi# values.yaml -- BookLore with Ingress exposure
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: booklore.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: booklore-tls
hosts:
- booklore.example.com# values.yaml -- BookLore with Gateway API HTTPRoute
gatewayAPI:
enabled: true
httpRoutes:
- name: booklore
parentRefs:
- name: main-gateway
namespace: gateway-system
hostnames:
- booklore.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: booklore
port: 6060Configuration Reference
Application Settings
| Parameter | Description | Default |
|---|---|---|
app.port |
HTTP port inside the container | 6060 |
app.rootLogLevel |
Root log level | INFO |
app.logLevel |
BookLore-specific log level | INFO |
app.allowedOrigins |
CORS allowed origins | "*" |
app.diskType |
LOCAL or read-safe NETWORK mode |
LOCAL |
app.env |
Additional environment variables | [] |
app.envFrom |
Additional envFrom sources | [] |
Image Configuration
| Parameter | Description | Default |
|---|---|---|
image.repository |
Container image repository | ghcr.io/booklore-app/booklore |
image.tag |
Image tag (pinned) | v2.3.1 |
image.pullPolicy |
Image pull policy | IfNotPresent |
Persistence
| Parameter | Description | Default |
|---|---|---|
persistence.data.enabled |
Persist application data and metadata | true |
persistence.data.size |
Data PVC size | 10Gi |
persistence.data.storageClass |
Storage class for data PVC | "" |
persistence.data.accessModes |
Access modes for data PVC | ["ReadWriteOnce"] |
persistence.books.enabled |
Persist library books | true |
persistence.books.size |
Books PVC size | 50Gi |
persistence.books.storageClass |
Storage class for books PVC | "" |
persistence.books.accessModes |
Access modes for books PVC | ["ReadWriteOnce"] |
persistence.bookdrop.enabled |
Persist BookDrop import folder | false |
persistence.bookdrop.size |
BookDrop PVC size | 5Gi |
Database
| Parameter | Description | Default |
|---|---|---|
mariadb.enabled |
Use bundled MariaDB subchart | true |
mariadb.auth.database |
MariaDB database name | booklore |
mariadb.auth.username |
MariaDB user | booklore |
database.external.host |
External DB host (when mariadb.enabled=false) |
"" |
database.external.port |
External DB port | "3306" |
database.external.name |
External DB name | booklore |
database.external.existingSecret |
Existing Secret for external DB password | "" |
Authentication
| Parameter | Description | Default |
|---|---|---|
oidc.enabled |
Deprecated compatibility value; no runtime effect | false |
oidc.forceDisable |
Force-disable OIDC configured through BookLore | false |
remoteAuth.enabled |
Enable proxy-based authentication headers | false |
remoteAuth.createNewUsers |
Create users from remote auth headers | true |
remoteAuth.headerName |
Display name header | Remote-Name |
remoteAuth.headerUser |
Username header | Remote-User |
remoteAuth.headerEmail |
Email header | Remote-Email |
Networking
| Parameter | Description | Default |
|---|---|---|
service.type |
Service type | ClusterIP |
service.port |
Service port | 6060 |
ingress.enabled |
Enable Kubernetes Ingress | false |
ingress.ingressClassName |
Ingress class | traefik |
gatewayAPI.enabled |
Enable Gateway API HTTPRoutes | false |
gatewayAPI.httpRoutes |
HTTPRoute definitions (required when enabled) | [] |
networkPolicy.enabled |
Create NetworkPolicy | false |
networkPolicy.egress.enabled |
Restrict egress traffic | false |
networkPolicy.egress.allowDNS |
Allow DNS egress to kube-system | true |
networkPolicy.egress.databaseTo |
Database egress peers (required for external DB with egress) | [] |
Monitoring and Scaling
| Parameter | Description | Default |
|---|---|---|
monitoring.serviceMonitor.enabled |
Create ServiceMonitor for Prometheus | false |
monitoring.serviceMonitor.path |
Metrics endpoint path (required when enabled) | "" |
autoscaling.enabled |
Enable HPA | false |
autoscaling.minReplicas |
Minimum replicas | 1 |
autoscaling.maxReplicas |
Maximum replicas | 3 |
The current upstream image does not include a Prometheus metrics endpoint. Enable monitoring.serviceMonitor only if
you build a custom image with micrometer-registry-prometheus or when upstream adds native support. You must set
monitoring.serviceMonitor.path to a real Prometheus exposition endpoint.
Setting replicaCount > 1 or autoscaling with maxReplicas > 1 requires every enabled persistence volume to
declare ReadWriteMany. The chart rejects unsafe data, books, and BookDrop combinations before installation.
When HPA is enabled, the Deployment omits spec.replicas so Helm upgrades do not reset the replica count. The
first upgrade from an older release can reduce the Deployment to one replica before the HPA reconciles. A fresh
installation with minReplicas > 1 can also start at one replica until that first reconciliation. Plan for reduced
capacity or redundancy during this transition.
Security and production boundaries
- Service-account token mounting is disabled by default.
- Database credentials can come from an existing Secret or ExternalSecret; inline passwords remain visible in Helm release state.
- Restrict
app.allowedOriginsto the public HTTPS origin and allow required metadata-provider egress when enabling NetworkPolicy restrictions. app.diskType=NETWORKdisables file writes and reorganization for network-attached book storage; it does not make every network filesystem safe or supported.- The stock pinned image has no Prometheus endpoint, so ServiceMonitor remains disabled unless an operator supplies a compatible image and path.
Troubleshooting
- For slow startup, inspect Flyway and MariaDB logs before restarting the pod.
- For probe failures, verify
BOOKLORE_PORTmatchesapp.portand request/api/v1/healthcheckinside the container. - For missing books, verify the
/booksPVC andapp.diskTypecontract. - For database failures, verify the JDBC endpoint, username, and Secret key refer to the same MariaDB instance.