Learn LXC - User Namespaces & ID Mapping
Series/Learn LXC/Episode 15
Episode 15 of 23

Learn LXC - User Namespaces & ID Mapping

This episode breaks down the security core of unprivileged containers: the /etc/subuid and /etc/subgid files, UID/GID mapping configuration via lxc.idmap, lxc-usernet network quotas, and best practices for mapping the 0-65536 range in trusted environments.

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

Introduction

In episode 5 we introduced unprivileged containers in general. Now we break down the mechanism that makes them safe: user namespaces and ID mapping (idmap). This is why root inside the container isn't dangerous on the host — and it's what you need to understand when a bind-mount feels "odd", or when you want to adjust the mapping to share files with the host. In episode 15 we go into the details.

Foundation: User Namespaces and Mapping

Why Mapping Is Needed

Inside an unprivileged container, UID 0 (root) is only a local view. On the host, that UID is actually mapped to a large number (e.g. 100000) belonging to a regular user. Because of this mapping, "root" in the container has no host-root power — it works with a regular user's rights when touching host resources.

The mapping goes one way: container UID → host UID. This range is defined twice: once in /etc/subuid//etc/subgid (the system's allocation for the user), and once in the container config via lxc.idmap.

The /etc/subuid and /etc/subgid Files

These files determine the sub-UID and sub-GID allocations for each user — the ranges of numbers allowed for unprivileged containers:

Linux/etc/subuid
root:100000:65536
devnull:165536:65536

The format is: <user>:<base>:<count>. The line devnull:165536:65536 means user devnull may use UIDs 165536 through 165536+65536-1 for their containers. Each container owned by that user uses a slice of this range.

Check your allocation:

Check subuid and subgid allocations
grep "$USER" /etc/subuid
grep "$USER" /etc/subgid

Warning

If /etc/subuid and /etc/subgid are empty for your user, unprivileged containers can't be created — or are created but fail to start with an idmap error. Add the lines (e.g. sudo usermod --add-subuids 100000-165535 $USER) before starting. This is the number one cause of unprivileged failures on fresh installs.

Configuring lxc.idmap

Line Format

The mapping inside the container config is written with lxc.idmap. The format is: lxc.idmap = <ttype> <nsid> <hostid> <range>.

  • ttypeu for UID, g for GID.
  • nsid — UID/GID inside the container namespace.
  • hostid — UID/GID on the host.
  • range — the number of consecutively mapped IDs.

A typical unprivileged container (modern template) uses two lines for UID and two for GID:

LinuxTypical unprivileged idmap mapping
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536

Meaning: container UIDs 0-65535 map to host UIDs 100000-165535. Same for GIDs. With this mapping, container UID 0 = host UID 100000, container UID 1000 = host UID 101000.

Adjusting the Mapping

To share a directory with a host user (e.g. host user UID 1000 wants files inside the container as UID 1000), you can add a mapping:

LinuxAdd a mapping for a host user
lxc.idmap = u 0 100000 1000
lxc.idmap = g 0 100000 1000
lxc.idmap = u 1000 1000 1
lxc.idmap = g 1000 1000 1
lxc.idmap = u 1001 101001 64535
lxc.idmap = g 1001 101001 64535

With this pattern, container UID 1000 maps one-to-one to host UID 1000 — so files owned by host user 1000 can be read/written directly from inside the container. This trick is used together with bind-mounts (episode 11) to share data between host and container.

Tip

Check the active mapping from inside the container: lxc-attach -n c1 -- cat /proc/self/uid_map. A line 0 100000 65536 shows the mapping currently in effect. This is the fastest way to verify your lxc.idmap configuration.

lxc-usernet: Network Quotas for Unprivileged Users

In episode 6 we touched lxc-usernet. Now its role becomes clear: a regular user isn't allowed to create veths attached to a host bridge. The /etc/lxc/lxc-usernet file grants that permission explicitly — it's the "network allocation" for unprivileged users.

Linux/etc/lxc/lxc-usernet
# <user> <type> <link> <parent bridge> <count>
devnull veth lxcbr0 10

Meaning: user devnull may create up to 10 veths connected to lxcbr0. Without this entry, lxc-start of an unprivileged container fails with "Operation not permitted" when creating the network.

Mapping Best Practices

  • Always default to unprivileged in any environment touching networks or untrusted tenants.
  • 0-65536 mapping: use a range of 65536 (or a multiple) as the per-container allocation. Modern templates set this automatically; don't casually shrink it unless you know exactly the consequences.
  • One container, one range: avoid two containers sharing the same ID range — that breaks isolation (they'd "share" the same host user).
  • Avoid privileged in untrusted environments: episodes 5-6 and 16 reiterate this repeatedly because it's the most fatal mistake.

Pre-Production Checklist

Verify the idmap foundation
grep "$USER" /etc/subuid
grep "$USER" /etc/subgid
lxc-attach -n c1 -- cat /proc/self/uid_map
grep "$USER" /etc/lxc/lxc-usernet

Closing

Key takeaways:

  • /etc/subuid and /etc/subgid grant ID range allocations for unprivileged containers.
  • lxc.idmap maps container UID/GID to the host: u 0 100000 65536.
  • Custom mappings can be used to share files with host users (host UID 1000 ↔ container UID 1000).
  • lxc-usernet grants network (veth) quotas to regular users.
  • Best practices: unprivileged as default, 0-65536 mapping, avoid privileged in untrusted environments.

In the next episode 16 we'll cover security & CVEs — the fact of the CVE-2026-39402 fix in LXC 7.0, the importance of routine patching, host-container isolation, and the threat model: unprivileged + AppArmor + seccomp + latest kernel.

Learn LXC - User Namespaces & ID Mapping | Learn LXC