LXC, short for Linux Containers, is a user-space toolset and library that creates system containers: isolated Linux environments that run their own init system, services and users while sharing the host kernel. In Linux virtualization, LXC sits between a plain chroot and a full virtual machine, offering near-native performance with an experience that resembles a lightweight server.
LXC does not emulate hardware. It combines kernel features, mainly namespaces, cgroups, capabilities, seccomp and mandatory access control profiles, into a coherent container runtime. Tools such as Incus, LXD and Proxmox VE build management layers on top of the same foundation.
How LXC builds a system container
Starting an LXC container means asking the kernel to create a process inside a new set of namespaces. That process becomes PID 1 of the container, usually systemd or another init system, and it sees its own process table, hostname, network interfaces, mount tree and user IDs.
Resource limits come from cgroups, which cap CPU, memory, I/O and the number of processes for the whole container. Security boundaries are reinforced by dropping capabilities, applying a seccomp filter and loading an AppArmor or SELinux profile. The root file system is normally a directory, a ZFS or Btrfs subvolume, or an LVM volume on the host.
The table maps each building block to the isolation it provides.
| Feature | What it isolates or limits | Container effect |
|---|---|---|
| PID namespace | Process IDs | Container sees only its own processes |
| Mount namespace | File system tree | Own root file system and mounts |
| Network namespace | Interfaces, routes, firewall | Own IP stack, often through a veth pair |
| User namespace | User and group IDs | Root inside maps to unprivileged host ID |
| Cgroups | CPU, memory, I/O, task count | Enforced resource limits |
| Seccomp and MAC profile | System calls and resource access | Reduced kernel attack surface |
Privileged and unprivileged containers
The most important security decision in LXC is whether a container is privileged or unprivileged. The difference lies in the user namespace mapping of the container’s root account.
| Property | Privileged container | Unprivileged container |
|---|---|---|
| Root UID mapping | Container root is host UID 0 | Container root maps to a high host UID |
| Escape impact | Attacker may gain host root | Attacker lands as an unprivileged user |
| Device and mount access | Broad | Restricted |
| Configuration effort | Simpler | Needs subuid and subgid ranges |
| Recommended use | Trusted legacy workloads only | Default for most deployments |
Unprivileged containers use ranges from /etc/subuid and /etc/subgid, typically 65,536 IDs per container. Files created by container root therefore belong to an ID such as 100000 on the host, which is why sharing host directories with containers requires careful ID mapping.
LXC compared with virtual machines and application containers
LXC is often confused with both virtual machines and application container platforms. The comparison below places it among the neighboring Terms of Linux virtualization, such as KVM, QEMU and libvirt.
| Approach | Kernel | Typical workload | Overhead |
|---|---|---|---|
| LXC system container | Shared with host | Full Linux system with init | Very low |
| Application container | Shared with host | One process per image | Very low |
| KVM with QEMU | Own guest kernel | Any operating system | Low to moderate |
| WSL 2 | Own Linux kernel in a lightweight VM | Linux tools on Windows | Low |
Because LXC shares the kernel, it cannot run Windows or a different kernel version, and kernel modules must be loaded on the host. In exchange, containers start in about a second, use memory only for their processes, and reach near-native disk and network throughput without paravirtual drivers such as Virtio.
Libvirt can manage LXC through its own driver, but most administrators use the native lxc-* commands or a higher-level manager. Application container tools follow a different philosophy: immutable images and one service per container, while LXC containers are usually long-lived and administered like small servers.
Working with LXC
The classic LXC toolset consists of lxc-prefixed commands and a configuration file per container, usually at /var/lib/lxc/NAME/config. The table lists the core commands.
| Command | Purpose |
|---|---|
| lxc-create -t download | Create a container from a distribution image |
| lxc-start / lxc-stop | Start or cleanly stop a container |
| lxc-attach | Run a command or shell inside a container |
| lxc-ls –fancy | List containers with state and addresses |
| lxc-snapshot | Create or restore snapshots |
| lxc-copy | Clone a container |
Configuration keys such as lxc.cgroup2.memory.max, lxc.net.0.type = veth and lxc.idmap define limits, networking and ID mapping. Networking usually connects a veth interface to a host bridge, which makes each container appear as a separate machine on the network.
Limitations and common mistakes
LXC provides strong practical isolation, but its boundary is weaker than a hypervisor boundary because every container talks to the same kernel. The following points summarize the issues seen most often.
- A kernel vulnerability affects all containers on the host, so prompt host kernel updates are essential.
- Privileged containers for untrusted users effectively give those users a path to host root.
- Nested containers and FUSE mounts require explicit configuration and often relaxed security settings.
- Tools that read /proc directly may report host totals for memory and CPU unless LXCFS is used.
- Bind-mounted host directories show unexpected ownership when ID mapping is not planned.
The idea in brief
Picture a large apartment building. Every apartment has its own door, rooms and residents, but all of them share the same foundation, plumbing and electricity. LXC builds apartments like that on a single Linux computer.
Each container feels like a separate small server, with its own programs and users. Because they share the building’s foundation, the Linux kernel, they start almost instantly and waste very little memory. The trade-off is that a crack in the foundation would affect every apartment.
A practical scenario
A university department runs one physical server with 32 cores and 128 GB of memory and must give twelve student project teams their own Linux servers for a semester. Full virtual machines would reserve memory for twelve guest kernels and take longer to provision.
The administrator creates twelve unprivileged LXC containers from a Debian image, each with lxc.cgroup2.memory.max set to 8G and a CPU weight that shares cores fairly. Each container receives an address on a bridged network and its own SSH access.
Provisioning all twelve takes a few minutes, idle containers use little memory, and snapshots before risky changes let teams recover from mistakes in seconds. At the end of the semester, the containers are archived and destroyed.