Incus

Incus is a next-generation system container and virtual machine manager. It manages LXC containers and QEMU virtual machines through a unified command-line interface and REST API. Instances are persistent, full-system environments with their own init system, networking, and storage, distinct from the application containers that Docker and Podman manage.

Incus originates from a community fork of LXD. In 2023, Canonical transferred LXD development exclusively to its own commercial organisation and removed the project from Linux Containers governance. The Linux Containers community forked the codebase as Incus under the LXC Project umbrella. Incus is FOSS, independently governed, and carries no commercial licensing terms. It represents the correct choice for operators who require system container infrastructure without dependency on Canonical's commercial roadmap.


Installation

Arch Linux

Incus is available in the AUR. Install with an AUR helper:

yay -S incus

Or build manually:

git clone https://aur.archlinux.org/incus.git
cd incus
makepkg -si

Enable and start the service:

sudo systemctl enable --now incus
sudo systemctl enable --now incus-user

Add your user to the incus-admin group:

sudo usermod -aG incus-admin $USER

Log out and back in for group membership to take effect.

Alpine Linux

apk add incus incus-client
rc-update add incusd
rc-service incusd start

Ubuntu / Debian

Canonical's repositories ship LXD, the proprietary fork. Incus requires the Zabbly repository:

curl -fsSL https://pkgs.zabbly.com/key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/zabbly.gpg
echo "deb [signed-by=/etc/apt/keyrings/zabbly.gpg] https://pkgs.zabbly.com/incus/stable $(lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/zabbly-incus-stable.list
sudo apt update
sudo apt install incus
sudo adduser $USER incus-admin

Verify Installation

incus --version
incus info

Initial Configuration

Run the interactive initialisation on first use:

sudo incus admin init

The wizard configures storage pools, networking, clustering, and remote access. For a minimal single-node setup, the following answers produce a functional environment:

Question Recommended Answer
Clustering no
Existing storage pool no
Storage backend dir (safe default) or zfs (recommended)
Remote access over network no (unless required)
Automatic image updates yes
YAML output of config optional

For production use, ZFS provides snapshots, clones, and copy-on-write storage that the dir backend does not. Install ZFS before running incus admin init:

# Arch Linux
sudo pacman -S zfs-utils
sudo modprobe zfs

Core Architecture

Component Definition
Instance A running container or virtual machine managed by Incus.
Image A compressed file-system used as the base for new instances.
Profile A named set of configuration options applied to instances. Multiple profiles stack.
Storage pool A named storage backend (ZFS, Btrfs, LVM, dir) hosting instance disks and images.
Network A managed bridge, VLAN, OVN overlay, or physical interface attached to instances.
Project A namespace isolating instances, images, profiles, and storage. Useful for multi-tenant environments.
Remote A reference to another Incus server or a public image registry.

Images

Incus instances launch from images. Images are distributed via remotes. The default remotes point to Linux Containers image servers.

Listing Remotes

incus remote list
Default Remote Content
images: Community images (Alpine, Arch, Ubuntu, Rocky, Debian, and many others)
ubuntu: Official Ubuntu images
ubuntu-daily: Ubuntu daily builds

Searching for Images

incus image list images:
incus image list images: alpine
incus image list images: arch
incus image list images: | grep -i rocky

Image Properties

Each image listing includes:

Field Meaning
ALIAS Friendly name for the image
FINGERPRINT SHA-256 hash identifying the image uniquely
PUBLIC Whether the image is publicly accessible
DESCRIPTION Distribution, version, and architecture
TYPE CONTAINER or VIRTUAL-MACHINE
SIZE Compressed image size
UPLOAD DATE When the image was cached locally

Managing Local Images

Command Action
incus image list List locally cached images
incus image info {alias} Show image metadata
incus image delete {fingerprint} Remove local image
incus image export {alias} {path} Export image to file
incus image import {path} --alias {name} Import image from file
incus image copy images:{alias} local: --alias {name} Copy remote image to local cache
incus image refresh Update cached images

Publishing Instances as Images

Use incus publish to save a stopped container or virtual machine as a reusable image in your local image list. You can also publish from a snapshot.

