Incus 7.6
Overview
This guide covers launching and using the Incus 7.6 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened MeshCentral & Helpdesk and Project Management AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
Incus is a system container and virtual machine manager from the LinuxContainers project, the community fork of LXD. A system container is a full Linux system rather than a single process. It boots its own init, runs its own services and behaves like a small independent server. It shares the host kernel, so it starts in about a second.
You manage instances from the Incus web UI in a browser or with the incus command line client.
What you get:
- HTTPS and a password from the first boot. nginx serves the Incus web UI and API on port 443 with a certificate generated on your instance's first boot. HTTP Basic Auth protects it with a password generated on your instance and written to
/root/incus-credentials.txt. Port 80 redirects to HTTPS. - The Incus API is never exposed. Incus listens on
127.0.0.1:8443only. nginx is the only way in. It authenticates to the API with a TLS client certificate minted on your instance at first boot, which is the only entry in the Incus trust store. Only root can read its private key. - Fail closed. No password, certificate or key is baked into the image. nginx refuses to start until first boot has written your instance's own password, server certificate and client certificate.
- Pinned to your instance's address. nginx answers only to the address first boot found for your instance (its public IPv4, else its private IPv4) and drops requests for any other host name. Cross-site requests and WebSocket connections from another origin are refused. Run
sudo incus-set-hostto move it to an Elastic IP or a DNS name. - Metadata service blocked. The AMI requires IMDSv2, and Incus, nginx and every container you run are blocked from the instance metadata service, so a workload inside a container cannot read your instance's IAM role credentials.
- Ready to use. A
defaultstorage pool and anincusbr0NAT bridge are configured, so you can launch a container straight away. - A fully patched base. Ubuntu 24.04 LTS is fully patched at build time, with unattended security upgrades switched on, a recorded licence notice and a dependency CVE audit on the image.
- Software on the image: Incus 7.6 (the
incusandincus-ui-canonicalpackages from the Zabbly repository published by the Incus maintainer, pinned and verified against the repository's signing key), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Containers, not virtual machines, on most instance types. Incus can also run full virtual machines, but that needs hardware virtualisation (/dev/kvm). EC2 exposes it only on bare metal instance types (*.metal). On every other type, including the recommended m5.large, this image runs system containers, and incus launch --vm is refused. See Step 9.
The default SSH user is ubuntu. The architecture is x86_64, and the recommended instance type is m5.large.
Prerequisites
- An AWS account subscribed to the listing.
- An EC2 key pair.
- A VPC subnet with a route to the internet. Containers download their images from the LinuxContainers image server.
- A security group that allows TCP 22 (SSH), TCP 443 (the web UI and API) and TCP 80 (redirects to HTTPS) from your own networks only.
Never open port 8443. The Incus API is bound to 127.0.0.1 and is not reachable from outside the instance by design. nginx on 443 is the only door it needs.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Incus 7.6 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.
Choose m5.large (2 vCPU, 8 GiB) or larger for more containers. Choose your key pair and a security group as described above.
Containers are stored on the root volume (16 GiB by default). If you plan to run many containers, give the root volume more space at launch, or add a second EBS volume as a separate storage pool (Step 7).
Connect to your instance
Connect over SSH with your key pair. For Incus 7.6 on Ubuntu 24.04, the SSH user is ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. After it finishes, the four units are active, and the client and server both report 7.6:
systemctl is-active incus incus-firstboot nginx sac-incus-imds-guard
sudo incus versionactive active active active Client version: 7.6 Server version: 7.6The Incus API listens on loopback only. nginx serves ports 80 and 443:
sudo ss -tlnp | awk 'NR==1 || /incusd|nginx|sshd/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'State Local Peer LISTEN 127.0.0.1:8443 users:(("incusd")) LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:22 users:(("sshd"),("systemd")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))Get your URL and password
sudo cat /root/incus-credentials.txtOnly root can read the file. It holds the address the web UI is served as (your instance's public IP address by default), the username, the password generated on this instance and the SHA-256 fingerprint of the instance's certificate:
INCUS_UI_URL=https://<public-ip>/ INCUS_UI_USERNAME=admin INCUS_UI_PASSWORD=<24 letters and digits> TLS_CERT_SHA256=<the fingerprint your browser should show>If the web UI does not answer, first boot has not finished. nginx cannot start until incus-firstboot.service has generated this instance's password and certificates. Check systemctl status incus-firstboot.
Sign in from your browser
Open INCUS_UI_URL in your browser and sign in as admin with the password from the file. The certificate is self-signed and generated on your instance, so your browser warns once. Compare the fingerprint it shows with TLS_CERT_SHA256, or install your own certificate (Step 11).
The Incus UI opens on Instances. A new instance starts with none. The example below shows three containers that have been created:
Launch your first container
From the instance, launch a system container. Incus downloads the image from the LinuxContainers image server the first time and then caches it:
sudo incus launch images:debian/12 app-01Launching app-01Then list your instances:
sudo incus list -c ns4t┌────────┬─────────┬───────────────────────┬───────────┐ │ NAME │ STATE │ IPV4 │ TYPE │ ├────────┼─────────┼───────────────────────┼───────────┤ │ app-01 │ RUNNING │ 10.138.219.166 (eth0) │ CONTAINER │ └────────┴─────────┴───────────────────────┴───────────┘Why sudo. Incus gives full control to members of the incus-admin group. The image does not add ubuntu to that group, because that would give anyone who reaches the account control of every instance without a further step. Run incus with sudo, or deliberately add an account with sudo usermod -aG incus-admin <user>.
Run a command inside it:
sudo incus exec app-01 -- sed -n 1,2p /etc/os-releasePRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux"For an interactive shell, run sudo incus exec app-01 -- bash. The container appears in the web UI at once, with its image, type, address and resource use:
The Terminal tab opens a shell inside the container from your browser:
Alpine containers. The Terminal tab opens bash, which Alpine does not ship. Use the Console tab or sudo incus exec <name> -- sh for Alpine containers.
Other lifecycle commands: sudo incus stop app-01, sudo incus start app-01, sudo incus restart app-01, sudo incus info app-01, and sudo incus delete -f <instance-name> to remove an instance.
Storage
The default storage pool uses the dir backend, which stores each instance as a directory on the root volume. It works on every instance type and needs no kernel module.
sudo incus storage list┌─────────┬────────┬─────────────────────────────────────────────┬─────────┬─────────┐ │ NAME │ DRIVER │ DESCRIPTION │ USED BY │ STATE │ ├─────────┼────────┼─────────────────────────────────────────────┼─────────┼─────────┤ │ default │ dir │ sac default storage pool (dir backend) │ 2 │ CREATED │ └─────────┴────────┴─────────────────────────────────────────────┴─────────┴─────────┘The dir backend has no pool-level snapshots or quotas. For those, attach a second EBS volume to the instance and create a ZFS pool on it. Use the device name that lsblk shows for the new volume, such as /dev/nvme1n1:
lsblk -o NAME,SIZE,MOUNTPOINT
sudo apt-get install -y zfsutils-linux
sudo incus storage create fast zfs source=/dev/nvme1n1
sudo incus launch images:debian/12 db-01 --storage fastBoth pools coexist and you choose one per instance.
Networking
Instances attach to the managed incusbr0 bridge and get a private address with NAT egress, so they can reach the internet while staying unreachable from outside the instance.
sudo incus network listTo publish a service from a container, forward a port from the instance to it:
sudo incus config device add app-01 http proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80
sudo incus config device show app-01Device http added to app-01 http: connect: tcp:127.0.0.1:80 listen: tcp:0.0.0.0:8080 type: proxyThen allow that port in the instance's security group, from your own networks only. For example, use the AWS CLI:
aws ec2 authorize-security-group-ingress --group-id <your-security-group-id> --protocol tcp --port 8080 --cidr <your-mgmt-cidr>Open only the ports you mean to publish, and never 8443.
Virtual machines on EC2
Incus runs full virtual machines with QEMU, which needs hardware virtualisation. EC2 exposes /dev/kvm only on bare metal instance types such as m5.metal or c5.metal. On every other instance type the image runs system containers only, and Incus reports the lxc driver alone:
ls /dev/kvm 2>&1;
sudo incus info | grep -E '^\s+driver:'ls: cannot access '/dev/kvm': No such file or directory driver: lxcOn a *.metal instance type, /dev/kvm is present and sudo incus launch images:debian/12 vm-01 --vm starts a virtual machine. On other types, that command is refused. Containers are unaffected.
Security checks
You can confirm how the image is secured on your own instance. Without the password, nginx answers 401. The Incus API on 8443 trusts nothing that does not present the instance's client certificate:
H=$(sudo sed -n 's/^HOST=//p' /etc/sac/incus.conf)
curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w 'no password: HTTP %{http_code}\n' "https://$H/ui/" curl -sk https://127.0.0.1:8443/1.0 | jq -r '.metadata.auth'
sudo incus config trust list -f csv -c nno password: HTTP 401 untrusted sac-nginx-ui-proxyOnly root can read the client certificate's private key and the server's TLS key. Incus and nginx are denied the instance metadata service:
sudo stat -c '%a %U:%G %n' /var/lib/sac/config/sac-incus/ui-client.key /var/lib/sac/config/sac-incus/nginx-selfsigned.key systemctl show -p IPAddressDeny incus.service nginx.service600 root:root /var/lib/sac/config/sac-incus/ui-client.key 600 root:root /var/lib/sac/config/sac-incus/nginx-selfsigned.key IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32Containers are blocked from the metadata service too, by the sac_imds_guard nftables table, while their normal internet access works:
sudo incus exec -n app-01 -- timeout 5 bash -c 'echo > /dev/tcp/169.254.169.254/80' && echo reachable || echo blocked
sudo incus exec -n app-01 -- timeout 5 bash -c 'echo > /dev/tcp/deb.debian.org/80' && echo reachable || echo blockedblocked reachableTo change the web UI password:
sudo htpasswd -B /var/lib/sac/config/sac-incus/htpasswd admin
sudo systemctl reload nginxTwo optional Incus features run helper programs that this image leaves switched off. Setting acme.domain makes Incus run the bundled lego ACME client, and adding an OCI image remote makes it run the bundled skopeo. Neither has a newer upstream release to move to if an advisory is published against it, so the image configures neither; the build records both binaries in the CVE audit (Step 12). Prefer Certbot on nginx (Step 11) to acme.domain.
Use your own DNS name and certificate
Point the instance at its new address after you attach an Elastic IP, after a stop and start changes the public IP, or when you want to use a DNS name. With no argument, the command uses the current public IP. It writes a new certificate and updates INCUS_UI_URL:
sudo incus-set-host incus.example.comThen, with a DNS A record for the name pointing at the instance, install a trusted certificate with Certbot:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d incus.example.comYour password and the API binding are unchanged.
Licence and dependency audit
The image records what it ships and under which licence, verified at build time against the upstream artifacts, and the result of a vulnerability scan of the shipped filesystem:
head -14 /usr/share/doc/incus-sac/LICENCE-NOTICE.txt sed -n 1,9p /usr/share/doc/incus-sac/cve-audit.txtsac Incus appliance - licence notice ========================================= Verified at build time against the real upstream artifacts, not package metadata or badges. Incus core (packages: incus, incus-base, incus-client) Apache-2.0 Source : https://github.com/lxc/incus @ tag v7.6.0 Artifact verified : COPYING, byte-identical to https://www.apache.org/licenses/LICENSE-2.0.txt sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 Incus web UI (package: incus-ui-canonical) GPL-3.0 Source : https://github.com/zabbly/incus-ui-canonical @ tag incus-0.22.1 (a fork of https://github.com/canonical/lxd-ui) Artifact verified : LICENSE, byte-identical to https://www.gnu.org/licenses/gpl-3.0.txt sha256 3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 sac Incus appliance - dependency CVE audit Scanner : Version: 0.76.2 Scanned : 2026-11-07T12:35:06Z - Trivy rootfs scan of the SHIPPED filesystem OS : ubuntu 24.04 Packages enumerated : 716 (positive control: a scan of nothing FAILS the build) Total findings : 24033 HIGH/CRITICAL, fix available : 0 <-- the build gate; must be 0 HIGH/CRITICAL in lego/skopeo : 0 (d-320 residual: no fixed upstream release; recorded, see below) HIGH/CRITICAL, no fix yet : 2245 (recorded, not gated: no version resolves these)The build fails on any HIGH or CRITICAL finding that has a fixed version available. The findings with no fix yet are all Linux kernel CVEs against the newest kernel Ubuntu publishes, counted once per kernel sub-package. The file lists them as distinct CVE ids.
Incus 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 its corresponding source is published at, with a written offer to supply it. Nothing in this image or SecureAnyCloud's support terms restricts the rights those licences grant you.
Keeping Incus up to date
Ubuntu security updates install automatically through unattended-upgrades, and no package is held back. Incus comes from the Zabbly repository, which unattended-upgrades does not install from, so the instance stays on the Incus release it shipped with until you upgrade it deliberately:
sudo apt-get update && sudo apt-get install -y incus incus-ui-canonicalRead the Incus release notes first, and snapshot the root volume before a major upgrade.
Troubleshooting
The web UI does not load. Check systemctl is-active incus incus-firstboot nginx. If nginx is inactive, first boot has not completed; see journalctl -u incus-firstboot. If the instance's public IP changed after a stop and start, run sudo incus-set-host.
The browser connection is closed with no page. nginx drops requests for host names it does not serve. Browse to the exact INCUS_UI_URL, or run sudo incus-set-host <name-or-ip> for the address you use.
I get a 401 and my password is correct. Sign in as admin, and re-read the password with sudo cat /root/incus-credentials.txt.
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 published and use a current alias.
incus launch --vm fails. Full virtual machines need /dev/kvm, which EC2 provides only on *.metal instance types. Use system containers on other types.
Support and licensing
Covered by SecureAnyCloud support: email sales@secureanycloud.com.
- Incus documentation
- The Hardened MeshCentral & Helpdesk and Project Management AMIs listing guide
- SecureAnyCloud guides
Deploy on AWS
Launch Incus 7.6 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com