Skip to main content
BlogDevOps

Incus/LXC System Containers vs Docker Application Containers

System containers give you a persistent, full-OS Linux box that shares the host kernel. Here is when Incus/LXC beats both Docker and a VM, with a comparison table and real commands.

Matthews Wong

Software Engineer

July 20269 min read
Incus/LXC System Containers vs Docker Application Containers

Photo by Manele R. via Wikimedia Commons (CC BY 2.0)

Key Takeaway

Incus and LXC run system containers: persistent, full-OS Linux instances with their own systemd, users, and services that share the host kernel. Docker runs application containers: one process, immutable, disposable. Use Incus when you want a lightweight VM-like box you SSH into and manage; use Docker to ship a single stateless app.

Most engineers meet containers through Docker, so they assume every container is a thin, throwaway wrapper around a single process. Then they hit a workload that wants a real machine: an init system, multiple daemons, cron jobs, users who SSH in, and state that survives reboots. You can bend Docker into that shape, but you spend the whole time fighting its design. That is where system containers earn their place, and Incus is the tool I reach for.

Incus is the community fork of LXD, now maintained under the Linux Containers project after Canonical took LXD in-house. It manages both system containers (via LXC) and full virtual machines from one CLI and API. This post is about where system containers sit between Docker and a VM, and how to pick the right one instead of reaching for Docker by reflex.

System containers vs application containers: the core split

An application container packages one process and its dependencies. The Docker philosophy is one concern per container, immutable image, ephemeral runtime: you rebuild the image to change anything, and the container's writable layer is disposable. A system container instead simulates a full operating system. It boots its own init (systemd or similar), runs many processes, holds persistent state on disk, and behaves like a small server you log into. Both share the host kernel through namespaces and cgroups, so neither pays the boot time or memory tax of a real VM.

The mental model that clicked for me: Docker gives you a process wearing a container as a costume; Incus gives you a machine that happens to share a kernel. When you ask what happens on restart, the answer separates them. Restart a Docker container and the writable layer is gone unless you mounted a volume. Restart an Incus container and everything is still there, because it was never designed to be thrown away.

A comparison across the dimensions that matter

DimensionIncus / LXC system containerDocker application container
Unit of packagingA full OS userland with init, many processesA single process and its dependencies
LifecyclePersistent, pet-like: you patch and keep itEphemeral, cattle-like: rebuild and replace
State on restartSurvives by default, like a serverWritable layer lost unless a volume is mounted
KernelShared host kernel (no separate kernel)Shared host kernel (no separate kernel)
Ecosystem for shipping appsWeaker; not built around a registry workflowHuge: Docker Hub, Compose, CI images everywhere
Best fitMulti-service boxes, dev sandboxes, VM replacementStateless microservices, reproducible builds

When system containers beat a VM

If a system container already feels like a small server, why not just run a VM? Density and speed. A VM boots its own kernel and reserves its own RAM, so ten VMs mean ten kernels and ten fixed memory allocations. Ten system containers share one kernel and only use the memory their processes actually touch. On a modest VPS I can run far more isolated environments as containers than as VMs, and each one starts in under a second instead of tens of seconds.

The trade-off is the shared kernel. Every container runs the host's kernel, so you cannot run a different kernel version, load a custom kernel module per instance, or boot a non-Linux guest. You also lean harder on namespace isolation than on a hypervisor boundary. For most Linux-on-Linux workloads that is a fine deal, and Incus lets you launch a real VM from the same tool when a workload genuinely needs one.

Shared kernel means a kernel-level container escape reaches the host directly, with no hypervisor in between. For hard multi-tenant isolation of untrusted code, a VM boundary is still stronger. Incus 7.2 added per-instance SELinux confinement to tighten this, but do not treat a system container as equivalent to a VM for adversarial workloads.

Getting a system container running with Incus

The workflow is deliberately server-like. You launch an image, and then you treat the result like a fresh machine: shell in, install packages, run systemd services, and it all persists. Here is the minimal path from a clean Incus install to a running Debian container you can log into.

