Virtualization intermediate ⏱ 45 minutes

Incus: Next-Gen Container and VM Management

LXD fork backed by Linux Containers. Unified management for system containers and VMs with modern features.

By homelabbing.io • 2025-12-16
#incus#lxd#containers#vms#linux-containers

LXD was the gold standard for system containers—lightweight, fast, and efficient. Then Canonical’s licensing changes created uncertainty. The community needed a true open-source alternative without corporate control.

Incus emerged as the answer—a community-driven fork maintaining LXD’s excellence.

Backed by the Linux Containers project and led by LXD’s original creator, Incus preserves everything that made LXD great while ensuring long-term open-source commitment. It’s not just a container runtime—it’s a complete infrastructure platform combining containers and VMs in a unified experience.

Why This Guide Matters

Most container guides focus on Docker. This guide explores Incus:

  • Complete installation and configuration
  • System containers vs. application containers (Docker)
  • VM support alongside containers
  • Profile-based configuration management
  • Advanced networking (bridges, VLANs, OVN)
  • Storage pool optimization
  • Migration from LXD
  • Production-ready deployments

Whether you’re new to system containers or migrating from LXD, this guide provides comprehensive coverage.

What You’ll Master

By the end of this guide, you will:

  • Understand Incus architecture and use cases
  • Install Incus on Ubuntu, Debian, and other distributions
  • Create and manage system containers and VMs
  • Configure profiles for consistent deployments
  • Implement advanced networking (bridges, NAT, routed)
  • Optimize storage pools (ZFS, btrfs, LVM)
  • Migrate from LXD to Incus
  • Automate deployments with cloud-init
  • Integrate with existing infrastructure

Prerequisites

Required Knowledge:

  • Linux command line proficiency
  • Basic networking concepts
  • Understanding of containers vs. VMs
  • Storage concepts (filesystems, volumes)

System Requirements:

Minimum:

  • Linux system (Ubuntu 22.04+, Debian 12+, or compatible)
  • 2GB RAM
  • 20GB available storage
  • Kernel 5.4+ with namespace support

Recommended:

  • 8GB+ RAM
  • 100GB+ storage (SSD preferred)
  • ZFS-compatible storage for advanced features
  • Dedicated network interface (for advanced networking)

Section 1: Understanding Incus

What is Incus?

Incus is a modern infrastructure platform providing:

System Containers (Primary Use Case):

Unlike Docker containers that run single applications, Incus containers are complete Linux systems:

Docker Container:
- Single process (nginx, postgres, etc.)
- Minimal OS (often Alpine Linux)
- Stateless, ephemeral
- Application isolation
- No init system

Incus Container:
- Full Linux distribution (Ubuntu, Debian, Alpine, etc.)
- Complete init system (systemd)
- Multiple processes and services
- Can run Docker inside container
- SSH access
- Persistent state
- System isolation

Virtual Machines (Since Incus 0.1):

Full VMs using QEMU/KVM, managed the same way as containers:

Unified Management:
incus launch ubuntu:22.04 my-container     # Container
incus launch ubuntu:22.04 my-vm --vm       # VM

Same commands, same management interface
Different isolation levels for different needs

Key Features:

  • Unified API for containers and VMs
  • Live migration (containers and VMs)
  • Snapshot and backup management
  • Profile-based configuration
  • Advanced networking (OVN, bridging, routing)
  • Storage pools (ZFS, btrfs, LVM, dir)
  • Resource limits (CPU, memory, disk I/O)
  • Image server (public and private)
  • REST API for automation
  • Cluster support (multiple hosts)

Incus vs. LXD vs. Docker

When to Use Each:

Docker:
✓ Microservices architectures
✓ Application deployment
✓ CI/CD pipelines
✓ Kubernetes workloads
✗ Full system requirements
✗ Multiple services in one container
✗ Traditional server workloads

Incus:
✓ Development environments
✓ Testing different Linux distributions
✓ Running complete services (mail server, web stack)
✓ Multi-service applications
✓ Docker host (run Docker inside Incus)
✓ VM needs alongside containers
✗ Orchestration at scale (use Kubernetes)
✗ Minimal overhead requirements

LXD:
⚠️  Consider Incus instead (fork for open-source future)
  Same features, community-driven development

