Skip to content
Lumaft documentation contents
Lumaft documentation

Set up on VMware

Run Lumaft on a vSphere virtual machine for on-premises and private-cloud environments.

Set up on VMware

This guide runs Lumaft as a container on one vSphere virtual machine. It covers the two things that differ most from a public cloud: where the database lives and how traffic gets in and out.

It applies equally to air-gapped sites running Enterprise Advanced Security; those differences are called out where they occur.

Architecture

   Corporate network ──443──▶ ┌──────────────────────────────────────────┐
   (or existing LB)           │  vSphere VM (Ubuntu LTS / RHEL)          │
                              │  ┌──────────┐    ┌────────────────────┐  │
                              │  │ reverse  │──▶ │ lumaft             │  │──▶ S3 / S3-compatible
                              │  │ proxy    │    │ 127.0.0.1:8080     │  │──▶ private registry
                              │  │ :443     │    │ /data ← data VMDK  │  │──▶ licensing (unless offline)
                              │  └──────────┘    └────────────────────┘  │
                              └──────────────────────────────────────────┘

Before you begin

  • A vSphere VM template with a supported Linux distribution and a container runtime (Docker Engine or Podman). Two vCPU and 4 GiB of memory leave room for the proxy beside Lumaft's 1 vCPU / 2 GiB baseline.
  • A DNS name for Lumaft and a certificate your browsers trust — from your internal CA or a public one.
  • Network reachability from the VM to the object store that holds your state.
  • The verified image digest, mirrored into a registry the VM can reach if the VM has no internet access. See Deployment and setup.
  • Your backend details. See Find your backend details.

Persistent storage

SQLite runs in WAL mode and needs an exclusive, durable, block-backed volume presented to the guest as local storage. On vSphere that means a dedicated virtual disk, not a network share inside the guest.

Storage shape Supported for /data
A second VMDK on a VMFS, vSAN, or NFS datastore Yes
An RDM (raw device mapping) Yes
An NFS or SMB share mounted inside the guest No
A disk shared between two VMs (multi-writer) No
The VM's root disk Works, but keep data separate

The datastore backing the VMDK may itself be NFS; what matters is that the guest sees a block device. WAL's locking requirements apply to the guest filesystem, and a VMDK gives the guest ext4 or XFS on a local block device.

Step 1: Add the data disk

  1. Edit the VM settings and add a new hard disk. Size it for your retention window; 50 GiB is a comfortable starting point.
  2. Place it on a datastore with the durability and snapshot policy you want for the database.
  3. Set the disk to Independent – Persistent if you take VM snapshots for the OS and do not want the database included in them. Database backups are taken cold, not by snapshot.
  4. In the guest, format and mount it:
sudo mkfs.xfs -L lumaft-data /dev/sdb
sudo mkdir -p /srv/lumaft/data
echo 'LABEL=lumaft-data /srv/lumaft/data xfs defaults,nofail 0 2' | sudo tee -a /etc/fstab
sudo mount -a
sudo chown 1000:1000 /srv/lumaft/data
sudo chmod 700 /srv/lumaft/data

Never attach the same VMDK to a second VM. Lumaft holds an ownership lock on lumaft.db.owner; a second replica pointed at the same database refuses to start with ownership-conflict, and a multi-writer disk would bypass the protection the lock provides.

PostgreSQL instead of SQLite

If you already operate PostgreSQL on-premises and hold a Business license, Lumaft can use it on one replica; Enterprise adds coordinated replicas. The VM then needs no data disk. See Database integration.

Networking

Inbound

Lumaft binds to the loopback interface. Terminate TLS in one of two places:

  • On the VM. Caddy or nginx on :443 forwarding to 127.0.0.1:8080. Simplest when the VM is directly addressable.
  • On an existing load balancer (NSX Advanced Load Balancer, F5, HAProxy). Publish the container on the VM's private address (--publish 10.10.20.5:8080:8080) and restrict the guest firewall so 8080 admits only the load balancer. The load balancer holds the certificate and forwards HTTP to 8080. Health check: GET /api/v1/readiness, expect 200.

Either way, browsers connect only over HTTPS. Leave LUMAFT_ALLOW_HTTP unset.

Guest firewall (for example ufw or firewalld):

Direction Port Source Purpose
Inbound 443 Client ranges or the LB Console and runner traffic
Inbound 8080 Load balancer only (if used) Forwarded traffic
Inbound 22 Management network only Administration

Outbound

Lumaft makes three kinds of outbound HTTPS calls:

Destination When Air-gapped equivalent
The object store Every observation cycle An on-premises S3-compatible store
The container registry At pull time A private registry mirror
The licensing service Connected Business or Enterprise renewals An offline-site license file (Enterprise Advanced Security)

Allow those destinations at your egress firewall. Lumaft reads the object store directly over HTTPS; if the store uses a certificate from your internal CA, mount the CA bundle and set NODE_EXTRA_CA_CERTS:

