Starting linux guest instance on incus is often done in a single command, with maybe one or more to configure it. Unfortunately, installing a windows instance is a lot more complex.
Scope
Since the choice of host, incus/windows versions and hardware can dramatically alter the guide, we have to settle for a few core assumptions for the rest of this guide:
The host is running debian 13 (trixie) and incus 7 or later and has a graphical display attached (note that you need to install incus from trixie-backports to get version 7)
The guest instance will run Windows 11
These assumption can easily be altered for your needs, for example you could use a debian13 on a remote host and operate it from a graphics-capable client terminal, or choose to install a different Windows version.
If you do make any changes, carefully check and adjust paths and commands where appropriate. Also mind technical limitations like many incus 6 versions having trouble booting some ISO images over UEFI.
Preparing the host
Since we are using linux debian 13 as the host our default apt packages for incus are stuck at version 6. To get incus 7, we have to first enable the trixie-backports repository:
cat <<EOF
Types: deb
URIs: https://deb.debian.org/debian
Suites: trixie-backports
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF | sudo tee /etc/apt/sources.list.d/trixie-backports.sourcesThen install the incus packages:
sudo apt update #must refresh cache after adding backports
sudo apt install -t trixie-backports incus distrobuilderYou can also install incus-extra if you want tools like incus-migrate on the host for later.
As a last step, the default incus storage pool is limited at 30GiB, which can be too small when using windows. If you are using the default btrfs storage pool created by incus during installation, consider growing it to accomodate the Windows VM and necessary ISO files:
incus storage set default size=100GiBUsing 100GiB leaves enough space to install a plain Windows11 virtual machine, create an image from it and launch a second instance for testing.
Preparing the ISO and drivers
Before installing, we need a Windows 11 ISO image, you can get this for free on the Microsoft website. Windows does not natively support virtual hardware available in incus virtual machines, so you will need the virtio windows drivers to use disks and network interfaces.
The distrobuilder utility is used to bake the necessary drivers directly into the Windows ISO file, so no second driver disc has to be mounted during installation:
sudo distrobuilder repack-windows original-windows11.iso incus-win11.isoThis will add virtio drivers and write the new image to incus-win11.iso. In the following steps, we assume you will only use this newly generated ISO file for installation.
Creating the VM
We start by creating an empty VM instance with adjusted hardware limits and a TPM device:
incus init --empty --vm windows11 -c image.os=windows
incus config device override windows11 root size=55GiB
incus config set windows11 limits.cpu=4 limits.memory=6GiBThe limits above are considered a usable baseline. 4 CPU cores and 6GiB memory will allow Windows to operate moderate workloads while demanding tasks like malware scanning run in the background, and a 55GiB disk leaves roughly 25GiB of usable space for user data (as of writing, Windows 11 takes up roughly 28.5GiB disk space after installation, updates and debloating).
Since Windows 11 requires SecureBoot and a TPM module, we have to add a virtual tpm device:
incus config set windows11 security.secureboot=true
incus config device add windows11 tpm tpmFor other Windows versions that need legacy boot, set security.secureboot=false and security.csm=true for that.
Finally, we need a fake CD drive to mount the ISO file. You could just mount it as a normal disk, but guest instances typically do better with eject/hotplug handling when masking it as a USB device:
incus config device add windows11 cdrom disk \
readonly=true \
boot.priority=10 \
io.bus=usb \
source=/path/to/distrobuilder/incus-win11.isoMake sure to replace the source path with the real ISO output of the previous step's distrobuilder command.
installing windows
If you want to create a local user (not connect to an online Microsoft account) during installation, you have to detach the network adapter before starting the VM:
incus config device override windows11 eth0 attached=falseNow you can start the instance:
incus start windows11 --console=vgaYou should see a new window showing the graphical output of the vm. Pay close attention to the first few seconds of the boot process, because the Windows ISO will prompt "Press any key to boot from CD or DVD" and time out after a few seconds if no input was registered. Press any button while that text is on screen to start the Windows installation process.
If you have trouble hitting keys in time or the graphical window starts late/doesn't register input fast enough, you can instead start the VM and attach the text console:
incus start windows11 --consolePress any button multiple times for a few seconds. The text console won't change, but the boot process will register the key presses.
After a few seconds, press ctrl+a, then q to quit the text console, and return to the graphical console:
incus console windows11 --type=vgaFollow the installation process until it asks to connect to a network. If you want a local-only user account, press shift+F10, enter OOBE\BYPASSNRO and press enter.
The installer will reboot and give you a new option "I don't have internet". Clicking that skips the network setup and creates an offline account.
After installation, return to the host shell and reattach the network interface:
incus config device set windows11 eth0 attached=trueNext steps include typical post-setup tasks like installing updates, disabling fast-startup and running scripts like win11debloat to remove unwanted features and applications.
Installing the incus agent
While not necessary for normal operation, features like incus file and incus exec require a running incus-agent instance inside the guest vm.
Before we can get to the incus agent installation, you have to download the virtio-win.iso containing optional drivers. While distrobuilder had packed drivers needed for installation (drives, network interfaces etc), it did not install the viosock driver required for incus-agent to communicate between host and guest.
Once downloaded, place the virtio-win drive into the "cdrom" drive:
incus config device set windows11 cdrom source=/path/to/virtio-win.isoThen from the windows side, start powershell as administrator and allow script execution, then install the driver:
Set-ExecutionPolicy Unrestricted
pnputil /add-driver "D:\viosock\w11\amd64\viosock.inf" /installWe assume you followed this guide, so your cdrom drive will be letter D: and you need the w11 directory contents. If you added more disks or use a different windows version, adjust the path above accordingly.
Now go back to the host and mount the incus-agent ISO:
incus config set windows11 cdrom source=agent:configIf you did not set the image.os config on the windows11 like we did earlier, mounting will work but the ISO will contain incus-agent files for linux, not Windows. In case you have to set the key now, reboot the instance for it to take effect.
Back in the Windows instance, start another powershell as administrator and run the installer script:
D:\install.ps1
Set-Service -Name Incus-agent -StartupType AutomaticSome incus-agent versions install with Manual start behavior, so we force automatic starting to be sure it is always available when the instance is running.
The agent should now be installed and already running.
Incus file and exec limitations
Now that the incus-agent is installed on the Windows instance, you can use file and exec features from the host - but with a few caveats.
For incus exec, only non-interactive sessions are supported, meaning you should always pass the -T flag:
incus exec windows11 -T -- powershell.exe
# drops into a powershell sessionNote that for cmd or powershell sessions, that means terminal features like command autocompletion when pressing tab or stopping processes with ctrl+c will not work.
For file commands, paths are a common issue. Incus does support either linux style or windows style paths, but your hosts shell will likely interpret backslashes as escape sequences. That means if you want to use windows style paths, you always have to use double backslashes or single quotes for the entire path.
All these 3 paths work as expected:
# these work
incus file create windows11/foo/test.txt
incus file create windows11/foo\\test.txt
incus file create 'windows11/foo\test.txt'This will fail in a bash shell, instead creating C:\footest.txt:
incus file create windows11/foo\test.txtThe reason is that the linux shell (likely bash) interprets the backslash before incus even sees it, stripping it because it is an invalid escape sequence. It could be even less predictable, e.g. if the word after a backslash begins with a valid escape sequence character like 0 (producing \0 nullbyte) or n (producing \n newline) etc.
For these reasons, you should always prefer linux style paths since the host shell understands them natively and does not interpret them as something else, and incus will happily translate them to windows style internally anyway.
If you need to access a specific drive, you can simply prefix the path with a drive letter and a colon for either path style:
incus file create windows11/C:/test.txt
incus file create 'windows11/C:\test.txt'If no drive letter is specified, incus falls back to implicitly using C:.
Finally, when using incus file edit, incus file mount or incus file push and access them with linux tools or text editors, remember that linux uses LF (single newline \n character) for newlines, whereas Windows settled for CR+LF (carriage return \r + newline \n). Linux tooling generally works well with windows style newlines, but may not produce windows-compatible ones when making changes.
For safety, consider running unix2dos to convert newlines on edited files before using them from Windows instances.
Creating a reusable Windows image
Now that the first Windows11 instance has been installed in such a time-consuming manner, it is a good idea to ensure you don't have to do it again. For this purpose, stop before configuring any workloads and turn it into a reusable image.
Ensure you have Windows "fast startup" disabled, power off the instance, then remove the cdrom drive to ensure the resulting image is clean and doesn't depend on local file paths (so you can later transfer it between hosts):
incus stop windows11
incus config device remove windows11 cdromNext, create an image from the instance:
incus publish windows11 --alias win11-baseThe command name "publish" is misleading here - it only creates a local incus image, not available publicly by others (unless you use --public).
Now you have a clean post-install image you can use to create new Windows instances without the manual setup steps:
incus launch win11-base another-windows11 --console=vgaThis will create a new instance named another-windows11 and start it, without any further configuration requirements.