Phase 2 — Incus¶
Objective¶
Incus cluster of 3 nodes with quorum, cluster groups by architecture, and
oliver in manual scheduler.
What you learn¶
Incus as a shared hypervisor: dqlite quorum across 3 members, cluster groups to steer x86 vs ARM64 (64-bit ARM architecture) workloads, and manual scheduler to protect RAM.
Learn this phase
In short: Incus is an apartment building. Each apartment is an instance: a container (a lightweight room sharing the building's utilities) or a virtual machine (a full apartment with its own kitchen). With three coordinated buildings, if one closes the other two keep running.
Key concepts
- Container and virtual machine: the container is lighter; the VM (Virtual Machine) isolates more.
- Cluster and quorum: with 3 members, the cluster decides by majority and tolerates the loss of one.
- Cluster group: a label to direct workloads to a node type (for example, ARM64 only).
- x86_64 and ARM64: two distinct processor architectures; they don't mix in the same instance.
- Manual scheduler: the
olivernode does not receive instances automatically; protects its memory.
Practical challenge (read-only): view cluster members and their instances.
Why a three-member cluster and not two?
With two, if one falls there is no majority and the cluster blocks. With three, the majority is two and it can keep running.
When do you prefer a container and when a VM?
Container for light, fast Linux workloads; VM when you need another kernel or more isolation.
What are cluster groups for?
To decide on which nodes an instance runs, for example keeping ARM64 workloads on deborah.
Questions? Use the Learn with AI button next to each heading, or the Learn page.
Stack for this phase¶
flowchart TB
subgraph cluster [Incus_cluster_3_nodos]
INV[invincible_x86]
NOL[oliver_x86_quorum]
DEB[deborah_arm64]
end
INV --> CGx86[x86_nodes]
DEB --> CGarm[arm64_nodes]
NOL --> CGx86
- Incus — LXC virtualization (Linux system containers)/VM; Phase 2.
- Zabbly Incus stable — Debian packages; Phase 2.
- Ansible — Installs package, bootstrap, join, groups, UI (user interface).
- Roles:
incus_install,incus_cluster,incus_ui
Deep dive: Incus Cluster
This decision in other environments
An Incus cluster with 3 members (dqlite quorum) on hardware you already had, free and with containers and VMs
in the same tool. If a member falls, the other two keep deciding; oliver only votes and receives no workloads.
- Benefits: a virtualization cluster pools several servers into a single resource: move VMs between nodes for maintenance without cutting service, restart workloads on another node if one fails, and manage everything from a single API or console, with permissions and audit trail of who did what.
- Commercial products: the most common is VMware vSphere (now Broadcom, with licensing changes driving many companies to seek alternatives), Nutanix AHV or Proxmox VE with support subscription. For large private clouds, OpenStack.
- Shared storage: instead of the local
dirdriver, shared or distributed storage (Ceph, SAN) is used, so a VM can move or start on another node without copying its disk. - Incus support: Incus also has commercial support (Zabbly, FuturFusion); the real cost is in the people who know how to operate it.
- When Incus makes sense: escaping expensive VMware licenses, edge or branch sites with few servers, and labs or CI that need to create and destroy many instances quickly.
| Cloud | Equivalent service |
|---|---|
| GCP | Compute Engine |
| AWS | Amazon EC2 |
| Azure | Azure Virtual Machines |
| Oracle Cloud (OCI) | OCI Compute |
You get VMs instantly, without buying or maintaining hardware, with SLA and in many regions. You lose control over the hypervisor, pay for every hour the VM is running, and are locked into that provider's network and images.
Before you start¶
- Phase 1 — Network completed (static IPs in
br0). -
br0operational on all 3 nodes. - ≥ 20 GB free on each node (see storage requirements).
Storage requirements¶
Default HomeLab: driver dir (directory on the OS filesystem, no
dedicated disk). Variables in group_vars/incus_cluster/vars.yml.
| Aspect | Value / requirement |
|---|---|
| Driver | dir — no dedicated partition or loop |
| Pool per node | local (each member has its local pool) |
| Pool path | /var/lib/incus/storage-pools/local |
| Filesystem | ext4 or xfs; not NFS/CIFS as backend |
| Minimum free space | 20 GB per node; 50 GB+ if you will create VMs or many instances |
| Metadata | dqlite and daemon config share disk with the pool |
Per node¶
| Node | Disk | Implication |
|---|---|---|
invincible |
Kingston 224 GB SSD | Leader + UI; largest pool of the x86 nodes |
oliver |
TECLAST 120 GB SSD | Quorum; local pool mandatory even if it receives no instances; avoid filling disk with CAPN images |
deborah |
eMMC 233 GB (NVMe 250 GB not detected today) | If eMMC tight: incus_storage_path: /srv/incus/storage-pools/local in host_vars |
Limitations of dir¶
- No pool-level copy-on-write (instance snapshots and export tarball do work).
- VMs (virtual machines) slower than with
zfs/btrfs/lvm. - No thin provisioning or native compression.
- Each member maintains its own
localpool; no distributed storage like Ceph.
Deep dive: Incus Cluster — Storage
Run¶
Step 1 — Install Incus package on all 3 nodes¶
Step 2 — Bootstrap, join, groups, scheduler, and UI¶
A single playbook: bootstrap on invincible, join on oliver and deborah, cluster groups, manual scheduler, and UI.
Optional tags: --tags bootstrap, --tags join, --tags groups, --tags ui.
On invincible (bootstrap):
On invincible, generate tokens:
On oliver and deborah: incus admin init → join with token.
Cluster groups and scheduler:
incus cluster group create x86-nodes
incus cluster group create arm64-nodes
incus cluster group assign invincible x86-nodes,default
incus cluster group assign oliver x86-nodes,default
incus cluster group assign deborah arm64-nodes,default
incus cluster set oliver scheduler.instance manual
UI on invincible:
Network profile (instances on LAN)¶
Mandatory manual step — neither the Ansible playbook nor incus admin init
do this automatically, in neither of the two paths above. Without this, Incus
instances remain on the default private NAT (Network Address Translation) network, not on your LAN (local network).
On invincible (profiles are shared across the whole cluster, no need to repeat on oliver/deborah):
Verify:
Admin UI¶
Ansible installs incus-ui-canonical and core.https_address on invincible
(play ui of the playbook). Initial access via client certificate; SSO (single sign-on)
(Dex) in Phase 4.
- Add
incus.homelab.localin/etc/hosts(full block in HomeLab Summary). - Open
https://incus.homelab.local:8443 - Accept the self-signed certificate
- Generate/import a client certificate
OIDC with Dex
SSO login (GitHub via Dex) is configured in
Phase 4 — Incus UI OIDC (OpenID Connect).
Only the devops team from maceosoft enters (Dex filter) and, with Incus default
configuration, those OIDC users have full access.
Deep dive: Incus Cluster — UI
Backup and snapshots (optional)¶
Not configured in bootstrap. The procedure is in Incus Backups; the alternatives, in Backup and snapshots.
Options¶
| Decision | Default HomeLab | Alternative |
|---|---|---|
| Storage backend | dir (Ansible) |
btrfs, zfs, lvm via incus_storage_driver in host_vars |
| oliver | scheduler.instance manual |
— |
| Pool on deborah | Default path on eMMC | incus_storage_path on NVMe (/srv/incus/...) |
Change driver in existing cluster
An already initialized cluster cannot be re-initialized with a preseed without manual migration.
Choose the driver before the first playbook-incus-cluster.yml.
playbook-incus-cluster.yml)incus admin init)dirzfs / btrfs/srv/incus)host_varsVerify¶
incus cluster list
# 3 miembros ONLINE
incus cluster group list
incus storage list
# eth0 → parent br0
incus profile show default
curl -kI https://incus.homelab.local:8443
If it fails¶
| Symptom | Check |
|---|---|
| Join rejected | Single-use token; stable IP in br0 |
| Broken quorum | Incus versions aligned on all 3 nodes |
| Insufficient space | df -h; minimum 20 GB (incus_storage_min_free_gb) |
| Playbook not idempotent | Node already clustered; check incus cluster list |
Next¶
URLs and global /etc/hosts: HomeLab Summary (after Phase 4).