LXC (Linux Containers) provides OS-level virtualization for running multiple isolated Linux systems on a single host. Unlike Docker which runs application containers, LXC runs full system containers with their own init system, services, and networking – closer to a lightweight VM than an app container.

This guide covers installing LXC and Incus (the actively maintained LXD fork) on Ubuntu 24.04 and 22.04, managing containers from the CLI, and setting up the Incus web UI for browser-based management. We also cover the classic LXC tooling for those who need direct LXC access without the Incus/LXD layer.

LXC vs LXD vs Incus – Quick Overview

ToolWhat It IsStatus
LXCLow-level container runtime (liblxc). Direct kernel interfaceActive, stable
LXDHigher-level container/VM manager built on LXC. REST API + CLICanonical-only (snap)
IncusCommunity fork of LXD after Canonical took it proprietaryActive, recommended

For most users, Incus is the recommended choice – it gives you the LXD experience (easy CLI, REST API, web UI, VM support) without being locked to Canonical’s snap. If you need raw LXC access for custom setups, we cover that too.

Prerequisites

  • Ubuntu 24.04 or 22.04 with root or sudo access
  • At least 2 GB RAM and 20 GB free disk space
  • Kernel 5.15+ (default on both Ubuntu versions)

Option A: Install Incus (Recommended)

Step 1: Add the Zabbly Repository

Incus is maintained by the Zabbly team (led by the original LXD creator). Add their stable repository:

curl -fsSL https://pkgs.zabbly.com/key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/zabbly.gpg

CODENAME=$(. /etc/os-release && echo ${VERSION_CODENAME})

echo "Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: ${CODENAME}
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.gpg" | sudo tee /etc/apt/sources.list.d/zabbly-incus-stable.sources

sudo apt update

Step 2: Install Incus

sudo apt install -y incus incus-ui-canonical

The incus-ui-canonical package adds the web-based management UI. Add your user to the incus-admin group so you can run Incus commands without sudo:

sudo usermod -aG incus-admin $USER
newgrp incus-admin

Verify the installation:

$ incus version
Client version: 6.x
Server version: 6.x

Step 3: Initialize Incus

Run the interactive setup wizard. For a single-server setup, the defaults work well:

$ incus admin init
Would you like to use clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: default
Name of the storage backend to use (dir, btrfs, lvm, zfs) [default=zfs]: zfs
Create a new ZFS pool? (yes/no) [default=yes]: yes
Would you like to use an existing empty block device? (yes/no) [default=no]: no
Size in GiB of the new loop device (1GiB minimum) [default=30GiB]: 30GiB
Would you like to connect to a MAAS server? (yes/no) [default=no]: no
Would you like to create a new local network bridge? (yes/no) [default=yes]: yes
What should the new bridge be called? [default=incusbr0]: incusbr0
What IPv4 address should be used? (CIDR subnet notation, "auto" or "none") [default=auto]: auto
What IPv6 address should be used? (CIDR subnet notation, "auto" or "none") [default=auto]: none
Would you like the server to be available over the network? (yes/no) [default=no]: yes
Address to bind to (not including port) [default=all]: all
Port to bind to [default=8443]: 8443
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: yes
Would you like a YAML "init" preseed to be printed? (yes/no) [default=no]: no

Verify the storage pool and network bridge were created:

$ incus storage list
+---------+--------+--------+---------+---------+
|  NAME   | DRIVER | SOURCE | USED BY | STATE   |
+---------+--------+--------+---------+---------+
| default | zfs    | ...    | 1       | CREATED |
+---------+--------+--------+---------+---------+

$ incus network list
+----------+----------+---------+----------------+------+-------------+---------+
|   NAME   |   TYPE   | MANAGED |     IPV4       | IPV6 | DESCRIPTION | USED BY |
+----------+----------+---------+----------------+------+-------------+---------+
| incusbr0 | bridge   | YES     | 10.x.x.1/24   | -    |             | 1       |
+----------+----------+---------+----------------+------+-------------+---------+

Step 4: Launch Your First Container

# Launch an Ubuntu 24.04 container
incus launch images:ubuntu/24.04 my-ubuntu

# Launch a Debian 12 container
incus launch images:debian/12 my-debian

# Launch an AlmaLinux 9 container
incus launch images:almalinux/9 my-alma

Check running containers:

$ incus list
+-----------+---------+---------------------+------+-----------+-----------+
|   NAME    |  STATE  |        IPV4         | IPV6 |   TYPE    | SNAPSHOTS |
+-----------+---------+---------------------+------+-----------+-----------+
| my-ubuntu | RUNNING | 10.x.x.100 (eth0)  | -    | CONTAINER | 0         |
| my-debian | RUNNING | 10.x.x.101 (eth0)  | -    | CONTAINER | 0         |
| my-alma   | RUNNING | 10.x.x.102 (eth0)  | -    | CONTAINER | 0         |
+-----------+---------+---------------------+------+-----------+-----------+

Step 5: Manage Containers

# Execute a command inside the container
incus exec my-ubuntu -- apt update

# Get a shell inside the container
incus exec my-ubuntu -- bash

# Stop a container
incus stop my-ubuntu