Command Action
incus publish {instance} --alias {alias} Create image from instance, assign alias
incus publish {instance}/{snapshot} --alias {alias} Create image from a snapshot
incus publish {instance} --public --alias {alias} Publish as a public image (visible to others)
incus publish {instance} --expiry 30d Set image to expire after 30 days
incus publish {instance} --reuse Replace existing image with same alias

Example:

# Publish a stopped container called "web-backup" as "my-website:1.0"
incus publish web-backup --alias my-website:1.0

# Publish from a snapshot
incus publish web-backup/snap0 --alias my-website:snap0

# List your published images
incus image list

The source instance should be stopped unless you use --force, but forcing may produce inconsistent file-systems.

Managing Image Aliases

Aliases give human-friendly names to image fingerprints. Use them to reference images in incus launch, incus copy, etc. without typing the full hash.

Command Action
incus image alias list List all aliases on the local server
incus image alias list images: List aliases on a remote (e.g., images:)
incus image alias create {alias} {fingerprint} Assign an alias to an existing image
incus image alias delete {alias} Remove an alias
incus image alias rename {old} {new} Rename an existing alias

Examples:

# List aliases you have defined locally
incus image alias list

# Create an alias "ubuntu-jammy" for image with fingerprint abc123...
incus image alias create ubuntu-jammy abc123...

# Delete an alias
incus image alias delete my-old-alias

# Rename an alias
incus image alias rename my-website my-website:stable

You can also assign aliases during incus publish, incus copy, or incus image import using the --alias flag. For example:

incus publish my-container --alias my-container:v1
incus image copy local:my-container:v1 images: --alias public-container

Building Custom Images

Incus accepts tarballs in its image format. The minimal structure requires a metadata.yaml and a root file-system tarball.

# build a minimal alpine image for import
mkdir -p rootfs
# populate rootfs with a base alpine system, then:
tar -czf rootfs.tar.gz -C rootfs .
cat > metadata.yaml <<EOF
architecture: x86_64
creation_date: $(date +%s)
properties:
  description: Custom Alpine minimal
  os: Alpine
  release: edge
EOF
tar -czf custom-alpine.tar.gz metadata.yaml rootfs.tar.gz
incus image import custom-alpine.tar.gz --alias custom-alpine

Instances — Containers

Incus containers use LXC and share the host kernel. They provide full system environments with their own init process, networking, and storage. They start in under a second and consume minimal RAM.

Creating and Starting Containers

Command Action
incus launch images:alpine/edge {name} Create and start Alpine container
incus launch images:archlinux {name} Create and start Arch Linux container
incus launch images:ubuntu/24.04 {name} Create and start Ubuntu 24.04 container
incus init images:alpine/edge {name} Create container without starting
incus start {name} Start a stopped container

Container Lifecycle

Command Action
incus stop {name} Stop container gracefully
incus stop {name} --force Stop container immediately
incus restart {name} Restart container
incus pause {name} Freeze container (suspend processes)
incus resume {name} Resume frozen container
incus delete {name} Delete stopped container
incus delete {name} --force Stop and delete container

Listing and Inspecting Containers

Command Action
incus list List all instances
incus list -f compact Compact list format
incus info {name} Full instance details
incus config show {name} Display instance configuration YAML
incus config show {name} --expanded Configuration with all applied profiles

Copying and Moving Containers

Command Action
incus copy {name} {new-name} Copy container
incus copy {name} {remote}:{new-name} Copy container to remote server
incus move {name} {new-name} Rename container
incus move {name} {remote}:{new-name} Migrate container to remote server

Instances -- Virtual Machines

Incus VMs use QEMU and provide full hardware virtualisation. They run their own kernel and are isolated at the hardware level. They require the --vm flag at creation time.

Creating VMs

Command Action
incus launch images:ubuntu/24.04 {name} --vm Create and start Ubuntu VM
incus launch images:alpine/edge {name} --vm Create and start Alpine VM
incus init images:archlinux {name} --vm Create Arch VM without starting

VM creation downloads a virtual-machine variant of the image. Not all images offer VM variants; filter by type when searching:

