Documentation/Getting started

Getting started#

This guide takes you from an empty Linux server to a running application: install the CLI, bootstrap one manager, deploy a first workload, then add more nodes. It assumes no prior Luma setup.

What you need#

Requirement Why
A Linux server, Ubuntu 22.04+ recommended, 2 CPU / 8 GiB RAM to start Runs Luma Control, Nomad and Traefik; Control reserves 4 GiB
A domain whose DNS is managed by Cloudflare Control API, dashboard and application hostnames
A Cloudflare API token with Zone Read and DNS Edit for that zone Luma creates and updates DNS records
Public ports 80 and 443 open, and an email address for Let's Encrypt HTTPS certificates and ingress
Access to the container registry that hosts the Luma Control image (GHCR by default) The manager pulls Control from it

Tailscale, a builder node and a private registry are not needed for the first workload. Add them later when you need home nodes, relays or source builds.

1. Install the CLI#

Run this on the manager and on every machine you want to deploy from:

curl -fsSL https://raw.githubusercontent.com/LiuTianjie/luma/main/scripts/install-luma.sh | sh
~/.local/bin/luma doctor --local

The installer needs Python 3.9+ and curl or wget. It installs the latest release into ~/.local/share/luma and puts luma in ~/.local/bin; open a new shell if that directory is not on your PATH yet. It does not change Docker, Nomad or firewall settings.

Other ways to install:

# A specific release, a branch or a commit
curl -fsSL https://raw.githubusercontent.com/LiuTianjie/luma/main/scripts/install-luma.sh | LUMA_INSTALL_REF=v0.2.14 sh

# CI runners and virtual environments
python -m pip install "luma-infra==0.2.14"

To uninstall the CLI (server state under /opt/luma is not touched), run scripts/uninstall-luma.sh; add --purge to also remove saved settings and logins.

2. Bootstrap the manager#

Run this on the manager server:

luma bootstrap --domain luma.example.com

luma bootstrap asks for anything it is missing and explains each value:

Answers are saved to ~/.luma.config.json (mode 0600) so reruns do not ask again. You can also export them as environment variables or put them in a .env file in the current directory; Luma reads only its own settings from ./.env.

If the manager needs a proxy to reach GHCR, which is usually the case on mainland China hosts, set EGRESS_SUBSCRIPTION_URL before bootstrapping. Otherwise pass --skip-egress.

Bootstrap installs Docker and the Nomad server, applies node metadata, configures the firewall, deploys Traefik and Luma Control as Nomad jobs and creates the Control database at /opt/luma/control/control.sqlite3. Each step prints [start], [ok] or [fail] with a Fix: hint. Bootstrap is safe to rerun after fixing a failure; individual layers can also be repaired with luma manager egress, luma node tailscale and luma doctor.

When it finishes, it prints:

Control URL: https://luma.example.com
Management token: ...
Node join token: ...
Dashboard: https://luma.example.com/dashboard/

Keep both tokens private:

Node agents get their own internal credentials automatically; you never handle them.

To use your own Control image, publish it and set LUMA_CONTROL_IMAGE before bootstrapping. Luma never builds or reuses a stale local image during bootstrap or update.

3. Deploy a first workload#

The fastest check is the dashboard: open the dashboard URL, paste the management token and choose Applications → Create application → hello-world first install. It deploys an internal service with no DNS or registry requirements and proves that scheduling works.

From your laptop, sign in and deploy a public service:

luma login https://luma.example.com --token-stdin < token.txt
luma init --name status --image traefik/whoami:v1.10.3 --region cn --domain status.example.com --port 80
luma validate status.yaml
luma deploy status.yaml --dry-run
luma deploy status.yaml
luma app list

luma init writes a commented manifest with a memory limit and a health check; edit it as needed. --dry-run shows the Nomad job that Control will submit. Replace status.example.com with a hostname in your Cloudflare zone. See Deploying applications for local builds, repository imports and Compose applications.

4. Add nodes#

Run this on each additional server, using the node join token:

luma node join https://luma.example.com --token <node-join-token> --region global --name sg-1

Home and other private nodes need Tailscale: set TAILSCALE_AUTHKEY or run luma node tailscale first. macOS nodes also need a running Docker (Docker Desktop or OrbStack).

luma node list shows registered nodes and their agents. luma node exit stops Nomad on the current machine and removes its Luma state; add --endpoint and --token to unregister it too.

Required ports#

Luma configures UFW on Linux nodes. Mirror these rules in cloud security groups and Tailscale ACLs:

Port Between Purpose
80/tcp, 443/tcp Internet → manager HTTP(S) ingress and Let's Encrypt
tcp-relay ports Internet → manager Public TCP relays, for example 3306/tcp
4646/tcp Clients and Traefik → Nomad server Nomad HTTP API
4647/tcp Nomad clients → server Nomad RPC
4648/tcp, 4648/udp All Nomad agents Nomad gossip

When the manager has a Tailscale address, Luma opens the Nomad ports only on tailscale0. Port 7890 (the egress proxy) is never exposed publicly.

5. Keep Luma up to date#

luma update            # on the manager: CLI and Control; on a node: CLI and agent; elsewhere: CLI
luma update fleet      # from any client: every node with a ready agent (the manager is skipped)

Both install the latest release unless you pass --install-ref. The dashboard's Nodes → Update center does the same with progress and route checks, and is the preferred way to update the manager. luma update never restarts Docker or Nomad and never redeploys applications.

6. Check health#

luma status
luma doctor
luma doctor --deep

doctor checks the login, Control, DNS configuration, node agents and Nomad; --deep also evaluates Docker and Nomad diagnostics reported by each node.

Next steps#