Skip to content
Français
Conteneurs & Orchestration medium

Extending an Incus OS cluster: workers and remote nodes

20 min de lecture

Read this page in French

incus logo

An Incus OS cluster keeps its quorum on three voters, but nothing stops you grafting on further nodes that only host instances. Those database-client members add compute capacity without weighing on the database, and they can live on a remote site. This guide shows how to add a worker, target instance placement (a specific node, a group), reflect topology with failure domains, and administer a distant node. Intended audience: administrators of an Incus OS cluster who want to extend it beyond its three-node core.

What you will learn

  • Why a worker joins the cluster as a database-client.
  • Add a member outside the quorum and keep it a client.
  • Place instances on a specific node or a group.
  • Model physical topology with failure domains.
  • Administer a remote node over the network, and its limits.

Prerequisites

Why a worker joins as a database-client

A cluster assigns its database roles within two limits: 3 voters (cluster.max_voters) and 2 stand-by members (cluster.max_standby). Once those five slots are taken, any additional member becomes a database-client: it neither replicates the database nor votes, it just hosts instances. That is exactly what a worker should be: capacity, with no responsibility for consensus.

To be certain a new node stays a client and does not pick up a database role, set stand-by slots to zero before adding it:

Fenêtre de terminal
incus config set node1: cluster.max_standby 0

After joining, incus cluster list confirms the result: the worker appears with no role, the roles column empty.

node1,database
node2,"database-leader,database"
node3,database
node4,

Adding the worker to the cluster

On Incus OS, a node joins the cluster through a seed, never through a shell. You issue a join token from an existing member, then provision the new node with a seed carrying that token, the cluster certificate and the configuration of its local pool:

Fenêtre de terminal
incus cluster add node1:node4

The rest of the mechanics (image, incus.json seed with the cluster block, certificate, pool member_config) is identical to joining a core node and is covered in Incus OS without a shell. The only difference for a worker is the max_standby setting applied before the join. Once first boot is done, the node updates itself to the cluster version and shows as ONLINE, ready to receive instances.

Placing instances on a worker

By default, Incus picks the host node by load. To force a placement, the --target option names a specific member:

Fenêtre de terminal
incus launch images:debian/12 node1:app1 --target node4 -s local -n incusbr0

The instance starts on the worker, as the location column confirms:

| app1 | RUNNING | node4 |

That direct targeting suits pinning a workload to a given node, for instance a worker with particular hardware or sitting on a specific site.

Targeting a subset with cluster groups

Pinning node by node quickly becomes tedious. Cluster groups gather members under a logical name you can launch instances towards without knowing the exact node. You create a group, then assign members to it:

Fenêtre de terminal
incus cluster group create node1:workers
incus cluster group assign node1:node4 default,workers

A member always belongs to the default group and can accumulate others. To launch an instance on the group, prefix the group name with @ in --target:

Fenêtre de terminal
incus launch images:debian/12 node1:calc1 --target @workers -s local -n incusbr0

Incus then places the instance on a member of the workers group. That is the ideal mechanism for reserving a family of workloads to a category of nodes: the workers, the ones with a GPU, or the ones on a given site.

Reflecting topology with failure domains

A failure domain tells Incus which members share a common risk (same rack, same site, same power feed). When a database member fails, Incus tries to reassign its role to another member of the same domain, which avoids concentrating every voter in one physical place. The domain is set by editing the member definition:

Fenêtre de terminal
incus cluster show node1:node4 | sed "s/^failure_domain:.*/failure_domain: siteB/" | incus cluster edit node1:node4

Reading the member back confirms the change:

failure_domain: siteB

By assigning domains that match physical reality, you help the cluster spread roles and instances sensibly across locations instead of stacking them onto a single failure zone.

The remote node case

A worker can sit on another site. Two points deserve attention. First, connectivity: on first boot, the node applies its join seed and contacts the leader at the address it was given. At that moment no application service is up yet, so that address has to be reachable at the infrastructure level (routing, site VPN). The built-in Tailscale service only activates after boot: it serves to administer the node once it is in place, not to carry the initial join.

Once the worker is in service, enabling it on the tailnet makes it reachable through a stable private 100.x address, handy for driving its API remotely without exposing a public port. The procedure is described in connecting nodes to Tailscale.

Troubleshooting

SymptomLikely causeFix
The new node takes a database rolemax_standby non-zero at join timeSet cluster.max_standby 0 before adding the worker
--target @group failsGroup missing or emptyCreate the group and assign at least one member
The remote worker goes OFFLINE intermittentlyLatency above offline_thresholdRaise cluster.offline_threshold, check the link
An instance cannot migrate off the workerVolume on local storagePut the data on shared storage

Key points

  • Beyond the 5 database slots (3 voters plus 2 stand-by), a member becomes a database-client.
  • Setting cluster.max_standby to 0 guarantees a new node stays a pure worker.
  • On Incus OS you always join through a seed; only the max_standby setting distinguishes a worker.
  • --target <node> pins an instance; --target @<group> aims at a cluster group.
  • Failure domains reflect physical topology and guide how roles are spread.
  • A remote node needs infrastructure connectivity to join; Tailscale serves administration, not the join itself.

Next steps

Is this site useful to you?

Fewer than 1% of readers support this site.

I maintain more than 700 free guides, with no ads and no tracking. Any support, even a symbolic one, helps cover hosting and keeps these resources free. Thank you for the help.

The form does not show? Open Ko-fi in a new tab.

Subscribe and follow my DevSecOps work on LinkedIn