incus image list images: type=virtual-machine

VM-Specific Configuration

VMs support hardware-level configuration that containers do not.

Config Key Example Effect
limits.cpu 2 Assign 2 vCPUs
limits.memory 2GiB Assign 2 GiB RAM
security.secureboot false Disable Secure Boot
limits.cpu.allowance 50% CPU time limit
incus config set {name} limits.memory 4GiB
incus config set {name} limits.cpu 2

Accessing VM Console

incus console {name}          # text console
incus console {name} --type=vga  # graphical console (VGA)

Exit the console with Ctrl-a q.


Exec and File Operations

Running Commands in Instances

Command Action
incus exec {name} -- {cmd} Run command in instance
incus exec {name} -- bash Open bash shell
incus exec {name} -- sh Open sh shell (Alpine default)
incus exec {name} -t -- bash Force TTY allocation
incus exec {name} -n -- {cmd} Run without a terminal (non-interactive)
incus exec {name} --env KEY=VALUE -- {cmd} Run with environment variable
incus exec {name} --user 1000 -- {cmd} Run as UID 1000
incus exec {name} --cwd /opt -- {cmd} Run in specific directory

The -- separator is required when the command takes its own flags.

File Transfer

Command Action
incus file push {local} {name}/{remote} Copy file into instance
incus file pull {name}/{remote} {local} Copy file out of instance
incus file push -r {local-dir} {name}/{remote-dir} Recursively copy directory in
incus file pull -r {name}/{remote-dir} {local-dir} Recursively copy directory out
incus file edit {name}/{remote} Edit remote file in local $EDITOR
incus file delete {name}/{remote} Delete file in instance
incus file mount {name}/{remote} {mountpoint} Mount instance path locally via SFTP

Profiles

Profiles store reusable configuration fragments. Every instance applies the default profile unless specified otherwise. Multiple profiles stack: later profiles override earlier ones.

Managing Profiles

Command Action
incus profile list List all profiles
incus profile show {name} Display profile configuration
incus profile create {name} Create empty profile
incus profile edit {name} Edit profile YAML in $EDITOR
incus profile delete {name} Delete profile
incus profile copy {name} {new-name} Copy profile

Applying Profiles to Instances

Command Action
incus launch {image} {name} --profile {profile} Launch with specific profile
incus launch {image} {name} -p default -p {profile} Launch with stacked profiles
incus profile add {name} {profile} Add profile to running instance
incus profile remove {name} {profile} Remove profile from instance

Example Profile: Web Server

# create and populate the profile
incus profile create webserver
incus profile edit webserver
config:
  limits.cpu: "2"
  limits.memory: 512MiB
  environment.DEBIAN_FRONTEND: noninteractive
description: web server baseline
devices:
  eth0:
    name: eth0
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    size: 10GiB
    type: disk
  http:
    connect: tcp:127.0.0.1:80
    listen: tcp:0.0.0.0:80
    type: proxy
  https:
    connect: tcp:127.0.0.1:443
    listen: tcp:0.0.0.0:443
    type: proxy
name: webserver

Example Profile: High-Density Container (Low RAM)

config:
  limits.cpu: "1"
  limits.memory: 64MiB
  limits.memory.swap: "false"
description: minimal alpine microservice
devices:
  eth0:
    name: eth0
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    size: 2GiB
    type: disk
name: micro

Storage

Storage Backends

Backend Characteristics
dir Simple directory-based storage. No copy-on-write. Suitable for testing.
zfs Full ZFS support: snapshots, clones, copy-on-write, compression. Recommended for production.
btrfs Btrfs subvolumes and snapshots. Good alternative to ZFS on systems without ZFS support.
lvm LVM logical volumes. Block-level storage without file-system-level features.
ceph Distributed Ceph storage. Required for clustered Incus deployments.

Managing Storage Pools

Command Action
incus storage list List storage pools
incus storage info {pool} Pool details including usage
incus storage create {name} zfs Create ZFS pool (auto-allocate)
incus storage create {name} zfs source={device} Create ZFS pool on device
incus storage delete {name} Delete pool
incus storage show {name} Show pool configuration
incus storage edit {name} Edit pool configuration