Architecture Comparison:

Incus/LXD Architecture:

Host OS (Ubuntu/Debian/etc.)
├── Incus Daemon (REST API server)
│   ├── Container Runtime (LXC)
│   │   ├── Namespaces (PID, Network, Mount, UTS, IPC)
│   │   ├── Cgroups (resource limits)
│   │   └── AppArmor/SELinux (security)
│   ├── VM Runtime (QEMU/KVM)
│   ├── Storage Backend (ZFS/btrfs/LVM)
│   ├── Network Manager
│   └── Image Server
└── Containers and VMs

Docker Architecture:

Host OS
├── Docker Daemon
│   ├── containerd (container runtime)
│   ├── runc (OCI runtime)
│   └── Image management
└── Containers (single process each)

Why Incus Over LXD?

Incus Advantages:

  1. True Open Source: Community-driven, no corporate control
  2. Original Creator: Led by Stéphane Graber (LXD creator)
  3. Active Development: Rapid feature additions
  4. Compatibility: Nearly 100% compatible with LXD
  5. Long-term Commitment: Linux Containers project backing

Migration Path: LXD users can migrate seamlessly (covered later in guide)


Section 2: Installing Incus

Installation on Ubuntu 22.04+

Method 1: From Official Repository (Recommended)

# Update system
sudo apt update && sudo apt upgrade -y

# Install Incus from Zabbly repository (official)
sudo mkdir -p /etc/apt/keyrings/
sudo wget -O /etc/apt/keyrings/zabbly.asc https://pkgs.zabbly.com/key.asc

# Add repository
cat <<EOF | sudo tee /etc/apt/sources.list.d/zabbly-incus-stable.sources
Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: $(. /etc/os-release && echo ${VERSION_CODENAME})
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.asc
EOF

# Update package list
sudo apt update

# Install Incus
sudo apt install incus -y

# Verify installation
incus --version
# Output: incus version 0.x

Method 2: Build from Source

# Install build dependencies
sudo apt install -y   acl   autoconf   dnsmasq-base   git   golang   liblxc-dev   libacl1-dev   libcap-dev   libuv1-dev   make   pkg-config   rsync   squashfs-tools   tar   tcl   xz-utils   ebtables

# Clone Incus repository
cd /opt
sudo git clone https://github.com/lxc/incus.git
cd incus

# Build Incus
make deps
make

# Install
sudo make install

# Verify
/usr/local/bin/incus --version

Installation on Debian 12+

# Add backports if needed
echo "deb http://deb.debian.org/debian bookworm-backports main" | sudo tee /etc/apt/sources.list.d/backports.list

# Update
sudo apt update

# Install from Zabbly (same as Ubuntu)
sudo mkdir -p /etc/apt/keyrings/
sudo wget -O /etc/apt/keyrings/zabbly.asc https://pkgs.zabbly.com/key.asc

cat <<EOF | sudo tee /etc/apt/sources.list.d/zabbly-incus-stable.sources
Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: bookworm
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.asc
EOF

sudo apt update
sudo apt install incus -y

Post-Installation Configuration

Initialize Incus:

# Run initialization wizard
sudo incus admin init

# Wizard prompts (recommended answers):

# Would you like to use clustering? No
# (Unless building multi-host cluster)

# Do you want to configure a new storage pool? Yes
# Name: default
# Driver: zfs (recommended) or btrfs/dir
# Create new ZFS pool? Yes
# Use existing block device? /dev/sdb (or create file-backed)
# Size: 50GB (if file-backed)

# Would you like to connect to a MAAS server? No

# Create a new local network bridge? Yes
# Bridge name: incusbr0
# IPv4 address: auto
# IPv4 NAT: Yes
# IPv6 address: auto
# IPv6 NAT: Yes

# Would you like stale cached images to be updated automatically? Yes

# Would you like a YAML "incus admin init" preseed? No

# Configuration complete!

Manual Configuration (Alternative):

# Create storage pool
sudo incus storage create default zfs size=50GB

# Create network bridge
sudo incus network create incusbr0   ipv4.address=10.0.10.1/24   ipv4.nat=true   ipv6.address=fd42:1234:5678::/64   ipv6.nat=true

# Set default profile
sudo incus profile device add default root disk path=/ pool=default
sudo incus profile device add default eth0 nic network=incusbr0

