Adding nodes
A new machine joins with one command, copied from the web UI of a node already in the cluster. The command downloads the exact release that node runs, installs the new machine as a node, and joins it.
Before you start
- The new machine meets the system requirements: fresh Ubuntu 24.04/26.04 or Debian 12/13, the same CPU architecture as the cluster, on the same LAN.
- Every node in the cluster is online. Membership changes one node at a time, with all nodes in contact; the join is refused otherwise.
- A fourth node and beyond needs a license. Up to 3 nodes are free; see License.
- The new machine can reach the cluster's nodes on TCP 7443 and 8443, and they can reach it (see Ports and files). It also needs internet access for the Ubuntu/Debian packages and Incus.
curlandsudoare installed on the new machine. A minimal Debian install may lack them:apt install curl sudoas root first.
1. Get the join command
On any node, open Nodes. A one-node cluster shows Add a second node; a cluster with two or more shows Add a node. Choose Show the join command.
The command looks like this:
curl -fsSk --pinnedpubkey 'sha256//…' https://192.168.1.11:7443/join.sh | sudo bash -s -- --token '…'
It works for 15 minutes and for one join. Create a new token gives you a fresh one; showing a new token replaces the old one.
The command contains the address you opened the dashboard with, so open it at an address the new machine can reach (the node's LAN IP, not localhost).
To add a third and later node, repeat this for each machine, one at a time: start the next join once the previous node shows as online on Nodes.
2. Run it on the new machine
Paste it into a terminal on the new machine. You can add options to the end:
| Option | Meaning |
|---|---|
--disk /dev/nvme1n1 | Give the VMs a whole disk. It is wiped. Without it, the pool takes the system disk's free space. |
--force-disk | Allow --disk to wipe a device that has partitions or filesystems. |
--pool-size SIZE | How much of the system disk's free space the pool takes, e.g. 80% or 200GiB (default: all of it). |
--name NAME | Node name, if it should differ from the hostname (lowercase letters, numbers and dashes, starting with a letter). |
--iface IFACE | LAN interface for VMs (default: the default-route interface). |
--keep-sleep | Leave suspend and hibernate on (by default the installer offers to turn them off). |
--yes | Don't ask for confirmation. |
Leave out --pool: the new node always gets the cluster's pool name.
The script:
- Checks the machine: it must have the cluster's CPU architecture, a usable node name, and no staxmax on it yet.
- Downloads the installer and its signature from the node, checking that node's certificate against the pin in the command (the certificate is self-signed, so the pin is what proves it is your node).
- Runs the same host setup as Installation: ZFS, Incus, the storage pool (named like the cluster's) and network. Questions such as the confirmation are asked in your terminal; add
--yesto skip them. The new node keeps the installer, so it can hand it on to the next machine too. - Waits for staxmaxd to start, then joins the cluster with the token.
When it is done it prints the new node's dashboard address. Sign in with the cluster's admin password; the new node adopts it.
When a second node joins, every protected VM starts copying to it right away; the Replication tile on the Overview shows the first copies in progress, and large disks take a while. A third or later node starts empty: existing VMs keep their current replica, and new VMs, moves and failovers can use the new node.
Going from two to three nodes changes how failover is decided: a majority of nodes now decides which nodes keep running VMs, and the router is no longer the tiebreaker. After a node fails, its VMs are protected again on the nodes left. See Failover. You can go back to two by retiring a node — see Nodes.
Installing by hand instead
Under Install it yourself instead the join card also shows the bare token. Install the new machine with the .run as in Installation (pass the cluster's --pool name if it isn't stax), open its web page, choose Join an existing cluster, enter a Node name, paste the Join token, and choose Join cluster.
If the join is refused
| Message | What to do |
|---|---|
| NODE is offline; every node must be online to add a node | Bring that node back, or retire it first if it is gone for good. |
| NODE has not caught up with the member list yet | Wait a few seconds and try again. |
| join token is invalid or expired; create a new one | The token is older than 15 minutes, was replaced, or was already used. Choose Create a new token. |
curl: (22) The requested URL returned error: 404 | No join token is active on that node any more (expired or used). Choose Create a new token and run the new command. |
| the cluster already has a node named … | Pass a different --name. |
| Cannot use '…' as the node name | The hostname doesn't make a valid node name. Pass --name. |
curl: (22) The requested URL returned error: 503 | The node you got the command from has no copy of its own release; the join card says so too (This node does not have the installer for staxmax …). Copy staxmax-<version>-linux-x64.run and its .sig into /var/lib/staxmax/updates/releases on that node, or update the cluster from Settings, then try again. |
| staxmax is already installed here | Usually a first run that installed staxmax but failed to join. Don't run the command again: create a new token, open the new machine's web page, choose Join an existing cluster and paste the token, as under Installing by hand instead above. |
| This machine is already a staxmax node | The machine was a node before. Remove it with sudo staxmax-uninstall and sudo rm -rf /var/lib/staxmax, then run the command again. |
| This machine is …, but the node serving this script runs a … build | The new machine has a different CPU architecture from the cluster. |
More in Troubleshooting.