Incus
The Incus extension adds the Incus daemon, CLI, and web UI to a Frostyard bootc image, with its own QEMU for virtual machines. Use it to run system containers and virtual machines on the host. Every image carries the extension’s definition, switched off. The extension itself is not in the image: Updex turns it on and downloads it with one command.
Before you start
Section titled “Before you start”You need:
- A Frostyard bootc image. Every image built on Base has
systemd-sysextandfrostyard-updex. - Root access to enable, update, and remove the extension. Checks that change
nothing run without
sudo. - Network access to
https://repository.frostyard.org/ext/incus/. - Free space in
/varfor the extension and for the storage your containers and virtual machines will use. - CPU virtualization switched on in the firmware, if you want virtual machines. The extension brings QEMU and OVMF; the firmware settings are up to you.
- A backup or export of any existing containers, virtual machines, and storage pools.
Incus has no published test results on Frostyard images yet. Try it on a machine you can spare first.
Choose Incus
Section titled “Choose Incus”Pick Incus for system containers and virtual machines. Incus runs as a system service: it creates and runs workloads, manages storage and networks, and listens on a local socket. Pick Docker for application containers instead.
Docker and Incus use different daemons and sockets. Running both on one host is not a supported setup; test your exact combination before you rely on it.
The extension works on Floe, the server image, as well as on the desktops. It carries the graphical libraries that Floe does not have, and each build checks that they are present.
The extension does not give you a package manager, and it does not replace an Incus you installed some other way.
Enable Incus
Section titled “Enable Incus”See what Updex found:
updex componentsupdex features list --component=incusincus appears as a component, with its feature disabled.
Enable Incus and download it now:
sudo updex features enable incus --nowUpdex downloads the image, checks its checksum and signature, links it, and
refreshes the merged extensions. A missing or bad signature stops the command;
Updex never installs an unsigned image. Without --now, Updex only marks the
feature enabled, and the next update downloads it.
Enabling writes one file in /etc:
/etc/sysupdate.incus.d/incus.feature.d/00-updex.conf.
Check that Incus is merged and its services are up:
systemd-sysext statussystemctl status incus.socket incus-startup.service incus-sysext-setup.servicesystemd-sysext status lists incus. Check the socket and the setup service
first. incus.service starts on demand through incus.socket.
incus-sysext-setup.service gives root a subordinate UID and GID range with
usermod, so user namespaces work without a manual step. It runs after
systemd-sysusers and systemd-tmpfiles, and before the Incus sockets and
incus-startup.service.
A merge does not prove that setup completed. At boot, reload-sysext.service
runs systemd-sysusers, systemd-tmpfiles --create --graceful, a
systemd-sysctl replay of the inotify, fs.aio-max-nr, and kernel.keys
limits, and systemctl daemon-reload after the merge. A refresh by Updex v2.1.3
and later also runs systemd-sysusers and systemd-tmpfiles --create. After an
interactive --now merge, the systemd-sysctl replay waits for the next boot,
and systemd may not have loaded the new units yet. If the rdma group, the
files in /etc, or incus-sysext-setup.service are missing, reboot and check
again.
If enable --now exits with a refresh error (refresh_error in JSON), the
download worked, but the activation did not. If the output says
activation did not finish, the extension is merged, but users and groups can
be missing: reboot. Otherwise the merge state is unknown: reboot. A
sudo systemd-sysext refresh by hand does not create users and groups. Then run
the two checks above again.
Update
Section titled “Update”See whether an update exists:
updex features check --component=incusThis changes nothing. It shows one row for each enabled feature. A feature
you have not enabled has no row, so a missing row does not mean “up to date”.
If Updex cannot fetch or verify a manifest, that row shows UPDATE=error and
the command exits non-zero. Treat an error row as unknown, not as up to date.
Install the newest version:
sudo updex features update --component=incusUpdex downloads and verifies the new image, switches the link, deletes older
versions, and refreshes the merge. Add --no-vacuum to keep older versions.
To see what would change, without changing anything:
sudo updex --dry-run features update --component=incusThe refresh swaps /usr under the running system, and the Incus daemon can
restart. Stop or migrate containers and virtual machines first if a restart
would hurt them. Your containers, virtual machines, and storage pools live
outside the extension. An update does not touch them, and it does not back
them up. Check your storage pool configuration to see where they are.
Once you enable Incus, it updates on its own: new versions are staged and become
active at the next boot. To opt out for the whole host, run
sudo systemctl mask --now updex-update.timer. See
Automatic updates.
To take one update at the next boot without the timer, run
sudo updex features update --no-refresh --component=incus, then reboot.
Roll back
Section titled “Roll back”Roll back with sudo updex features rollback incus. It takes effect at the next boot. Updex then holds updates until a newer version is published. To stop future updates instead, see below. To remove the extension, use the remove command below. Your containers, virtual machines, and storage pools stay outside the extension either way. See Roll back.
A rollback to an older Incus release can break Incus. After an upgrade, Incus updates its database, and an older Incus can treat that database as invalid. Read Upgrade Incus before you roll back.
Stop updates or remove
Section titled “Stop updates or remove”Stop future updates:
sudo updex features disable incusThis only sets Enabled=false. Incus stays merged, and the link
/var/lib/extensions/incus.raw stays. The next boot merges the version that the link names. LINKED in
updex features list shows it. To go back to the version you run now, roll
back first. Updex and the timer skip it from now on.
Remove Incus now:
sudo updex features disable incus --now --forceThis unmerges all extensions on the host, deletes the Incus image, and merges the rest again. Running containers and virtual machines can stop immediately. Stop or migrate your workloads first, and reboot when the command finishes.
Without --force, Updex refuses while Incus is merged:
Extension incus (version ...) is active. Removing requires --force and a reboot to take effect.
Removal takes away the daemon, the CLI, the web UI, and their units. Your
containers, virtual machines, storage pools, and other data in /var stay.
Manage or delete them yourself. Enabling Incus again does not restart your
workloads or bring back a missing storage pool.
If the refresh at the end fails, the remaining extensions may stay unmerged. Run
sudo systemd-sysext refresh, or reboot. If the output says
activation did not finish, they are merged, but their users and groups can be
missing: reboot.
Special cases
Section titled “Special cases”Incus runs privileged workloads. The extension creates the rdma group, the
runtime directories, and the alternatives links that its tools expect. The
dnsmasq user that owns /run/dnsmasq and /var/lib/misc comes from Base.
The extension copies its default files into /etc only when they are
missing. Your edits to /etc/default/incus or the libvirt configuration
survive reboots and updates. The defaults stay in /usr/share/factory/etc.
The extension carries only the /etc files it names, not a copy of the build
machine’s /etc. The QEMU network helpers, /etc/qemu-ifup and
/etc/qemu-ifdown, come from Base, not from the extension.
The extension ships virt-viewer, the client for
incus console --type=vga. Graphical consoles use the display support in the
bundled QEMU under /usr/incus. Incus always uses its own QEMU in
/usr/incus/bin. Do not replace it with a host package, and do not expect a
change to the host’s qemu-system-x86_64 to affect Incus.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check | Fix |
|---|---|---|
incus launch --vm says virtual machines are not supported |
Run incus info and look for the QEMU driver, for example driver: lxc | qemu |
If only lxc is present, the bundled QEMU runtime is not loading. Check that the extension is merged and that its libraries load (next row). |
| The bundled QEMU fails with a missing shared library | LD_LIBRARY_PATH=/usr/incus/lib/ ldd /usr/incus/bin/qemu-system-x86_64 |
This extension build does not match your Base. Install a corrected extension. Do not patch /usr by hand. |
| The setup service did not run | systemctl status incus-sysext-setup.service and its journal |
The service runs once per boot and skips when /run/incus-sysext-setup.done exists. Check that file. |
incus.socket is inactive |
systemctl status incus.socket and systemd-sysext status |
Check that the extension is merged and the preset applied. After a staged update, refresh or reboot. |
enable --now exits non-zero after the download |
The output shows Error: sysext refresh failed (refresh_error with --json) |
If the output says activation did not finish, reboot. Otherwise reboot too: a refresh by hand does not create the extension’s users and groups. Then check systemd-sysext status. |
| A manifest or signature check fails | updex features check --component=incus |
Check the network and the published manifest. Never turn verification off. |
| Containers or VMs stopped during an update | The journal shows the daemon restart after the /usr refresh |
Drain or migrate workloads before you update by hand. Back up storage first. |
Do not repair the extension with apt. If a merged image is missing files,
the fix is a corrected extension build.
What ships
Section titled “What ships”| Item | What you get |
|---|---|
| Build package requests | incus, incus-extra, incus-ui-canonical, dnsmasq-base, genisoimage, virt-viewer, librsvg2-common |
| Version | The image file name carries the incus version |
| Image file | incus_<version>_<os>_<arch>.raw.zst; .raw.xz, .raw.gz, and .raw also work |
| Download | From https://repository.frostyard.org/ext/incus/, signature required (Verify=true), into /var/lib/extensions.d/ |
| Services | incus.socket, incus-user.socket, incus-lxcfs.service, incus-startup.service, and incus-sysext-setup.service |
| Activation | 40-incus.preset enables the services. 10-incus.conf starts incus-sysext-setup.service, the two sockets, and incus-startup.service at boot. The setup service pulls in incus-lxcfs.service |
| Setup | incus-sysext-setup.service runs a repeatable script once per boot, then writes /run/incus-sysext-setup.done |
| Configuration | Defaults for /etc/default/incus, logrotate, needrestart, libvirt, and the VTE profile scripts |
| Users and directories | The rdma group, plus runtime directories and alternatives links. The dnsmasq user comes from Base |
The active image is the link /var/lib/extensions/incus.raw. It points to
the chosen file in /var/lib/extensions.d/. The transfer file names a second
link, CurrentSymlink=incus.raw, but Updex deletes that one
(/var/lib/extensions.d/incus.raw) when it installs.
The checked catalog snapshot is generated, and its version changes with each
sync. The source builds revision 5 (SYSEXT_REVISION=5). The revision is the
+r number at the end of the version.
The incus package comes from the Frostyard APT repository,
https://repository.frostyard.org/debian/. It is Frostyard’s rebuild of
Zabbly’s package, and it bundles QEMU, OVMF, swtpm, and virtiofsd under
/usr/incus. incusd puts /usr/incus/bin first in its PATH, so it runs
/usr/incus/bin/qemu-system-x86_64, not Debian’s
/usr/bin/qemu-system-x86_64.
The package row is what the build asks for. It does not promise that every
request ships in the image: Base already ships qemu-system-x86,
qemu-utils, and ovmf, and the image holds only what Base does not have.
Since revision 4, the extension ships no Debian QEMU or OVMF. It drops qemu-kvm, qemu-utils,
qemu-system-gui, qemu-system-modules-spice, ovmf, and ipxe-qemu, which
Incus never used. A build guard keeps it so: any extension build that ships
Base’s QEMU, OVMF, or SeaBIOS files fails. Revision 3 images
carried the Debian qemu-kvm, qemu-system-gui,
qemu-system-modules-spice, and ipxe-qemu. These Debian leftovers are
separate from the bundled /usr/incus runtime, which Incus uses.
Each build checks that the GTK3 and media libraries that the bundled QEMU and
virt-viewer need are present. A smaller Base can still break an older Incus image: this has
happened once, and a rebuild restored the libraries.
Updex picks the image that matches your OS version (%w in the file name)
and your architecture (%a). The catalog lists versions, not architectures,
so it does not tell you which architectures exist. A Base with a different
composition can break the image even when the OS version and architecture
match. The build check protects the build, not a host with a different Base.
See also
Section titled “See also”- Snow image
- Docker system extension
- Tailscale system extension
- System extension model
- nbc to bootc migration runbook
- Updex v2.1.3 command reference
Sources
Section titled “Sources”Reviewed 2026-10-27; Updex facts reviewed 2026-10-30. snosi commit
b2858ca0222814c395cf8248db98f3e096d57dc9;
Updex v2.1.3 commit
2661e987faac986f2594d1d9bbbadf1bf3053f5b.
mkosi.images/incus/mkosi.confmkosi.images/incus/mkosi.finalizemkosi.images/incus/required-paths.txtmkosi.images/incus/mkosi.extra/usr/lib/systemd/system-preset/40-incus.presetmkosi.images/incus/mkosi.extra/usr/lib/systemd/system/multi-user.target.d/10-incus.confmkosi.images/incus/mkosi.extra/usr/lib/systemd/system/incus-sysext-setup.servicemkosi.images/incus/mkosi.extra/usr/lib/incus/incus-sysext-setupmkosi.images/incus/mkosi.extra/usr/lib/tmpfiles.d/incus.confmkosi.images/base/mkosi.extra/usr/lib/sysusers.d/dnsmasq.conf(thednsmasquser; the incus copy at the same path never reached the extension, becauseOverlay=yesdrops paths that Base ships: snosi#1111, removed inf83b264)- Murbella’s review (frostyard-org#64)
mkosi.images/incus/mkosi.extra/usr/lib/sysusers.d/rdma.confmkosi.images/base/mkosi.extra/usr/lib/sysupdate.incus.d/incus.featuremkosi.images/base/mkosi.extra/usr/lib/sysupdate.incus.d/incus.transfermkosi.images/base/mkosi.extra/usr/lib/systemd/system/reload-sysext.servicemkosi.images/base/mkosi.confmkosi.sandbox/etc/apt/sources.list.d/frostyard.sourcesshared/sysext/finalize/sysext-no-base-owned.shshared/sysext/base-owned-paths.txtshared/sysext/postoutput/sysext-postoutput.shdocs/design/sysexts.mddocs/nbc-to-bootc-migration.md- Updex PR #445: refuse removal of a merged extension
- Updex v2.1.3
updex/features.go - Updex v2.1.3
README.md - Updex v2.1.3
updex/install.go - Updex v2.1.3
sysext/manager.go - Updex v2.1.3 configuration reference
- Updex v2.1.3 SDK reference
- Updex v2.1.3 ADR 0007: daemon stages, never activates
- Updex v2.1.3 release
- Updex
docs/adr/0019-apply-merged-sysusers-and-tmpfiles-on-a-live-refresh.md(a live refresh runssystemd-sysusersandsystemd-tmpfiles --create; when the last refresh fails, the merge state is unknown; a refresh by hand does not runsystemd-sysusers) - snosi#1189 and snosi#1193 (open on 2026-10-30): a refresh reloads systemd only when a merged extension sets
EXTENSION_RELOAD_MANAGER=1, and this extension does not set it yet src/data/extensions.jsonmkosi.images/base/mkosi.extra/usr/lib/systemd/system-preset/05-updex.presetdocs/adr/0016-automatic-sysext-updates-apply-at-next-boot.md- Incus database after an upgrade: Incus, Upgrade Incus, read 2026-10-24; Ben Sherman’s review of core#162.