Add User to incus-admin Group:

# Add your user
sudo usermod -aG incus-admin $USER

# Apply group membership
newgrp incus-admin

# Verify access (no sudo needed now)
incus list

Section 3: Creating and Managing Containers

Launching Your First Container

List Available Images:

# List Ubuntu images
incus image list images:ubuntu

# Common aliases:
# ubuntu:22.04 - Ubuntu 22.04 LTS
# ubuntu:24.04 - Ubuntu 24.04 LTS
# debian:12 - Debian Bookworm
# alpine:3.19 - Alpine Linux
# rockylinux:9 - Rocky Linux 9

# List all available distributions
incus image list images: | less

Launch Container:

# Launch Ubuntu 22.04 container
incus launch images:ubuntu/22.04 web-server

# Breakdown:
# images:ubuntu/22.04 - Image from images.linuxcontainers.org
# web-server - Container name

# Container downloads, creates, and starts
# Takes 10-30 seconds on first launch

Interact with Container:

# Execute commands in container
incus exec web-server -- ls /

# Get shell in container
incus exec web-server -- bash

# Or shorthand
incus shell web-server

# Inside container:
root@web-server:~# apt update
root@web-server:~# apt install nginx -y
root@web-server:~# systemctl start nginx
root@web-server:~# exit

# Check container IP
incus list

# Output:
# +------------+---------+---------------------+------+
# | NAME       | STATE   | IPV4                | IPV6 |
# +------------+---------+---------------------+------+
# | web-server | RUNNING | 10.0.10.100 (eth0)  | ...  |
# +------------+---------+---------------------+------+

# Access nginx
curl http://10.0.10.100

Container Lifecycle Management

Container Operations:

# Stop container
incus stop web-server

# Start container
incus start web-server

# Restart container
incus restart web-server

# Pause container (freeze processes)
incus pause web-server

# Resume container
incus resume web-server

# Delete container
incus delete web-server

# Delete running container (force)
incus delete web-server --force

Container Information:

# Show detailed info
incus info web-server

# Output shows:
# - State (running/stopped)
# - Resources (CPU, memory usage)
# - Network info
# - Storage usage
# - Snapshots

# Show configuration
incus config show web-server

# Show running processes
incus exec web-server -- ps aux

# Monitor container resources
incus monitor web-server

File Transfer

Copy Files to/from Container:

# Copy file to container
echo "Hello from host" > test.txt
incus file push test.txt web-server/root/

# Copy file from container
incus file pull web-server/etc/nginx/nginx.conf ./

# Copy directory
incus file push -r /local/directory web-server/root/

# Create file in container
incus exec web-server -- bash -c "echo 'content' > /root/file.txt"

Snapshots and Backups

Create Snapshot:

# Create snapshot
incus snapshot create web-server pre-update

# List snapshots
incus info web-server | grep Snapshots -A 10

# Restore snapshot
incus snapshot restore web-server pre-update

# Delete snapshot
incus snapshot delete web-server pre-update

# Create with description
incus snapshot create web-server backup-20251216 --reuse description="Before major update"

Export/Import Containers:

# Export container to file
incus export web-server web-server-backup.tar.gz

# Import container from file
incus import web-server-backup.tar.gz new-web-server

# Publish container as image
incus publish web-server --alias my-web-server-image

# Launch from custom image
incus launch my-web-server-image web-server-02

Section 4: Creating and Managing VMs

Launching Virtual Machines

Enable VM Support:

# Verify VM support (requires KVM)
incus info | grep "kvm:"

# Should show:
# - kvm: true

# If false, enable virtualization in BIOS

Launch VM:

# Launch Ubuntu 22.04 VM
incus launch images:ubuntu/22.04 test-vm --vm

# Specify resources
incus launch images:ubuntu/22.04 windows-vm --vm   -c limits.cpu=4   -c limits.memory=8GiB

# VM launches and boots (slower than containers)
# Can take 30-60 seconds for first boot

VM-Specific Operations:

# Access VM console
incus console test-vm

# Exit console: Ctrl+a q

# Get VM IP (after boot complete)
incus list

# SSH to VM (after SSH server installed)
# VMs require SSH server installation:
incus exec test-vm -- apt install openssh-server -y

