Incus
Overview
This image runs Incus 7.2 on Ubuntu 24.04 LTS. Incus is a system container and virtual machine manager from the LinuxContainers project, the community fork of LXD.
It gives you full Linux systems rather than single-process application containers. Each instance boots its own init, runs its own services and behaves like a small independent machine. Instances share the host kernel, so they start in about a second and cost a fraction of a VM.
The image ships the Incus web UI for managing instances from a browser, and the incus command-line client for everything you would rather script.
No credential ships in this image, and it is locked until first boot. No password, certificate or token of any kind is baked into it. A unique password is generated on your VM specifically by incus-firstboot.service during first boot, and the Incus API's trust store is empty until that runs. There is no default login to find, and no window during which a known credential is live.
What you get:
- Incus 7.2, pinned, from the Zabbly package channel published by the Incus maintainer
- The Incus web UI, served through nginx over HTTPS on port 443, so you can create and manage instances from a browser
- The
incuscommand-line client, working out of the box over the local socket, for everything you would rather script - A ready-to-use
defaultstorage pool and anincusbr0NAT bridge, so you can launch an instance immediately - A unique web UI password generated on this VM at first boot, in a root-only file
- A recorded licence notice and dependency CVE audit on the image itself
Before you start
You need an Azure subscription, and the Azure CLI if you deploy from the command line. Everything below uses azureuser, the default administrator name this image expects.
Sizing. System containers run comfortably on the recommended Standard_B2s (2 vCPU, 4 GiB). Scale up for more instances.
Containers and virtual machines. Incus can run both. System containers work on any VM size, including the recommended B2s, and are what this guide covers.
Incus can also run full virtual machines (incus launch ... --vm). This needs nested virtualisation, which Azure exposes only on Dv3/Ev3 series and later and not on the B series. If you want Incus VMs as well as containers, choose a size that supports nested virtualisation, such as Standard_D4s_v3. On a B-series VM, /dev/kvm is absent and --vm does not work. Containers are unaffected.
Deploy from the Azure Marketplace
- In the Azure portal, search the Marketplace for Incus on Ubuntu 24.04 LTS by SecureAnyCloud and select Create.
- Choose your subscription, resource group and region.
- Set the VM name and size (
Standard_B2sor larger). - Set the administrator username to azureuser and upload your SSH public key.
- Under Inbound port rules, allow SSH (22) and HTTPS (443).
- Select Review + create, then Create.
Do not open port 8443. The Incus API is bound to 127.0.0.1 and is deliberately not reachable from outside the VM. nginx on 443 is the only door it needs.
Deploy from the Azure CLI
Create a resource group, accept the Marketplace terms, create the VM and open port 443:
az group create --name incus-rg --location eastus
az vm image terms accept --urn sac1647283583153:incus-ubuntu-24-04:default:latest
az vm create \
--resource-group incus-rg \
--name incus-vm \
--image sac1647283583153:incus-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
az vm open-port --resource-group incus-rg --name incus-vm --port 443 --priority 1001Get the public IP address:
az vm show -d --resource-group incus-rg --name incus-vm --query publicIps -o tsvRetrieve your unique password
The password is generated on your VM at first boot and written to a root-only file. SSH in and read it:
ssh azureuser@<your-vm-public-ip>
sudo cat /root/incus-credentials.txtThe file shows the URL, the username admin, and the password generated for this VM. While you are connected, confirm that the three services are running:
systemctl is-active incus incus-firstboot nginxIf the web UI does not answer, first boot has not finished. nginx cannot start until incus-firstboot.service has successfully generated this VM's password and certificate. This is intentional: the appliance fails closed rather than ever serving without authentication. Give it a moment after first boot, then check systemctl status incus-firstboot.
Sign in to the web UI
Open https://<your-vm-public-ip>/ in a browser and sign in as admin with the password from the file above.
Your browser warns about the certificate. The image generates a self-signed certificate for your VM's own IP at first boot, because it cannot know your domain name in advance. The warning is expected. Add your own domain and a trusted certificate when you are ready, as shown in the last domain section of this guide.
The Incus UI opens on Instances. A fresh VM starts with none, so the list is empty until you launch one.
Launch your first container
From the VM, launch a system container. Incus pulls the image from the LinuxContainers image server the first time and caches it:
sudo incus launch images:debian/12 app-01Why sudo. Incus grants full control to members of the incus-admin group. This image does not add your administrator account to that group, because that would hand anyone who reaches the account complete control of every instance. Run incus with sudo. Alternatively, add your own account deliberately with sudo usermod -aG incus-admin $USER, then log out and back in.
Check the instance and the storage pool:
sudo incus list
sudo incus storage listThe container also appears in the web UI immediately, with its image, status and address.
Get a shell inside it from the command line:
sudo incus exec app-01 -- bashYou can also open a shell from the Terminal tab in the web UI.
A note on Alpine images. The web UI's Terminal tab opens a bash login shell. Alpine ships BusyBox ash and has no bash at all, so the Terminal tab fails to attach on an Alpine container. Use the Console tab instead, run sudo incus exec <name> -- sh from the command line, or install bash inside the container.
These lifecycle commands are useful:
sudo incus stop app-01
sudo incus start app-01
sudo incus restart app-01
sudo incus info app-01When you are finished with an instance, remove it. The -f flag stops it first. Leave app-01 running if you intend to follow the networking section below:
sudo incus delete -f <instance-name>Storage
The image ships a default storage pool that uses the dir backend. It stores instances as plain directories on your VM's own OS disk. It works on every VM size, needs no kernel module, and grows with whatever disk you attach.
The dir backend does not support pool-level snapshots or quotas. If you want those, add a second pool on a dedicated data disk. For example, after attaching a data disk as /dev/sdc:
sudo apt-get install -y zfsutils-linux
sudo incus storage create fast zfs source=/dev/sdc
sudo incus launch images:debian/12 db-01 --storage fastBoth pools coexist. The default pool stays as it is, and you choose a pool per instance.
Networking
Instances attach to the managed incusbr0 bridge and get a private address with NAT egress. They can reach the internet while remaining unreachable from outside the VM.
To publish a service from a container, forward a port from the VM to the instance:
sudo incus config device add app-01 http proxy \
listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80Then open that port in your network security group:
az vm open-port --resource-group incus-rg --name incus-vm --port 8080 --priority 1002Open only the ports you intend to publish, and never open 8443.
How this image is secured
This image is deliberately different from a stock Incus install, and the differences are worth understanding.
Incus authenticates its remote API with TLS client certificates, not passwords. Exposing that API to a browser means meeting a certificate prompt with no certificate to offer. So this image does not expose the API at all:
- The Incus API listens on
127.0.0.1:8443only and is never reachable from the network. - nginx on 443 is the only door. It terminates TLS and gates access with HTTP Basic Auth, using your VM's unique password.
- nginx authenticates to the API with a TLS client certificate that first boot generated on your VM and added to the trust store. It is the only entry in the trust store.
- Your SSH key is the root of trust. The password is readable only through
sudoon a VM that only your key opens.
You can verify all of this on your own VM:
sudo ss -tlnp | grep -E '8443|:443|:80 ' curl -sk -o /dev/null -w 'unauthenticated: HTTP %{http_code}\n' https://localhost/ui/To change the web UI password at any time:
sudo htpasswd -B /var/lib/sac/config/sac-incus/htpasswd admin
sudo systemctl reload nginxLicence and dependency audit
The image records what it ships and under which licence, verified at build time against the real upstream artifacts. Read the notice and the audit:
head -18 /usr/share/doc/incus-sac/LICENCE-NOTICE.txt head -8 /usr/share/doc/incus-sac/cve-audit.txtIncus core is Apache-2.0. The Incus web UI is GPL-3.0, conveyed verbatim and unmodified. The notice on the image names the exact upstream tag where the corresponding source is published, together with a written offer to supply it. Nothing in this image or SecureAnyCloud's support terms restricts the rights those licences grant you.
Add your own domain and certificate
To serve the web UI under a real name with a trusted certificate:
- Point a DNS
Arecord at your VM's public IP. - Install a certificate with Certbot:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d incus.example.comCertbot edits the nginx site in place and sets up renewal. Your password and the API binding are unchanged.
Keeping the image up to date
The OS receives security updates automatically through unattended-upgrades, and no package is held back, so security fixes are never blocked, including for Incus itself.
Incus is installed from the Zabbly package channel, a third-party origin that unattended-upgrades does not auto-install from. Your appliance therefore stays on the release this image shipped until you upgrade it deliberately:
sudo apt-get update && sudo apt-get install -y incus incus-ui-canonicalReview the Incus release notes first, and snapshot the VM before a major version jump.
Troubleshooting
The web UI does not load. Check systemctl is-active incus incus-firstboot nginx. If nginx is inactive or failed, first boot has not completed successfully, and nginx will not start without this VM's generated password and certificate. Check journalctl -u incus-firstboot.
I get a 401 and my password is correct. Confirm you are signing in as admin, and read the password again with sudo cat /root/incus-credentials.txt.
The Terminal tab will not attach. On Alpine containers this is expected, as they have no bash. Use the Console tab or sudo incus exec <name> -- sh.
incus launch says the image cannot be found. The LinuxContainers image server retires old releases. Run sudo incus image list images: | grep <distro> to see what is currently published and use a current alias.
incus launch --vm fails. Full VMs need nested virtualisation. Check with ls /dev/kvm. If it is absent, your VM size does not support nested virtualisation. Use a Dv3/Ev3 or later size, or use system containers.
Support
This image is covered by SecureAnyCloud support. Email sales@secureanycloud.com.
- Incus documentation
- SecureAnyCloud guides
Deploy on Azure
Launch Incus on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com