Incus Integration
MetalSoft integrates with Incus in two ways:
- As a VM Pool provider (VM Management) Consult Managing a VM Pool for more details for more details
- As an application via the extensions mechanism, which deploys and manages the lifecycle (install, scale-out/scale-in, upgrade) of an Incus cluster on bare-metal nodes The extension definition and Ansible playbooks live in the Incus extension repository
The rest of this page covers the application extension.
Prerequisites
Section titled “Prerequisites”- A MetalSoft controller running release 7.2 or newer, with the Ansible Runner capability enabled on the site controller. See Enabling the Ansible Runner Capability for more details.
- Bare-metal hosts prepared for the Incus deployment: at least one host with a healthy hardware status and a minimum of two network interfaces.
- The Incus OS templates registered at the target site:
incus-ubuntu-22-04-cluster-nodeorincus-ubuntu-24-04-cluster-node(see the MetalSoft OS templates repository). - A logical network profile labeled
incus-mgmtat the target site. The extension attaches the cluster nodes to this network with DNS records provisioned per instance, so forward DNS resolution for the instance FQDNs must work. - The extension’s site configuration saved on every target site (see below). Because the extension declares site-level configuration variables, a deployment fails with “Extension not enabled on site” until an operator opens the extension’s site config on each site and saves it — all variables ship with defaults, so this is a one-click save unless overrides are needed.
Creating the Incus application extension
Section titled “Creating the Incus application extension”- Clone the repository containing the extension definition:
git clone https://github.com/metalsoft-io/metalsoft-extensionsThe definition references an Ansible bundle and an execution environment image already hosted on repo.metalsoft.io, so no re-packaging is needed. If your sites use a different logical network profile, adjust infrastructure.logicalNetworks[].profileLabel (default incus-mgmt) before creating the extension.
- Create and publish the extension:
metalcloud-cli extension create "Incus" application "Incus cluster lifecycle management" --definition-source metalsoft-extensions/incus/extension.jsonmetalcloud-cli extension publish <id-of-created-extension>- Enable the extension on the site level and save its configuration. After publishing, the extension must be activated per site on the site level configuration page. This is also where its configVars — the operator-set, site-level settings — are configured, and it must be done before the first deploy.
You should now be ready to deploy the Incus application from the Infrastructure Designer.
Site configuration (configVars)
Section titled “Site configuration (configVars)”These settings are set once per site by an operator and apply to every Incus deployment at that site:
| Variable | Type | Default | Description |
|---|---|---|---|
DNSResolvers | String | 1.1.1.1 | Comma-separated DNS resolver IPs handed to every cluster node (systemd-resolved). |
branch_release | String | stable | Zabbly package channel to install/track: daily, stable or lts-6.0. |
images_auto_update_interval | Integer | 6 | Interval in hours at which cached images are auto-updated. |
core_https_port | Integer | 8443 | HTTPS port the Incus API listens on. |
separate_api_and_cluster_traffic | Boolean | false | When true, cluster traffic uses a dedicated port (API port + 1) instead of sharing the API address. |
cluster_member_upgrade_enabled | Boolean | false | When true, edit deployments apply OS/package upgrades to existing members one node at a time before scaling. Enable it before scaling out an aged cluster, or the new node’s newer Incus version fails the cluster join version check. |
cluster_member_force_remove_enabled | Boolean | false | When true, scale-in force-removes a member that stays offline after evacuation. |
vars_debugging_enabled | Boolean | false | When true, playbooks print the resolved extension variables for debugging. |
Deployment inputs
Section titled “Deployment inputs”Each deployment created from the Infrastructure Designer takes the following inputs:
| Input | Type | Default | Description |
|---|---|---|---|
cluster_server_type | ServerType | — | Server type for the Incus cluster nodes. |
cluster_instance_count | Integer | 1 | Number of nodes (1–50). Changing it on an existing deployment triggers scale-out/scale-in. |
cluster_node_os_template | OsTemplate | — | OS template for the cluster nodes (Ubuntu). |
cluster_enabled | Boolean | true | Deploy Incus in clustering mode. |
install_ui | Boolean | true | Install the Incus web UI. |
storage_driver | String | dir | Storage backend for the default pool: dir, btrfs, lvm, lvmcluster, zfs, ceph, cephfs or cephobject. |
Outputs
Section titled “Outputs”After every successful deployment or edit the extension publishes the following outputs on the extension instance:
| Output | Description |
|---|---|
cluster_api_url | https://<first node FQDN>:<core_https_port> |
cluster_ui_url | Same as the API URL when install_ui is true, empty otherwise |
cluster_members | Comma-separated FQDNs of the current cluster members |
cluster_member_count | Number of cluster members |
cluster_storage_driver | Storage driver of the default pool |
incus_release | Zabbly channel the cluster tracks |
browser_client_certificate | PEM user (browser) client certificate trusted by the cluster |
browser_client_certificate_key | PEM private key for the browser certificate (see security note below) |
Browser access to the Incus UI (client certificate)
Section titled “Browser access to the Incus UI (client certificate)”Each deployment generates a dedicated user client certificate on the first cluster member and registers it in the cluster-wide trust store. The PEM certificate and private key are published as the browser_client_certificate / browser_client_certificate_key outputs so a person can authenticate to the Incus web UI (cluster_ui_url):
- Copy the two output values into local files, keeping the
BEGIN/ENDlines and line breaks intact:browser-client.crtandbrowser-client.key. - Convert the pair to a PKCS#12 bundle (choose an export password — the browser asks for it at import):
openssl pkcs12 -export \ -in browser-client.crt \ -inkey browser-client.key \ -name "incus-browser-user" \ -out incus-browser-user.p12If an older browser rejects the file, re-run with the additional -legacy flag.
3. Import the bundle into the browser’s certificate store (Firefox: Settings → Privacy & Security → Certificates → View Certificates… → Your Certificates → Import…).
4. Open the cluster_ui_url output. Accept the self-signed server certificate warning first, then select the imported certificate when the browser prompts for a client certificate (restart the browser if no prompt appears — it may cache a “no certificate” choice per host).
Scaling
Section titled “Scaling”Edit the deployment’s cluster_instance_count. Scale-in nodes are evacuated and removed from the cluster before the infrastructure change (while they are still reachable), and scale-out nodes are installed and joined after it. When cluster_member_upgrade_enabled is true, existing members are upgraded one node at a time before new nodes join.
Execution environment
Section titled “Execution environment”The extension’s Ansible playbooks run on the Site Controller inside an execution environment — a container image holding all the dependencies the playbooks need. The extension ships its own image, declared as an OciImage asset in the extension definition and selected by each Ansible task via the ee option, so no Site Controller configuration is required. To customize it, rebuild the image from the execution-environment.yml recipe in the extension repository and update the OciImage asset in the definition.