Skip to content

Enroll an LXD / Incus Node

Add a host running LXD or Incus as a container backend. MFCloud talks to LXD over its native HTTPS API on 8443/tcp using mutual TLS — the same auth model LXD itself uses for lxc remote add.

Prerequisites

  • LXD ≥ 5.0 OR Incus ≥ 0.4 installed and listening on 0.0.0.0:8443:
    lxc config set core.https_address :8443
    
  • The compute node is already registered in MFCloud (Compute Nodes table → Active).
  • Outbound 8443/tcp from the master to the node.

Quick steps

  1. On the LXD host, generate a one-time trust token:

    lxc config trust add --name mfcloud
    
    Copy the token it prints — it's a JWT-shaped string starting with eyJ.

  2. Containers tab → ➕ Enroll LXD Node.

  3. Fill in:
  4. Host IP — must match a row in Compute Nodes.
  5. LXD Port — defaults to 8443.
  6. Trust Token — paste from step 1.
  7. Enroll Node.

MFCloud will:

  • Generate a fresh client TLS keypair locally.
  • POST it to the LXD trust API along with the token.
  • Open a TLS socket to the LXD endpoint and pin its server certificate fingerprint (TOFU — Trust On First Use) to compute_nodes.lxd_server_fingerprint.
  • Flip lxd_enabled=true on the row.

The node now appears in the Containers tab dropdowns and the Create Container modal.

What "fingerprint pinning" means

After enrollment, every subsequent LXD call validates the server cert's SHA-256 fingerprint against the stored value. If the LXD host is reinstalled (new self-signed cert) the master will refuse to talk to it with:

[LXD] server fingerprint mismatch — possible MITM or re-installed daemon

Resolve via Containers tab → node row → 🛡 Re-trust, which clears the pin and re-captures on the next call.

Verify enrollment

# On the master
docker compose exec app python -c "
from core.lxd import LXDClient
c = LXDClient('192.168.10.56')
print(c.list_instances())"

Should print [] (empty list, no containers yet) — anything else means the trust didn't take.

See also