--mount type=bind,source=/srv/lumaft/run/ca-bundle.pem,target=/run/lumaft/ca-bundle.pem,readonly \
--env NODE_EXTRA_CA_CERTS=/run/lumaft/ca-bundle.pem

Do not disable certificate verification.

Backend credentials

An on-premises VM has no cloud workload identity. Options, in order of preference:

  • S3-compatible store with its own access keys. Create a read-only key pair scoped to the state namespace and pass it as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY through an --env-file with mode 0600.
  • Static IAM user keys, scoped to the read-only policy in Connect a backend, rotated on a schedule.

Step 2: Secret files

sudo mkdir -p /srv/lumaft/run && sudo chmod 700 /srv/lumaft/run
sudo sh -c 'umask 077; printf "%s\n" "$(openssl rand -base64 24)" > /srv/lumaft/run/admin-password'
sudo sh -c 'umask 077; cat > /srv/lumaft/run/backends.json' <<'JSON'
{
  "schemaVersion": 1,
  "backends": [
    {
      "kind": "s3",
      "id": "private-cloud",
      "displayName": "Private cloud infrastructure",
      "bucket": "pulumi-state",
      "layout": "project-scoped",
      "region": "local",
      "endpoint": "https://s3.internal.example",
      "forcePathStyle": true,
      "enabled": true
    }
  ]
}
JSON
sudo chown -R 1000:1000 /srv/lumaft/run
sudo chmod 600 /srv/lumaft/run/*

endpoint must be an HTTPS origin with no path, query, or credentials. forcePathStyle: true is typical for S3-compatible stores that do not serve virtual-hosted bucket names.

Step 3: Run Lumaft

sudo docker run --name lumaft \
  --detach \
  --restart unless-stopped \
  --init \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --publish 127.0.0.1:8080:8080 \
  --mount type=bind,source=/srv/lumaft/data,target=/data \
  --mount type=bind,source=/srv/lumaft/run,target=/run/lumaft,readonly \
  --env-file /srv/lumaft/s3-credentials.env \
  --env LUMAFT_LOCAL_ADMIN_PASSWORD_FILE=/run/lumaft/admin-password \
  --env LUMAFT_BACKENDS_FILE=/run/lumaft/backends.json \
  --env LUMAFT_PUBLIC_ORIGIN=https://lumaft.internal.example \
  "registry.internal.example/dekglas/lumaft@sha256:<digest>"

curl -fsS http://127.0.0.1:8080/api/v1/readiness

Put your reverse proxy in front (the EC2 guide's containerized Caddy works unchanged here, or use your existing load balancer), sign in, and confirm the backend diagnostic under Administration → Backends. Then remove the password environment variable and securely delete the password file. Docker Compose and systemd turns this command into a unit that starts on boot.

ScreenshotAdministration → Backends page showing an S3-compatible backend with a custom endpoint, path-style access enabled, and a successful diagnostic

Air-gapped sites

Enterprise Advanced Security runs the same image with an organization-wide offline license and no outbound licensing calls.

  1. On a connected machine, verify the release signature, pull the image by digest, and transfer it with docker save / docker load or by mirroring to the private registry. The digest is the artifact identity; carry it with the image.

  2. Obtain the signed offline-site license document from Dekglas and mount it as a protected file, mode 0600, owned by UID 1000:

    --mount type=bind,source=/srv/lumaft/run/site-license,target=/run/lumaft/site-license,readonly \
    --env LUMAFT_OFFLINE_SITE_LICENSE_FILE=/run/lumaft/site-license
    
  3. Open Administration → Licensing to confirm the edition, grants, and expiry the license resolved to.

The license has its own signed expiry and grace period. Renewal is a file replacement and a restart.

Operate on vSphere

Backups

Backups are cold: stop the container, copy lumaft.db, start the container. Copy the file to backup storage that is independent of the datastore.

sudo docker stop -t 30 lumaft
sudo cp /srv/lumaft/data/lumaft.db /mnt/backups/lumaft-$(date -u +%Y%m%dT%H%M%SZ).db
sudo docker start lumaft

A VM snapshot or a datastore-level backup of a running VM is crash-consistent. It is a useful safety net for the operating system; it is not a verified database backup.

High availability

Run one VM. vSphere HA restarts it on another host after a host failure, and the data VMDK moves with it. Do not run two Lumaft VMs against one database; use PostgreSQL with an Enterprise license when you need coordinated replicas.

Upgrades

  1. Stop the container cleanly and take a cold backup.
  2. Pull or load the new digest and run the same command against /srv/lumaft/data.
  3. Confirm readiness. A refused start naming a gated migration has migrated nothing; see Database integration.

Monitoring

Poll /api/v1/readiness from your monitoring system and the authenticated /api/v1/storage-health endpoint for disk pressure. Alert when dataDirectory.freeBytes falls below the growth headroom you sized the disk for.