# Initialise Incus once (accept defaults for a simple setup)
incus admin init --minimal

# Launch a persistent Debian 12 system container
incus launch images:debian/12 web01

# It boots its own init and runs many processes
incus exec web01 -- ps aux
incus exec web01 -- systemctl status

# Get a real shell, install and enable a service
incus exec web01 -- bash
#   apt update && apt install -y nginx
#   systemctl enable --now nginx

# Restart the container: state and installed packages survive
incus restart web01
incus list

Since Incus 6.3 you are not locked out of the Docker ecosystem either. Incus can pull OCI images straight from registries like Docker Hub, using skopeo and umoci to flatten them, and run them as application containers alongside your system containers and VMs. It is still young and has gaps, but for simple cases it means one tool can cover both container styles.

# Add the Docker Hub OCI remote, then run an app container from it
incus remote add docker https://docker.io --protocol=oci
incus launch docker:nginx:latest myproxy

# Docker also runs happily INSIDE a system container
# (the reverse never works: you cannot run Incus in a Docker container)
incus config set web01 security.nesting=true
incus exec web01 -- bash -c 'curl -fsSL https://get.docker.com | sh'

When Docker is still the right tool

None of this makes Docker obsolete. If you are shipping a stateless service, Docker's immutable image and huge ecosystem are exactly what you want. The whole industry speaks Dockerfile, Compose, and OCI registries; CI systems, PaaS platforms, and Kubernetes all assume application containers. For reproducible builds and stateless microservices, Docker is the path of least resistance and I still use it daily for my own services.

  • Reach for Docker when the unit is one stateless process you want to build once and run anywhere, or when your platform (Kubernetes, CI, PaaS) demands OCI images.
  • Reach for Incus/LXC when you want a long-lived, patchable box with an init system, multiple services, users, and persistent state, without paying VM overhead.
  • Reach for a full VM when you need a different kernel, a non-Linux guest, or hard isolation for untrusted, adversarial workloads.

A pattern I like on a single VPS: use Incus system containers as clean, isolated environments (one per project or client), and run Docker Compose inside whichever container needs the app-container workflow. You get VM-style separation and the Docker ecosystem at the same time, from one host, with one manager.

The takeaway is not Incus versus Docker as rivals. They solve different problems. Docker answers how do I package and ship one application; Incus answers how do I run many isolated machines cheaply. Once you stop forcing every workload into an application container, both tools get simpler to use, and your VPS gets a lot more room to breathe.

FAQ

Frequently Asked Questions

Short answers to what readers ask most about this topic.

What is the difference between a system container and an application container?

A system container simulates a full operating system: it runs its own init (like systemd), multiple processes, users, and persistent state, behaving like a lightweight server you log into. An application container, like Docker's, packages a single process and its dependencies as an immutable, disposable image. Both share the host kernel, but they are built for opposite lifecycles.

Is Incus the same as LXD?

Incus is the community fork of LXD, created after Canonical took LXD in-house, and it is now maintained under the Linux Containers project. It manages both LXC system containers and full virtual machines from one CLI and API. The current LTS is Incus 6.0, with Incus 7.x on the feature-release track.

Can I run Docker inside an Incus system container?

Yes. You enable security.nesting on the container and install Docker normally, which is a common way to get VM-style isolation plus the Docker ecosystem on one host. The reverse does not work: you cannot run Incus inside a Docker application container. Since Incus 6.3 you can also run OCI/Docker images directly in Incus without Docker at all.

When should I use a system container instead of a virtual machine?

Use a system container when your workload is Linux-on-Linux and you want density and fast startup: containers share one kernel and only use the memory their processes touch, so you fit far more per host than with VMs. Choose a VM when you need a different kernel version, a non-Linux guest, custom kernel modules, or hard isolation for untrusted code.

Does Incus replace Docker?

No, they solve different problems. Docker excels at packaging and shipping single stateless applications with a huge ecosystem of registries, Compose, and CI integration. Incus excels at running many persistent, isolated, full-OS environments cheaply. Many setups use both: Incus system containers for isolation, Docker inside them for the app-container workflow.