# Access via VNC (if configured)
incus config set test-vm raw.qemu="-vnc 0.0.0.0:0"

VM vs. Container Comparison:

Operation              Container    VM
Boot time              1-2 sec      30-60 sec
Memory overhead        ~10 MB       ~512 MB
Kernel                 Shared       Dedicated
OS support             Linux only   Any OS
Isolation              Strong       Strongest
Performance            Native       Near-native
Windows support        No           Yes
Docker inside?         Yes          Yes

Section 5: Profiles and Configuration

Understanding Profiles

Profiles are templates for container/VM configuration:

Default Profile Structure:

config:
  - CPU limits
  - Memory limits
  - Security policies

devices:
  - Network interfaces
  - Disk mounts
  - USB devices
  - GPU passthrough

Multiple profiles can be applied to single container
Settings merge with later profiles overriding earlier

View Default Profile:

# Show default profile
incus profile show default

# Output:
config: {}
description: Default Incus profile
devices:
  eth0:
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default

Creating Custom Profiles

Example: Web Server Profile

# Create profile
incus profile create webserver

# Configure profile
incus profile edit webserver

# In editor, add:
config:
  limits.cpu: "2"
  limits.memory: 2GiB
  security.nesting: "true"
description: Web server optimized profile
devices:
  eth0:
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
  shared-data:
    path: /var/www/shared
    source: /srv/www-shared
    type: disk
name: webserver

# Save and exit

Apply Profile to Container:

# Launch with custom profile
incus launch images:ubuntu/22.04 web1 -p webserver

# Add profile to existing container
incus profile add web-server webserver

# Remove profile
incus profile remove web-server default

# List container profiles
incus config show web-server | grep profiles -A 5

Example: Development Environment Profile

incus profile create devenv

incus profile edit devenv

# Configuration:
config:
  limits.cpu: "4"
  limits.memory: 8GiB
  security.nesting: "true"  # Allow Docker inside
  security.syscalls.intercept.mknod: "true"
  security.syscalls.intercept.setxattr: "true"
description: Development environment with Docker support
devices:
  eth0:
    network: incusbr0
    type: nic
  root:
    path: /
    pool: default
    size: 50GiB
    type: disk
  projects:
    path: /home/ubuntu/projects
    source: /home/user/projects
    type: disk

Resource Limits

CPU Limits:

# Set CPU limit (number of cores)
incus config set web-server limits.cpu=2

# CPU priority (shares)
incus config set web-server limits.cpu.priority=10

# Pin to specific CPUs
incus config set web-server limits.cpu.allowance="0,1"

Memory Limits:

# Set memory limit
incus config set web-server limits.memory=4GiB

# Memory with swap
incus config set web-server limits.memory.swap=true
incus config set web-server limits.memory.swap.priority=1

# Hard enforcement
incus config set web-server limits.memory.enforce=hard

Disk I/O Limits:

# Limit read throughput
incus config device set web-server root limits.read=20MB

# Limit write throughput
incus config device set web-server root limits.write=10MB

# IOPS limits
incus config device set web-server root limits.read.iops=1000
incus config device set web-server root limits.write.iops=500

Section 6: Advanced Networking

Network Types

1. Bridge Network (Default):

# Create bridge network
incus network create br-production   ipv4.address=10.0.20.1/24   ipv4.nat=true   ipv4.dhcp=true   ipv6.address=none

# Attach container to bridge
incus network attach br-production web-server eth1

# Or set in profile
incus profile device add production-profile eth0 nic   network=br-production

2. Macvlan Network:

Containers get IPs from physical network:

# Create macvlan (parent: physical interface)
incus network create mvlan-external   parent=enp1s0   type=macvlan

# Attach to container
incus config device add web-server eth0 nic   network=mvlan-external   name=eth0

# Container now has IP from physical network DHCP
# Accessible directly from LAN

3. Routed Network:

# Create routed network
incus network create routed-net   ipv4.address=10.0.30.1/24   ipv4.routing=true   ipv4.nat=false   ipv6.address=none

# Requires static routes on gateway
# ip route add 10.0.30.0/24 via [incus-host-ip]

VLANs

# Create VLAN network
incus network create vlan100   parent=enp1s0   vlan=100   type=macvlan

