Skip to content
Français
Conteneurs & Orchestration medium

Incus OS: A/B updates and rollback

25 min de lecture

Read this page in French

incus logo

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:

Fenêtre de terminal
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:

Fenêtre de terminal
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:

Fenêtre de terminal
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:

Fenêtre de terminal
incus query -X POST node3:/os/1.0/system/update/:check

The 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:

Fenêtre de terminal
incus query -X POST node3:/os/1.0/system/:reboot

The 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.

FieldBefore rebootAfter reboot
os_version (active)202607010319202607011621
os_version_alternate (inactive)202607011621202607010319
needs_reboottruefalse

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.

  1. Check the overall state before starting. Every node should be ONLINE and report needs_reboot: true, meaning the update is ready.

    Fenêtre de terminal
    incus cluster list node1:
  2. 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/:reboot

    While that node is unavailable, the other two hold the quorum and the service keeps running.

  3. 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_version
    incus cluster list node1:
  4. Move to the next node and repeat. Finish with the database leader: when it restarts, another node automatically takes the database-leader role.

    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

SymptomLikely causeFix
needs_reboot stays false after a checkNo newer version on the channel, or check_frequency set to neverCheck config.channel and force a manual :check
The PUT on system/update returns invalid update check frequencyIncomplete body, empty check_frequencySend a complete config object with all three fields
incus query nodeX: fails with not authorizedClient certificate not trusted on that nodeRestore trust, see Incus OS without a shell
Several cluster nodes go OFFLINEToo many nodes restarted at once, quorum lostWait 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 PUT on system/update.
  • auto_reboot: false is 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.

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