Storage Volumes

Custom storage volumes provide persistent storage independent of instance lifecycle.

Command Action
incus storage volume list {pool} List volumes in pool
incus storage volume create {pool} {name} Create custom volume
incus storage volume delete {pool} {name} Delete volume
incus storage volume attach {pool} {volume} {instance} {mountpoint} Attach volume to instance
incus storage volume detach {pool} {volume} {instance} Detach volume
incus storage volume snapshot {pool} {name} Snapshot a volume
incus storage volume copy {pool}/{name} {pool}/{new-name} Copy volume
incus storage volume move {pool}/{name} {pool}/{new-name} Move or rename volume

Configuring Instance Disk Size

# set root disk size on a running instance
incus config device set {name} root size 20GiB

# set during launch via profile or direct device config
incus launch images:alpine/edge {name} --device root,size=10GiB

Resizing VM Root Disk

Increasing the root disk size of a virtual machine requires two steps: resizing the virtual disk on the Incus host, then expanding the partition and filesystem inside the VM.

Command Action
incus stop {vm} Stop the VM before resizing
incus config device override {vm} root size={newSize} Resize the virtual disk (e.g., 50GiB)
incus start {vm} Start the VM
incus exec {vm} -- sudo apt install cloud-guest-utils e2fsprogs Install growpart and resize2fs inside VM
incus exec {vm} -- sudo growpart /dev/sda {partition} Expand the partition (usually 2)
incus exec {vm} -- sudo resize2fs /dev/sda{partition} Expand the filesystem
incus exec {vm} -- df -h / Verify new size

Step-by-step example:

# On the Incus host
incus stop {instance}
incus config device override {instance} root size=50GiB
incus start {instance}

# Inside the VM (or via incus exec)
incus exec {instance} -- sudo apt update
incus exec {instance} -- sudo apt install -y cloud-guest-utils e2fsprogs
incus exec {instance} -- sudo lsblk   # identify the root partition (usually /dev/sda2)
incus exec {instance} -- sudo growpart /dev/sda 2
incus exec {instance} -- sudo resize2fs /dev/sda2
incus exec {instance} -- df -h /

Notes:

  • The root partition is typically the second partition (/dev/sda2) on standard Ubuntu cloud images. Verify with lsblk before running growpart.
  • For non-Ubuntu VMs, replace apt with the appropriate package manager (e.g., apk add cloud-utils on Alpine, pacman -S cloud-guest-utils on Arch).
  • If you have never overridden the root disk before, incus config device set {vm} root size={newSize} works as well.

Networking

Default Network

incus admin init creates a bridge incusbr0 by default. Containers attached to this bridge receive DHCP addresses from an integrated dnsmasq instance and share the host's internet connection via NAT.

Listing and Inspecting Networks

Command Action
incus network list List all networks
incus network info {name} Network details and lease table
incus network show {name} Network configuration YAML
incus network edit {name} Edit network configuration

Creating Networks

Command Action
incus network create {name} Create bridge network
incus network create {name} ipv4.address=10.10.0.1/24 ipv4.nat=true Create with specific subnet
incus network delete {name} Delete network

Attaching Networks to Instances

# add nic device during launch
incus launch images:alpine/edge {name} --network incusbr0

# add nic device to existing instance
incus config device add {name} eth0 nic network=incusbr0

# remove nic device
incus config device remove {name} eth0

Port Forwarding (Proxy Devices)

Proxy devices forward traffic from host ports to instance ports. This is the standard method for exposing services.

# expose instance port 80 on host port 8080
incus config device add {name} proxy-http proxy \
  listen=tcp:0.0.0.0:8080 \
  connect=tcp:127.0.0.1:80

# expose instance port 443
incus config device add {name} proxy-https proxy \
  listen=tcp:0.0.0.0:443 \
  connect=tcp:127.0.0.1:443

# remove proxy device
incus config device remove {name} proxy-http

Static IP Assignment

# assign a static DHCP lease to an instance by MAC address
incus network set incusbr0 raw.dnsmasq "dhcp-host=aa:bb:cc:dd:ee:ff,10.10.0.100"

