Deployment
Lumastra uses PostgreSQL 18 + pgvector, Valkey 9, and the same app image for web and background-worker roles. Choose a deployment method:
| Method | Best for | Guide |
|---|---|---|
| Docker Compose | A single host: home server, NAS, one VM | Deploy with Docker |
| Kubernetes (Helm chart, beta) | Multi-replica / high availability. Best-effort support until 1.0 | Deploy on Kubernetes |
Docker images
Section titled “Docker images”Images are published to GHCR:
ghcr.io/lumastra/lumastra:stable/:latest: the latest release (intended production tag)ghcr.io/lumastra/lumastra:0.1.0: a specific releaseghcr.io/lumastra/lumastra:edge: every commit tomain, amd64 only (testing)
A deployment runs separate web and worker containers, PostgreSQL 18 +
pgvector, and two Valkey 9 instances: persistent noeviction task/shared
coordination and disposable evictable cache. Exactly one lean TypeScript worker
runs the existing tasks/plugins and supervises one Rust watcher child in the same
container. An optional separate stream proxy handles Live TV. Keep storage
services on a private network.
Reverse proxy
Section titled “Reverse proxy”Terminate TLS at Caddy, Traefik, or nginx and forward to port 3000. ORIGIN is
required for secure login cookies. Forward auth additionally requires FORWARD_AUTH_ENABLED=true,
identity headers and TRUSTED_PROXY_IPS; see Authentication.
The Reverse proxy guide has copy-paste setups for Caddy, nginx, Traefik and Nginx Proxy Manager. The Docker proxy section covers compression, credential-safe logs, and upload limits for all deployment methods.
Scaling to multiple replicas
Section titled “Scaling to multiple replicas”For two or more web replicas, use the following topology:
Run N web replicas + exactly one worker, sharing PostgreSQL and both Valkeys:
- Web replicas (
WORKER_MODE=disabled) serve HTTP/API/SSE. Scale these freely. - Worker replica (
start-worker.sh) runs the lean TypeScript task host, existing task/plugin execution, scheduler and supervised Rust watcher child. Run exactly one: it’s the single source of background job orchestration, and it scans the mounted media library. Web replicas and the worker need the same media mounted at the same path, read-only: the worker to scan, the web replicas for direct play and transcoding. Running more than one worker is not supported. If background processing is your bottleneck, give the single worker more CPU/RAM and tune its concurrency. Don’t add worker replicas. - Requests for one playback session must reach the same web replica. Per-session HLS transcode state is pinned to the replica that started it. The Kubernetes Helm chart ships a router that does this. Elsewhere, use cookie affinity or source-IP hash on your load balancer, never round-robin.
- Each replica needs a unique
INSTANCE_IDfor Valkey coordination, leader election, and the admin cluster view. Leave it unset and Lumastra derives a unique one from the hostname; if you set it explicitly, it must differ per replica. - The
configvolume is shared between web and worker replicas (artwork, trickplay tiles). Mount the media library, read-only and at the same path, on the worker and on every web replica. - Database migrations are serialized by a PostgreSQL advisory lock at startup.
See Deploy on Kubernetes for the Helm chart, the per-pod requirements, and the Valkey eviction-policy split.
Production checklist
Section titled “Production checklist”NODE_ENV=production, andORIGIN=https://your-domainset so secure session cookies work behind your TLS proxyLUMASTRA_MASTER_KEYset to a ≥ 32-byte random value and backed up separately.JWT_SECRETis optional: without it, the signing secret is derived from the master key. Set it only if you want to rotate it on its own, and then use a ≥ 32-byte random value- Sign-in configured: a first admin via the
/setupwizard (built-in login); for forward auth,FORWARD_AUTH_ENABLED=trueandTRUSTED_PROXY_IPSset and admin granted via anADMIN_GROUPSgroup in your IdP; or for OIDC,AUTH_OIDC_*set and the callback URI registered - Postgres and Valkey on a private network; regular backups (the database and your master key)
- Valkey eviction policy chosen so task-system keys aren’t evicted (see the Kubernetes guide)
- Resource limits set (the worker especially: 2–4 GB RAM under load)
- Logs aggregated from container stdout
- Compression enabled at the reverse proxy for HTML, CSS, JavaScript and JSON (including
/api/v1), but not fortext/event-streamor media. See Compression - Proxy access logs record paths without query strings. Third-party clients pass tokens and Subsonic passwords as query parameters. See Access logs
- The proxy’s request-body limit is at least your
BODY_SIZE_LIMIT. nginx’s 1 MB default rejects artwork uploads, library upload chunks, and app diagnostics bundles. Behindauth_request, the auth location needs the same limit. See Request body size - Running more than one web replica? Session affinity (the Helm chart’s router, or
sticky sessions on your load balancer) and a unique
INSTANCE_IDper replica. See Scaling to multiple replicas above