LXC and Incus: System Containers That Behave Like Lightweight VMs

LXC and Incus: System Containers That Behave Like Lightweight VMs

Containers are usually explained as a way to run one process in isolation. That is application containers, which is what Docker popularised. System containers are the other tradition, and they are considerably more useful than their obscurity suggests.

The difference

A Docker container runs one process. When that process exits, the container is finished. There is no init system, no cron, no logins, and installing three packages into one is considered poor practice.

A system container boots an init system and runs a full userspace. It has services, users, log files, and a package manager. It behaves like a small machine, except it shares the host kernel and starts in under a second.

Both use the same kernel primitives: namespaces and cgroups. The difference is entirely in what runs inside.

LXC and Incus

LXC is the low-level toolkit that creates and runs system containers.

Incus is the management layer above it: an API, a CLI, images, storage pools, networking, snapshots, and clustering. It is a community fork of LXD, created after Canonical moved LXD under a contributor licence agreement, and it is maintained under the Linux Containers project.

Incus is what you actually use. Working with raw LXC is like working with raw QEMU rather than libvirt.

sudo apt install incus         # Debian and Ubuntu
sudo dnf install incus         # Fedora
sudo pacman -S incus           # Arch

sudo incus admin init          # interactive first-time setup
sudo usermod -aG incus-admin "$USER"   # then log out and back in

Using it

incus launch images:debian/13 web
incus list
incus exec web -- bash                 # a shell inside
incus exec web -- systemctl status
incus stop web
incus delete web

incus file push ./config.yml web/etc/myapp/config.yml
incus file pull web/var/log/syslog ./syslog

incus exec web -- bash is the moment the model clicks. You get a root shell in what feels like a fresh Debian install, with systemd running, and it started instantly.

Limits, snapshots, and images

incus config set web limits.memory 2GiB
incus config set web limits.cpu 2

incus snapshot create web before-upgrade
incus snapshot restore web before-upgrade

incus publish web --alias my-web-image  # turn a container into a reusable image
incus copy web web-clone

Snapshots are near-instant on a copy-on-write storage backend such as ZFS or Btrfs, which is why Incus asks about storage during init. Choosing dir works and gives up most of the speed.

It runs VMs too

incus launch images:debian/13 vm1 --vm
incus list

Same commands, same storage, same networking, but a real virtual machine with its own kernel. That flexibility is genuinely useful: run containers where a shared kernel is fine, and a VM for the one workload that needs a different one or stronger isolation.

Unprivileged by default

Containers run unprivileged, meaning root inside the container maps to an unprivileged user on the host. A container escape lands in a nobody account rather than as host root.

This is a large improvement over privileged containers and it is not a virtual machine boundary. The kernel is shared, so a kernel vulnerability affects everything. For genuinely untrusted workloads, use a VM.

Where this fits

System containers for things you want to behave like machines: a development environment per project, a service with several components, anything you want to log into and administer normally.

Docker or Podman for services distributed as images, which is most self-hosted software.

Virtual machines for a different kernel, a different operating system, or untrusted code.

A homelab running all three is normal rather than indecisive.

Frequently Asked Questions

How is a system container different from a Docker container?

A Docker container runs one process and disappears when it exits. A system container boots an init system and runs a full userspace with services, logins, and cron, so it behaves like a small machine. Both use the same kernel features; the difference is what runs inside.

What is Incus and how does it relate to LXD?

Incus is a community fork of LXD created after Canonical moved LXD under a contributor licence agreement. It is maintained under the Linux Containers project alongside LXC and is the version most distributions now package.

Can Incus run virtual machines as well as containers?

Yes. It manages both with the same commands and the same storage and networking, so you can run containers where the shared kernel is fine and a VM where you need a different kernel or stronger isolation.

Is a system container as isolated as a virtual machine?

No. Containers share the host kernel, so a kernel vulnerability is a shared risk and workloads needing a different kernel cannot run. Unprivileged containers map root inside to an unprivileged host user, which is a substantial improvement but not the same boundary as a VM.

When should I choose this over Docker?

When you want something that behaves like a machine rather than a process: a place to install several packages, run systemd services, and log into. For a single service packaged as an image, Docker or Podman remains the better fit.

What happened to LXD?

It moved to Canonical stewardship under a CLA, which prompted the original maintainers to fork it as Incus under the Linux Containers project. LXD still exists and is developed by Canonical; Incus is what most non-Ubuntu distributions ship.