# Start a container
incus start my-ubuntu

# Delete a container (must be stopped first)
incus stop my-ubuntu && incus delete my-ubuntu

# Force delete a running container
incus delete my-ubuntu --force

Step 6: File Transfer and Snapshots

# Push a file into the container
incus file push /local/file.conf my-ubuntu/etc/myapp/file.conf

# Pull a file from the container
incus file pull my-ubuntu/var/log/syslog ./syslog-backup

# Create a snapshot
incus snapshot create my-ubuntu snap-before-upgrade

# List snapshots
incus snapshot list my-ubuntu

# Restore from snapshot
incus snapshot restore my-ubuntu snap-before-upgrade

Step 7: Access the Incus Web UI

If you installed incus-ui-canonical and chose to expose the server over the network during init (port 8443), the web UI is available at:

https://your-server-ip:8443

Choose Login with TLS:

incus ui login 01

Generate a client certificate for browser access and download it locally. Run the given commands to trust it.

incus ui login 01 2

Import it into your browser by following the instructions provided:

incus ui login 02

Quit the browser and start it again. Then generate token in step 2, and import by pasting token value.

incus ui login 03

The UI lets you create, start, stop, snapshot, and configure containers and VMs visually.

incus ui login 04

Option B: Install Classic LXC (Low-Level)

If you need raw LXC without the Incus/LXD management layer:

sudo apt update && sudo apt install -y lxc lxc-utils

Verify the bridge network was created:

$ ip ad | grep lxc
3: lxcbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
    inet 10.0.3.1/24 scope global lxcbr0

Configure unprivileged containers (run as your user, not root):

mkdir -p ~/.config/lxc

echo "lxc.include = /etc/lxc/default.conf
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0" > ~/.config/lxc/default.conf

echo "$USER veth lxcbr0 10" | sudo tee -a /etc/lxc/lxc-usernet

Create and start a container:

# Create Ubuntu 24.04 container
lxc-create -t download -n my-container -- \
  --dist ubuntu --release noble --arch amd64

# Start it
lxc-start -n my-container

# Attach to it
lxc-attach -n my-container

# List containers
lxc-ls --fancy

# Stop
lxc-stop -n my-container

# Destroy
lxc-destroy -n my-container

If you get “ERROR: Unable to fetch GPG key from keyserver“, add --no-validate:

lxc-create -t download -n my-container -- \
  --dist ubuntu --release noble --arch amd64 --no-validate

Container Networking

By default, containers get a private IP on the bridge network (NAT). For more advanced setups:

# Expose a container port to the host (Incus)
incus config device add my-ubuntu http proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80

# Give a container a static IP (Incus)
incus config device override my-ubuntu eth0 ipv4.address=10.x.x.50

# Use macvlan for direct LAN access (Incus)
incus network create macvlan-net --type=macvlan parent=eth0
incus launch images:ubuntu/24.04 web-server --network=macvlan-net

Launch Virtual Machines (Incus Only)

Incus can run full VMs alongside containers using QEMU. The same CLI works for both:

# Launch a VM (note the --vm flag)
incus launch images:ubuntu/24.04 my-vm --vm -c limits.cpu=2 -c limits.memory=2GiB

# Check type
incus list
# TYPE column shows CONTAINER or VIRTUAL-MACHINE

Useful Container Images

Browse all available images:

incus image list images: | head -40

Common images:

ImageCommand
Ubuntu 24.04incus launch images:ubuntu/24.04 name
Debian 13incus launch images:debian/trixie name
Rocky Linux 9incus launch images:rockylinux/9 name
AlmaLinux 9incus launch images:almalinux/9 name
Fedora 41incus launch images:fedora/41 name
Arch Linuxincus launch images:archlinux name
Alpine Linuxincus launch images:alpine/3.20 name
openSUSE Leapincus launch images:opensuse/15.6 name

Troubleshooting

Incus service not starting:

sudo systemctl status incus
sudo journalctl -u incus -n 50 --no-pager

Permission denied running incus commands:

Make sure your user is in the incus-admin group and you logged out/in (or ran newgrp incus-admin).

Container has no network:

# Check bridge exists
ip link show incusbr0

# Check container network config
incus config show my-container | grep -A5 network

GPG key error with lxc-create:

Use --no-validate flag or install gnupg: sudo apt install gnupg

Storage pool full:

incus storage info default
# Expand the pool if using ZFS loop device
sudo zpool set autoexpand=on default

Conclusion

For most use cases, Incus gives you the best LXC experience – easy CLI, web UI, VM support, snapshots, and clustering built in. Use raw LXC only when you need direct liblxc control for custom orchestration or embedded systems. Both are production-ready and actively maintained.

Related guides:

4 COMMENTS

  1. This no longer works on ubuntu 22.04. Install says successful, but no joy.

    Additional libraries required include for install include:

    python-is-python3
    python3-pyflakes
    pyflakes
    python3-pip

  2. Last additional library:

    python3-flask
    python3-itsdangerous

    Successful launch after these two installed. However, there are deprecated classes that will be removed in python 3.12. SafeConfigParser needs to be altered to ConfigParser

LEAVE A REPLY

Please enter your comment!
Please enter your name here