
Incus is safe by default: its containers are unprivileged, meaning the instance root is not the host root. What remains is not undoing that protection, and knowing how to compartmentalise and cap. This guide explains the idmap that isolates containers, shows how to cap resources, and sets up a restricted project that forbids privileged containers. Tested on Incus 7.0. For anyone hosting instances that do not trust each other.
What you will learn
- Why an unprivileged container is safer (the idmap).
- Limit the CPU and memory of an instance.
- Compartmentalise with a restricted project.
- The
security.privilegedandsecurity.nestingflags, to handle with care.
Prerequisites
- A working Incus server: see installing Incus.
- The basics of profiles and projects: see profiles and projects.
The foundation: unprivileged containers
This is the fundamental protection of Incus, active with nothing to configure. An unprivileged container shifts identifiers (idmap): the instance root (UID 0) corresponds to a harmless, high UID on the host.
incus exec sec-lab -- cat /proc/self/uid_map 0 1000000 1000000000That line reads: UID 0 inside the container starts at 1000000 on the host, across a range of a billion UIDs. In practice, if an attacker becomes root inside the container and escapes, they are only a user with no privilege at all on the host. That is the whole difference from a classic privileged Docker container.
Capping resources
A container with no limit can exhaust the host CPU or memory and starve its neighbours, which is a denial of service. You cap with the limits.* keys.
incus config set sec-lab limits.cpu=1 limits.memory=512MiBincus config show sec-lab | grep limits limits.cpu: "1" limits.memory: 512MiBThose limits apply live and extend further (limits.cpu.allowance for a time quota, limits.memory.swap, limits.processes). On a shared host, capping is a security measure, not only a performance one.
Compartmentalising with a restricted project
A project isolates a set of instances. Setting it to restricted=true forbids dangerous features to everything created inside it, whatever the user types.
-
Create the project and restrict it:
Fenêtre de terminal incus project create confinedincus project set confined restricted=true -
Check that a privileged container is refused there:
Fenêtre de terminal incus launch images:debian/13 badct --project confined -c security.privileged=trueError: ... Invalid value "true" for config "security.privileged" oncontainer "badct" of project "confined": Privileged containers are forbidden
The refusal comes from the project, not from a convention: even an administrator cannot create a privileged container inside a restricted project. You then refine with the restricted.* keys (restricted.containers.nesting, restricted.devices.disk, restricted.networks.access) to frame precisely what the project allows.
The flags to handle with care
Two settings undo the default isolation. Enabling them has to be a conscious choice, never a reflex to "make something work".
security.privileged=truemakes the container privileged (container root equals host root). An escape becomes a host compromise. Rule it out except for a very specific need on a trusted instance.security.nesting=trueallows running Incus or Docker inside the container. Handy, since it is what makes nested labs possible, but it widens the attack surface. Reserve it for the cases that demand it.
Other levers
Beyond these basics, Incus applies a per-instance AppArmor profile and seccomp system call filtering by default. For remote API access, prefer TLS certificate or OIDC authentication over broad access, and compartmentalise rights per project. These mechanisms combine: idmap, limits, restricted project and AppArmor form independent layers.
Key points
- Incus containers are unprivileged by default: the idmap shifts root away from the host.
- Capping CPU and memory (
limits.cpu,limits.memory) protects a shared host. - A restricted project (
restricted=true) forbids privileged containers, even to an administrator. security.privileged=truecancels the idmap: avoid it;security.nestingwidens the surface.- A restricted project starts from nothing is permitted: add the allowed pool and network explicitly.
FAQ: common questions about securing Incus
Unprivileged thanks to the idmap
By default, an Incus container is unprivileged: identifiers are shifted.
incus exec sec-lab -- cat /proc/self/uid_map
0 1000000 1000000000
UID 0 (root) inside the container maps to UID 1000000 on the host. If an attacker becomes root in the container and escapes, they are only an unprivileged user on the host. That is the fundamental protection of Incus.
The limits keys
incus config set sec-lab limits.cpu=1 limits.memory=512MiB
They apply live and extend further: limits.cpu.allowance (a time quota), limits.memory.swap, limits.processes. An unlimited container can exhaust the host CPU or RAM and starve its neighbours: on a shared host, capping is a security measure as much as a performance one.
The restricted project
incus project create confined
incus project set confined restricted=true
Inside a restricted=true project, a privileged container is refused:
Error: ... Privileged containers are forbidden
The refusal comes from the project, not from a convention: even an administrator cannot work around it. You refine with the restricted.* keys (restricted.containers.nesting, restricted.devices.disk, restricted.networks.access).
A flag to avoid
security.privileged=true removes the idmap: the container root becomes the host root again. An escape then becomes a complete compromise of the host.
If a container needs one specific capability, prefer:
- a dedicated device (
incus config device add); - a targeted interception (
security.syscalls.intercept.*);
rather than opening everything with security.privileged. Reserve it for trusted instances and very specific needs.
Next steps
- Multi-tenant private cloud: push restricted projects to full tenant isolation, with quotas and scoped credentials.
- Network ACLs: complement the isolation the idmap gives with explicit traffic filtering between instances.
- Profiles and projects: the compartmentalisation this hardening builds on.