Learn LXC - Privileged vs Unprivileged Containers
Series/Learn LXC/Episode 5
Episode 5 of 23

Learn LXC - Privileged vs Unprivileged Containers

This episode compares the two container modes: privileged, which is fast but risky because root inside it equals root on the host, and unprivileged, which uses user namespaces and idmapping (subuid/subgid) so it's safe for multi-tenant environments.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

In episode 4 your first container c1 is running. But the question is: is that container running as privileged or unprivileged? The difference between these two modes is the most fundamental security decision in LXC. In episode 5 we break down both: why privileged is dangerous, how unprivileged uses user namespaces and idmapping, and when each is worth using.

Privileged Containers

What Privileged Means

A privileged container runs directly as root on the host without user mapping. That means UID 0 inside the container is UID 0 on the host, with all the capabilities that entails. LXC tries to reduce the risk with AppArmor and seccomp defaults (episode 13), but the isolation boundary remains thin.

Its strengths are speed and simplicity: no idmapping layer, direct access to devices (e.g. /dev/loop*, kernel modules) works out of the box, and I/O performance is closest to the host. That's why privileged is still used for workloads that need raw hardware access.

Main Risks

  • Direct escalation: a compromise inside the container (e.g. a kernel exploit or container escape) means host root falls directly into the attacker's hands.
  • Hostile multi-tenant: if one host serves many untrusted users, a single vulnerability compromises everyone.
  • Devices & mounts: a privileged container can manipulate host devices and filesystems if not tightly restricted.

Warning

The golden rule: never grant privileged container access to parties you don't fully trust. Privileged containers suit infrastructure you manage yourself within a single trust domain — not public tenants.

Unprivileged Containers

What Unprivileged Means

An unprivileged container is LXC's modern default. It uses user namespaces to map UIDs/GIDs inside the container to harmless UIDs/GIDs on the host. Root inside the container (UID 0) is mapped to a regular user on the host — e.g. UID 100000 — so actions considered "as root" inside the container still run with a regular user's privileges from the host's point of view.

We'll look at the mapping in detail in episode 15, but here's the gist:

Check the available subuid ranges
grep "$USER" /etc/subuid
grep "$USER" /etc/subgid

A typical output: devnull:100000:65536 — meaning UID 0-65535 inside the container maps to UID 100000-165535 on the host. Each container can get a different range.

Security Benefits

  • Attack chain broken: because container root is a regular host user, many kernel exploits "fail" at the privilege layer.
  • Safe multi-tenant: multiple untrusted users can share one host without threatening each other.
  • Least privilege by default: whatever happens inside the container, its impact on the host is bounded by the mapped user's rights.

Trade-offs

Unprivileged has a small cost: access to some host devices is restricted, and certain cases (bind-mounts with unusual permissions, or direct hardware access) need extra configuration. Pure performance is nearly the same — user namespaces add no meaningful runtime overhead once the container is running.

How to Check Your Container's Mode

Look at the c1 container config from episode 4:

View the c1 container configuration
cat /var/lib/lxc/c1/config

Notice the lines that determine the mode:

LinuxUnprivileged triggers in the config
lxc.include = /usr/share/lxc/config/common.conf.d/00-lxcfs.conf
lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
lxc.rootfs.path = dir:/var/lib/lxc/c1/rootfs

The presence of the ubuntu.userns.conf include indicates the container is running unprivileged. Conversely, without a userns include (and without lxc.idmap), the container runs privileged. You can also see it at runtime: files created by root inside the container will appear owned by a large UID (e.g. 100000) when viewed from the host.

Tip

Quick check: from the host, run ls -n /var/lib/lxc/c1/rootfs/. If file owners appear as large numbers like 100000, the container is unprivileged. If 0, it's privileged.

When to Choose Which

  • Privileged: a single self-managed host, workloads that need direct device access, or the highest raw performance. Add strict AppArmor/seccomp as compensation.
  • Unprivileged: the default for everything else — homelab, multi-tenant, edge devices, and anything touching public networks.

For the rest of this series, we assume unprivileged unless stated otherwise, and we use the existing c1 container as our practice base.

Closing

Key takeaways:

  • Privileged: root inside the container = root on the host; fast, but with high risk.
  • Never give privileged access to untrusted parties.
  • Unprivileged: user namespaces + idmapping map container root to a regular host user.
  • /etc/subuid and /etc/subgid determine the per-user mapping ranges.
  • Unprivileged is the safe modern default for multi-tenant environments.

In the next episode 6 we'll cover networking: bridge, veth & NAT — how lxcbr0 becomes the default NAT bridge, how veth pairs work, configuration in /etc/lxc/default.conf and /etc/lxc/lxc-usernet, and the network modes none, veth, macvlan, and phys along with static IP/DHCP.

Learn LXC - Privileged vs Unprivileged Containers | Learn LXC