# Attach containers to VLAN
incus launch images:ubuntu/22.04 vlan-web -p default
incus network attach vlan100 vlan-web eth1

Port Forwarding

Forward Host Port to Container:

# Create proxy device
incus config device add web-server http-proxy proxy   listen=tcp:0.0.0.0:80   connect=tcp:10.0.10.100:80

# Now host:80 forwards to container:80

# HTTPS forwarding
incus config device add web-server https-proxy proxy   listen=tcp:0.0.0.0:443   connect=tcp:10.0.10.100:443

# Multiple ports
incus config device add db-server mysql-proxy proxy   listen=tcp:0.0.0.0:3306   connect=tcp:10.0.10.101:3306

Section 7: Storage Pools

Storage Backends

ZFS (Recommended):

Advantages:

  • Instant snapshots (copy-on-write)
  • Efficient cloning
  • Data integrity (checksums)
  • Compression
  • Deduplication
# Create ZFS pool
incus storage create zfs-pool zfs size=100GB

# Or use existing ZFS pool
incus storage create zfs-pool zfs source=tank/incus

# Configure compression
incus storage set zfs-pool zfs.compression=lz4

# Enable snapshots
incus storage set zfs-pool volume.zfs.use_refquota=true

Btrfs:

Similar to ZFS but lighter weight:

# Create btrfs pool
incus storage create btrfs-pool btrfs size=50GB

# Enable snapshots
incus storage set btrfs-pool btrfs.mount_options=compress=zstd

LVM:

Traditional block storage:

# Create LVM pool
incus storage create lvm-pool lvm   source=/dev/sdb   lvm.thinpool_name=IncusPool

# Set volume group
incus storage set lvm-pool lvm.vg_name=incus-vg

Directory (Simple):

File-based, no special filesystem:

# Create dir pool
incus storage create dir-pool dir source=/var/lib/incus/storage

# Works on any filesystem
# No advanced features (snapshots via rsync)

Storage Volumes

Create Custom Volumes:

# Create storage volume
incus storage volume create default data-volume size=20GiB

# Attach to container
incus storage volume attach default data-volume web-server /mnt/data

# Detach volume
incus storage volume detach default data-volume web-server

# Delete volume
incus storage volume delete default data-volume

Shared Volumes:

# Create shared volume
incus storage volume create default shared-data

# Attach to multiple containers
incus storage volume attach default shared-data web1 /mnt/shared
incus storage volume attach default shared-data web2 /mnt/shared
incus storage volume attach default shared-data web3 /mnt/shared

# All containers see same data

Section 8: Migrating from LXD

Migration Process

Prerequisites:

✓ LXD containers stopped or prepare for downtime
✓ Incus installed on target system
✓ Storage pools created in Incus
✓ Network configuration planned
✓ Backup of LXD data

Method 1: Direct Migration (Same Host)

# Stop LXD
sudo systemctl stop lxd

# Migrate data
sudo incus admin migrate-lxd

# Wizard prompts:
# - Migrate all containers? Yes
# - Migrate storage pools? Yes
# - Migrate networks? Yes
# - Migrate profiles? Yes

# Migration completes
# Containers now managed by Incus

# Verify
incus list

# Start containers
incus start --all

Method 2: Manual Export/Import

# On LXD system, export container
lxc export my-container my-container.tar.gz

# Transfer to Incus system
scp my-container.tar.gz incus-host:/tmp/

# On Incus system, import
incus import /tmp/my-container.tar.gz

# Container imported and ready

Conclusion

Incus represents the future of system containers—combining LXD’s excellence with true open-source commitment. From lightweight containers to full VMs, unified management to advanced networking, it delivers professional infrastructure tools for homelabs and production.

Key Takeaways:

  • Incus preserves LXD’s power with community governance
  • System containers offer full Linux environments, not just apps
  • VMs and containers managed identically
  • Profiles enable consistent, repeatable deployments
  • Advanced networking rivals enterprise platforms
  • ZFS integration provides enterprise-grade storage

Next Steps:

  • Explore clustering for multi-host setups
  • Implement OVN for software-defined networking
  • Integrate with Ansible/Terraform for IaC
  • Join the Incus community forums
  • Contribute to open-source development

Resources:

Welcome to Incus. The future of containers is open.