# set a static IP directly in the instance NIC configuration
incus config device set {name} eth0 ipv4.address 10.10.0.100

ACL and Firewall Rules

Incus supports network ACLs for instance-level traffic control:

# create an ACL
incus network acl create webacl

# edit ACL rules
incus network acl edit webacl

# apply ACL to a NIC device
incus config device set {name} eth0 security.acls webacl

Resource Limits

Resource limits apply per-instance or via profiles.

CPU

Command Limit Type
incus config set {name} limits.cpu 2 Pin to 2 CPUs
incus config set {name} limits.cpu 0-3 Pin to CPU cores 0 through 3
incus config set {name} limits.cpu.allowance 50% Allow 50% of a single CPU
incus config set {name} limits.cpu.priority 5 Set scheduler priority (0-10)

Memory

Command Limit Type
incus config set {name} limits.memory 512MiB Hard memory limit
incus config set {name} limits.memory.swap false Disable swap
incus config set {name} limits.memory.swap.priority 5 Swap priority (0-10)

Disk I/O

Command Limit Type
incus config set {name} limits.disk.priority 5 I/O scheduler priority (0-10)
incus config device set {name} root limits.read 100MB Read throughput limit
incus config device set {name} root limits.write 100MB Write throughput limit
incus config device set {name} root limits.read 500iops Read IOPS limit
incus config device set {name} root limits.write 500iops Write IOPS limit

Network I/O

incus config device set {name} eth0 limits.ingress 100Mbit
incus config device set {name} eth0 limits.egress 50Mbit

Snapshots and Backups

Snapshots

Snapshots capture instance state at a point in time. On ZFS or Btrfs backends, they are instant and space-efficient.

Command Action
incus snapshot {name} Create snapshot with auto-generated name
incus snapshot {name} {snap-name} Create named snapshot
incus snapshot {name} {snap-name} --stateful Snapshot including RAM state (containers only)
incus restore {name} {snap-name} Restore instance to snapshot
incus delete {name}/{snap-name} Delete snapshot
incus list {name}/ List snapshots of instance
incus info {name} View snapshots in instance info
incus copy {name}/{snap-name} {new-name} Create new instance from snapshot

Scheduled Snapshots

# create a snapshot every hour
incus config set {name} snapshots.schedule "0 * * * *"

# retain a maximum of 24 snapshots
incus config set {name} snapshots.expiry 24h

# use a naming pattern
incus config set {name} snapshots.pattern "snap-%d"

Backups

Incus exports instances to compressed tarballs for off-instance backup.

Command Action
incus export {name} {name}.tar.gz Export instance to archive
incus export {name} {name}.tar.gz --optimized-storage Export with storage-optimised format (ZFS send)
incus import {name}.tar.gz Import instance from archive
incus import {name}.tar.gz --storage {pool} Import to specific storage pool

Remote Operations

Incus remotes enable management of multiple Incus servers from a single client and image distribution.

Managing Remotes

Command Action
incus remote list List all configured remotes
incus remote add {name} {url} Add a remote server
incus remote remove {name} Remove a remote
incus remote set-default {name} Set default remote
incus remote get-default Show current default remote

Enabling Remote Access on a Server

# on the target server, enable remote access
incus config set core.https_address :8443

# generate or view the certificate fingerprint
incus info | grep fingerprint

# on the client, add the remote
incus remote add myserver https://server-ip:8443

Authentication uses certificate trust. On first connection, the client presents its certificate and the server administrator trusts it via:

incus config trust list
incus config trust add {fingerprint}

Cross-Remote Operations

Command Action
incus launch {remote}:{image} {name} Launch from remote image server
incus copy {name} {remote}:{new-name} Copy instance to remote server
incus move {name} {remote}:{new-name} Migrate instance to remote server
incus exec {remote}:{name} -- {cmd} Execute command on remote instance
incus file push {local} {remote}:{name}/{path} Push file to remote instance

Configuration Reference

Common Instance Config Keys

