
The strongest promise of Incus OS is its atomic A/B update: the new image installs on a second partition while the system keeps running, then the node switches over on reboot, with an automatic fallback if boot fails. This guide shows how to read versions, set the update policy, trigger a switch and update a cluster with no outage, all through the API, without ever opening a shell. Intended audience: administrators running an Incus OS fleet.
What you will learn
- Read the A/B version state of a node (
os_version,os_version_alternate,os_version_next). - Consult and change the update policy (channel, frequency, automatic reboot).
- Force a check and apply a switch on reboot.
- Update a cluster one node at a time without losing the quorum.
- Understand the rollback mechanism and when it fires.
Prerequisites
- One or more Incus OS nodes in service. See Incus OS without a shell for installation and establishing trust.
- An Incus client whose certificate the nodes trust, with a remote configured for each node to administer.
- For the cluster part, an Incus cluster of at least three nodes.
Incus OS exposes no shell. All administration goes through its REST API, prefixed with /os, queried with the incus query command. Each node publishes its own local OS API: unlike the regular Incus API, which has a cluster-wide view, the update commands below target one specific node through its remote.
The A/B model in short
An immutable system does not update package by package. It keeps two system slots side by side, called A and B. At any moment only one is active: the one the node booted from. When a new version arrives, it is written to the inactive slot, without touching the running system. The reboot moves activity to that freshly populated slot.
The scheme gives two guarantees: the update is atomic, since the node runs the old version until it restarts and there is no half-applied state, and the fallback is immediate because the old version stays intact on the other slot. It is the same principle as immutable mobile or container operating systems.
By default, Incus OS checks for updates every 6 hours. Two channels exist: stable, which sees at least one weekly update carrying the latest stable kernel and security fixes, and testing, rebuilt daily. Stable is the default.
Reading the version state of a node
The root of a node's OS API returns its environment, including the three fields that describe the A/B cycle:
incus query node3:/os/1.0{ "environment": { "hostname": "node3.lab", "os_name": "IncusOS", "os_version": "202607010319", "os_version_alternate": "202607011621+3-0", "os_version_next": "202607011621", "system_is_ready": true, "uptime": 109027 }}Three fields tell the whole story of the switch. os_version is the active version, the one the node booted from. os_version_alternate is what sits on the inactive slot. os_version_next is the version that will be active at the next reboot. Here os_version_next differs from os_version: a new image (202607011621, a build from 1 July) has already been downloaded and laid down on the inactive slot, ready to take over. The version number is a build timestamp in YYYYMMDDHHMM format.
Consulting and setting the update policy
The system/update endpoint exposes the node's update configuration and state:
incus query node3:/os/1.0/system/update{ "config": { "auto_reboot": false, "channel": "stable", "check_frequency": "6h" }, "state": { "last_check": "2026-07-02T16:14:34Z", "needs_reboot": true, "status": "Update check completed" }}The state.needs_reboot field set to true is the key signal: an update is installed and pending, and only a restart is missing to apply it. The config.auto_reboot field set to false explains why nothing happened on its own: the node downloaded and prepared the new image, but it is waiting for a decision from the administrator before rebooting. That is the recommended behaviour in a cluster, where restarts should be orchestrated one at a time.
To change the policy, you send a PUT with the config object. For instance, moving a lab node to the testing channel with a daily check:
incus query -X PUT node3:/os/1.0/system/update \ -d '{"config":{"auto_reboot":false,"channel":"testing","check_frequency":"1d"}}'Forcing an immediate check
Without waiting for the six-hour cycle, you trigger a manual check. It clears the image provider cache, looks for a newer version and downloads it onto the inactive slot if one exists:
incus query -X POST node3:/os/1.0/system/update/:checkThe response is empty on success. Query system/update afterwards: if an image was fetched, state.needs_reboot turns true and os_version_next reflects the new version. You can restrict the search to the operating system alone by passing {"os_only": true} in the request body, which skips application updates.
Applying the switch on reboot
Once needs_reboot is true, the switch fires with a plain restart of the node, commanded through the API:
incus query -X POST node3:/os/1.0/system/:rebootThe node restarts on the inactive slot. After a few seconds of unavailability it answers again, and the version fields have swapped:
{ "environment": { "hostname": "node3.lab", "os_version": "202607011621", "os_version_alternate": "202607010319", "os_version_next": "202607011621", "system_is_ready": true, "uptime": 156 }}The reading confirms success. os_version now shows the new version, 202607011621. The old one, 202607010319, moved into os_version_alternate: it now occupies the inactive slot and remains available as a safety net. On the system/update side, state.needs_reboot has fallen back to false. The table below sums up the swap observed on that node.
| Field | Before reboot | After reboot |
|---|---|---|
os_version (active) | 202607010319 | 202607011621 |
os_version_alternate (inactive) | 202607011621 | 202607010319 |
needs_reboot | true | false |
Updating a cluster with no outage
On a cluster, each node receives and prepares the update independently, but you never restart them all at once. A three-node Incus OS cluster keeps its quorum as long as two database members stay online. The rule is therefore to proceed one node at a time, checking each one comes back before moving to the next.
-
Check the overall state before starting. Every node should be
ONLINEand reportneeds_reboot: true, meaning the update is ready.Fenêtre de terminal incus cluster list node1: -
Reboot a non-leader member first, then wait for it to come back on the new version.
Fenêtre de terminal incus query -X POST node3:/os/1.0/system/:rebootWhile that node is unavailable, the other two hold the quorum and the service keeps running.
-
Confirm the switch of the rebooted node and its return to the cluster.
Fenêtre de terminal incus query node3:/os/1.0 | grep os_versionincus cluster list node1: -
Move to the next node and repeat. Finish with the database leader: when it restarts, another node automatically takes the
database-leaderrole.Fenêtre de terminal incus query -X POST node1:/os/1.0/system/:reboot
When you restart the former leader, the cluster API stays reachable through the other nodes: query the cluster with a remote pointing at a member that is still online, for example incus cluster list node2:, to follow the re-election. Once the last node is back, every member reports the same version and the cluster is homogeneous again.
Rolling back
The rollback of Incus OS is not a command you type after the fact: it is an automatic safety net built into boot. The bootloader counts boot attempts on the new slot. If the system fails to start correctly, it falls back to the previous slot, the one listed in os_version_alternate, which has stayed intact. Nothing has to be done by hand for that case.
This is exactly the point of the A/B scheme: an update that breaks boot does not turn the node into a brick. The old version, known to work, takes over automatically. After such a fallback, the node runs the old image and needs_reboot stays at false until a healthy new version is offered.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
needs_reboot stays false after a check | No newer version on the channel, or check_frequency set to never | Check config.channel and force a manual :check |
The PUT on system/update returns invalid update check frequency | Incomplete body, empty check_frequency | Send a complete config object with all three fields |
incus query nodeX: fails with not authorized | Client certificate not trusted on that node | Restore trust, see Incus OS without a shell |
Several cluster nodes go OFFLINE | Too many nodes restarted at once, quorum lost | Wait for the nodes to return; in future, reboot one member at a time |
Key points
- Incus OS updates in A/B: the new image goes to the inactive slot, the switch happens on reboot.
- Three fields track the state:
os_version(active),os_version_alternate(safety net),os_version_next(next boot). - Checks run every 6 hours by default on the stable channel; adjust with a
PUTonsystem/update. auto_reboot: falseis the right default in a cluster: the administrator orchestrates restarts.- You apply a switch with
POST /os/1.0/system/:reboot, targeted at one node. - In a cluster, update one node at a time to preserve the quorum; finish with the leader.
- Rollback is an automatic bootloader safety net, not a manual command.