Use real domains for the control API and public services, for example luma.example.com and status.example.com.
Self-hosted deploy control plane
luma Lightweight multi-server control plane
Write one YAML file for the domain, port, exposure, and region. Luma syncs Cloudflare DNS, Traefik HTTPS routes, and Nomad jobs while each server joins the cluster locally.
curl -fsSL https://raw.githubusercontent.com/LiuTianjie/luma/main/scripts/install-luma.sh | sh
Install the skill from https://github.com/LiuTianjie/luma/tree/main/skills/luma-deployment-yaml
Or install manually via terminal
mkdir -p ~/.codex/skills && git clone --depth 1 https://github.com/LiuTianjie/luma.git /tmp/luma-repo && cp -R /tmp/luma-repo/skills/luma-deployment-yaml ~/.codex/skills/ && rm -rf /tmp/luma-repo
domain: status.example.com
exposure: cn-edge
port: 80
$ luma deploy status.yaml
ok dns status.example.com -> cn-edge
ok tls acme certificate requested
ok route Host(status.example.com) -> status:80
ok nomad status allocations x2
domain is the service address
compose.yml with Luma sidecars for multi-service apps
Have these ready first.
Put the DNS zone in Cloudflare and create a token that can read the zone and edit records.
Start with one sudo-capable Linux server. A 2c2g manager is enough for testing if app resources are capped.
Skip Tailscale for a single public manager. Use it for home nodes, private joins, or tailscale-relay.
What each command updates.
name: status
image: traefik/whoami:latest
region: cn
domain: status.example.com
port: 80
exposure: cn-edge
replicas: 2
resources:
limits:
cpus: "0.50"
memory: 512M
Luma reads the manifest and updates the deploy path.
-
01
Read service entry
Read
domain,port, andexposureto get the address and target port. -
02
Write DNS record
Write the Cloudflare record so the domain points to the selected entry node.
-
03
Create HTTPS route
Traefik reads the Host rule, requests a certificate, and creates the HTTPS route.
-
04
Pull private image if needed
For private GHCR or custom registries, Luma uses saved registry credentials without writing tokens into the manifest.
-
05
Update service
Update the Nomad job so
status.example.comforwards tostatus:80. -
06
Keep version history
Nomad stores job versions so the dashboard and
luma rollbackcan revert the running job. -
07
Limit resources
Use Nomad resource limits when apps and control services share a small server.
Multi-Service Compose & Declarative Storage
-
01
Standard docker-compose.yml
Zero intrusion. Continue using standard Compose files to describe service dependencies and networks, keeping it useful for local development.
-
02
Luma Sidecar (luma.compose.yml)
Isolate deployment-specific settings (like region, public domain exposure, and storage volumes mapping) into a separate sidecar file.
-
03
Managed Storage Classes
Register NFS or local storage classes in Luma Control. Luma dynamically resolves mounting endpoints at deploy time based on node and region topology.
-
04
Data Loss Safeguards
Strict data-loss warnings. Luma blocks deployments if a volume's backend storage path changes, demanding explicit
adoptedorinitializeoverrides.
name: app-stack
compose: docker-compose.yml
region: cn
volumes:
pg-data:
storageClass: cn-nfs
path: postgres/pg-data
services:
app:
exposure: cn-edge
domain: app.example.com
port: 3000
version: '3.8'
services:
app:
image: company/app:latest
volumes:
- pg-data:/var/lib/pg/data
postgres:
image: postgres:15
volumes:
- pg-data:/var/lib/postgresql/data
volumes:
pg-data: {}
Run the command for each machine role.
Install the CLI
Install luma on the manager first. Workers and clients install the same command later when needed.
curl -fsSL https://raw.githubusercontent.com/LiuTianjie/luma/main/scripts/install-luma.sh | sh
~/.local/bin/luma preflight
Initialize the control plane
Initialize the Nomad server on the first machine, then deploy Traefik, Luma Control, and the egress gateway as Nomad jobs.
luma bootstrap manager --domain luma.example.com
Join workload nodes
Each server runs join locally and gets a region label. Scheduling mainly follows region.
luma node join https://luma.example.com --token <join-token> --region global --name global-sg-1
Save private registry credentials
Use saved credentials when the image registry is private.
luma login https://luma.example.com --token <deploy-token>
printf '%s' "$GHCR_TOKEN" | luma registry login ghcr.io --username <user> --password-stdin
Deploy from a client
After login, the client submits the manifest. Luma prints DNS, certificate, route, and stack progress.
luma login https://luma.example.com --token <deploy-token>
luma deploy status.yaml
Documentation & Guides
Explore detailed specifications, architecture principles, and usage guides for every component.
Bootstrap & Operations
Learn how to quickly initialize and deploy the cluster control plane infrastructure with luma bootstrap.
Compose & Storage
Explore luma.compose.yml sidecars, multi-service deployments, and NFS storage binding rules.
AI Agent Skill
Equip your Codex, Claude Code, or Gemini agent with Luma deployment rules for smart YAML generation.
Read Docs βCLI Command Reference
Complete manual for luma subcommands, including deploy, history, rollback, and operator workflows.