Key Type Description
limits.cpu string CPU count or range
limits.memory string Memory limit (e.g. 512MiB)
limits.memory.swap bool Allow swap
environment.* string Environment variables
boot.autostart bool Start on host boot
boot.autostart.priority int Boot order (higher starts first)
boot.autostart.delay int Seconds to wait after starting
security.privileged bool Run as privileged container
security.nesting bool Allow nested virtualisation
snapshots.schedule string Cron schedule for auto-snapshots
snapshots.expiry string Duration before snapshot deletion
user.* string Custom metadata (free-form)

Editing Instance Configuration

# set a single key
incus config set {name} {key} {value}

# unset a key
incus config unset {name} {key}

# edit full YAML configuration
incus config edit {name}

# view expanded config including all profile values
incus config show {name} --expanded

Device Types Reference

Device Type Use Case
nic Network interface
disk Storage volume or host path mount
proxy Port forwarding
gpu GPU passthrough
usb USB device passthrough
unix-char Character device
unix-block Block device
tpm TPM chip
pci PCI device passthrough

Cheatsheet

Images

Command Action
incus image list images: Browse remote images
incus image list images: alpine Filter by name
incus image list Local image cache
incus image delete {fp} Remove local image
incus image refresh Update cached images
incus publish {instance} --alias {name} Save instance as image
incus publish {instance}/{snap} --alias {name} Save snapshot as image
incus image alias list List image aliases
incus image alias create {alias} {fp} Assign alias to image
incus image alias delete {alias} Remove alias
incus image alias rename {old} {new} Rename alias

Container Lifecycle

Command Action
incus launch images:{distro} {name} Create and start
incus init images:{distro} {name} Create only
incus start {name} Start
incus stop {name} Stop gracefully
incus stop {name} --force Stop immediately
incus restart {name} Restart
incus delete {name} Delete
incus delete {name} --force Stop and delete
incus list List all instances
incus info {name} Instance details

Shell Access

Command Action
incus exec {name} -- bash Bash shell
incus exec {name} -- sh Sh shell
incus exec {name} -t -- bash Force TTY
incus console {name} Console (VMs)

File Operations

Command Action
incus file push {src} {name}/{dst} Copy in
incus file pull {name}/{src} {dst} Copy out
incus file push -r {dir} {name}/{dst} Recursive copy in
incus file edit {name}/{path} Edit remote file
incus file mount {name}/{path} {local} Mount via SFTP

Networking

Command Action
incus network list List networks
incus network info incusbr0 Network details and leases
incus config device add {name} eth0 nic network=incusbr0 Add NIC
incus config device add {name} web proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80 Port forward

Storage

Command Action
incus storage list List pools
incus storage info {pool} Pool details
incus storage volume list {pool} List volumes
incus storage volume create {pool} {name} Create volume
incus storage volume attach {pool} {vol} {inst} {path} Attach volume
incus stop {vm} Stop VM before resize
incus config device override {vm} root size={size} Resize virtual disk
incus exec {vm} -- sudo growpart /dev/sda {part} Expand partition (VM)
incus exec {vm} -- sudo resize2fs /dev/sda{part} Expand filesystem (VM)

Snapshots and Backups

Command Action
incus snapshot {name} {snap} Create snapshot
incus restore {name} {snap} Restore snapshot
incus delete {name}/{snap} Delete snapshot
incus export {name} {name}.tar.gz Export to archive
incus import {name}.tar.gz Import from archive

Profiles

Command Action
incus profile list List profiles
incus profile show {name} View profile
incus profile create {name} Create profile
incus profile edit {name} Edit profile
incus profile add {instance} {profile} Apply profile
incus profile remove {instance} {profile} Remove profile

Configuration

Command Action
incus config set {name} {key} {value} Set instance option
incus config unset {name} {key} Remove instance option
incus config edit {name} Full YAML edit
incus config show {name} --expanded Show with profiles merged
incus config device add {name} {dev} {type} {k=v} Add device
incus config device remove {name} {dev} Remove device

Remotes

Command Action
incus remote list List remotes
incus remote add {name} {url} Add remote
incus copy {name} {remote}:{new} Copy to remote
incus move {name} {remote}:{new} Migrate to remote