Join our Newsletter — 33% off our NHI Course
Home› Guides› LXD and Incus Security: Hardening System Containers, VMs…
Guide Cloud Security

LXD and Incus Security: Hardening System Containers, VMs and the Remote API

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 8 November 2026 14 min read
On this page

Anyone who can talk to the LXD or Incus daemon can attach host file systems and devices to an instance, so the daemon socket, its trust store and its group membership are effectively root on the host. Both managers run full Linux system containers and virtual machines from one API, and both default to unprivileged containers, but a few settings (privileged mode, nesting, raw options, device passthrough, a remote API bound to every interface) undo that isolation quickly. This guide covers the security model the two projects share and where they differ: local and remote access, user namespaces and idmaps, projects and restrictions, fine-grained authorisation, network spoofing controls, logging and a hardening baseline. It is for Linux platform engineers, homelab and lab operators moving to production, and security teams reviewing container hosts. Settings were checked against the LXD 7.0 and current Incus documentation on 8 November 2026; general container and image security is in the Container Security Guide.

Incus is the community-driven alternative to Canonical's LXD, maintained under the Linux Containers project by many of LXD's original developers, and it keeps most configuration keys. Where the two differ, tables below list them alphabetically (Incus, then LXD); the order is not a ranking.

This is an independent guide from NHI Mgmt Group, based on Canonical's public documentation as of November 2026. It is not sponsored or reviewed by Canonical. Product features change, so confirm settings against Canonical's current documentation.

Key takeaways

  • The admin group is a root equivalent. Members of incus-admin or lxd get full API control over the Unix socket, including mounting host paths into instances. Treat membership like sudo and audit it.
  • Keep containers unprivileged and isolated. security.privileged=false is the default; add security.idmap.isolated=true so containers do not share a UID/GID range, and never combine security.nesting=true with privileged mode.
  • Use projects with restrictions for anyone who is not an admin. Setting restricted=true on a project blocks privileged containers, low-level raw options, nesting and most device passthrough by default.
  • Bind the remote API to one address and trust only what you issue. Set core.https_address to a full IP and port, keep core.trust_ca_certificates=false, shorten trust token expiry and prefer OIDC with MFA for people.
  • Bridged instances can spoof by default. Turn on security.mac_filtering, security.ipv4_filtering and security.ipv6_filtering in profiles for untrusted workloads, and use network ACLs.

How identity and access work in LXD and Incus

Both daemons run as root and expose a REST API over two channels: a local Unix socket, always on, and an optional HTTPS listener for remote clients. Inside each instance a third channel, the guest API socket (DevLXD in LXD, /dev/incus in Incus), answers only queries about the requesting instance.

Access pathIncusLXDSecurity meaning
Local socket, full accessincus-admin group and rootlxd group (daemon.group) and rootUnrestricted API, host path and device attachment
Local socket, confinedincus group: each user is restricted to a single project tied to their userdaemon.user.group (snap) confines members to a project via the socketThe supported way to give non-admins local access
Remote, certificateTrusted TLS clients added by token; incus config trust listTrusted TLS clients added by token or PKI; lxc config trust listUnrestricted certificates are full admins
Remote, peopleOpenID Connect (oidc.*)OpenID Connect (oidc.issuer, oidc.audience, oidc.groups.claim)Lets the identity provider enforce MFA and offboarding
Fine-grained authorisationExternal OpenFGA server (authorization.openfga.*), applied to client classes routed to it by authorization.client.*Built-in groups, entitlements and permissions (lxc auth)Per-resource rights instead of all or nothing

Remote trust in practice

  • Trust tokens: a client runs remote add, sees the server certificate fingerprint, and presents a one-time token; the server then adds the client certificate to its trust store. The LXD hardening guide recommends cutting core.remote_token_expiry and cluster.join_token_expiry to 15 minutes (the cluster join token default is 3 hours).
  • PKI mode: clients with certificates from your CA can connect, but core.trust_ca_certificates is false by default and should stay that way so a CA-signed certificate does not become trusted automatically. Use a certificate revocation list and short-lived certificates.
  • Fingerprint verification: the first connection is trust on first use. Compare the fingerprint shown with the one on the server before accepting, or an interception at that moment becomes permanent.
  • Revocation: remove trust with config trust remove <fingerprint>; regular review of the trust store is the only thing that catches laptops and CI runners that were retired without cleanup.

User namespaces and idmaps

An unprivileged container runs in a user namespace: its UID 0 maps to an unprivileged UID on the host, so most container escapes become an ordinary unprivileged-user kernel bug rather than host root. The LXD docs require allocations of at least 65,536 UIDs and GIDs; on hosts without /etc/subuid and /etc/subgid LXD assumes a range starting at base UID 1,000,000 (the snap ignores those files entirely). By default every unprivileged container shares the same map, which means a process in one container has the same host UID as the matching process in another.

ModeSettingIsolationWhen to use
Privilegedsecurity.privileged=trueContainer root is host root; the LXD docs state these are not root-safeTrusted legacy workloads only, ideally in a dedicated project or VM instead
Unprivileged, shared mapDefaultContainer root is an unprivileged host UID, shared across containersSingle-tenant hosts
Unprivileged, isolated mapsecurity.idmap.isolated=trueEach container gets a non-overlapping range, limiting cross-container DoSDefault for multi-tenant and untrusted workloads
Custom mappingraw.idmapMaps specific host IDs into the container (for shared data)Only where needed; restrict with restricted.idmap.uid and restricted.idmap.gid in projects
Virtual machineInstance type VMSeparate guest kernel under QEMUUntrusted code, kernel modules, different OS

For the kernel mechanisms underneath, see the Linux Namespaces and cgroups Security Guide.

Admin roles and privileged access

There are only two safe shapes for human access: a small admin group with full rights, and everyone else confined to restricted projects.

  • Separate the groups: both daemon.group and daemon.user.group default to lxd in the LXD snap, which gives every member full access. The hardening guide recommends a separate group such as lxdusers for non-admins. Incus ships the split already (incus-admin versus incus).
  • Restricted TLS certificates: set restricted: true and a project list with config trust edit. A restricted client cannot change global configuration or the limits and restrictions of its own projects; an empty project list means no access.
  • LXD fine-grained authorisation: identities (OIDC users and fine-grained TLS identities) join groups, and groups receive permissions. Useful entitlements: admin on server (full access), project_manager on server (projects but not server, storage pool or identity configuration), operator on a project (all resources in it, not the project config), user on an instance (view, files, console, terminal). Map identity provider groups with oidc.groups.claim.
  • Incus OpenFGA: run your own OpenFGA server; Incus writes its model and queries it for every request from client classes routed to OpenFGA. Unrestricted certificates still get full access, so route remote clients deliberately.

Project restrictions that matter

Key (with restricted=true)Default when restrictedBlocks
restricted.containers.privilegeunprivilegedPrivileged containers
restricted.containers.nestingblockRunning nested container managers
restricted.containers.lowlevel and restricted.virtual-machines.lowlevelblockraw.lxc, raw.apparmor, raw.seccomp, raw.qemu style options
restricted.containers.interceptionblockSystem call interception options
restricted.devices.disk and .disk.pathsmanagedArbitrary host path mounts
restricted.devices.pci, usb, gpu, unix-char, unix-block, proxyblockDevice passthrough
restricted.devices.nic and restricted.networks.*managed / blockAttaching to host interfaces or uplinks
restricted.idmap.uid and .gidNone allowedMapping host IDs with raw.idmap

Combine restrictions with project limits (limits.instances, limits.cpu, limits.memory, limits.processes, limits.disk): instance CPU, memory and process limits are unset by default, which leaves the host open to resource exhaustion from one tenant.

Integrations, API tokens and service accounts

Automation (Terraform providers, Ansible, CI runners, cluster members, monitoring) usually authenticates with a client certificate, which makes each certificate a long-lived non-human identity with whatever scope its trust entry grants.

  • One certificate per automation, restricted: give each tool its own certificate, restricted to the projects it manages; never reuse an admin's client certificate in a pipeline. Name trust entries so the owner is obvious.
  • Protect the private key: the client key in the user's config directory is a full credential for that remote. Store pipeline keys in a secrets manager and inject them at run time. See the Secrets Management Guide.
  • Bearer tokens from certificates: Incus can derive a JWT bearer token from a client certificate (remote get-client-token) for browser clients; it carries the same rights as the certificate, so handle it as a secret.
  • OIDC client secret: LXD's hardening guide says to set oidc.client.secret only if the identity provider requires it, and to configure short-lived access tokens. oidc.session.expiry defaults to one week in LXD.
  • Metrics: core.metrics_authentication defaults to true; give Prometheus a dedicated metrics certificate rather than disabling authentication.
  • Image downloads as a probe: LXD notes that a user who can create images can make the daemon fetch from internal addresses, and error messages may reveal internal services. Route outbound requests through a filtering proxy with both core.proxy_http and core.proxy_https.
  • Guest API exposure: security.devlxd is on by default but returns only the instance's own data; keep security.devlxd.images and security.devlxd.management.volumes off unless a workload needs them.

Where identity fits: the trust store is an inventory of machine identities. Review it on the same cadence as service accounts; see the SSH Key Management Guide for the equivalent discipline on SSH keys.

Logging and detection

Recent LXD releases emit a dedicated security event stream (lxc monitor --type=security, or the /1.0/events WebSocket with type=security) and both projects can forward events to Loki (loki.api.url, loki.types, which defaults to lifecycle and logging events, so add security events explicitly where supported). Lifecycle events record instance creation, configuration changes and deletion.

SignalSourceWhy it matters
authn_login_fail:tlsLXD security eventsUntrusted certificates probing the remote API (T1133)
authn_token_created, authn_token_reuse, authn_token_revokedLXD security eventsTrust tokens issued outside change windows; reuse suggests leakage
authn_certificate_change, user_created, user_updated, user_deletedLXD security eventsTrust store and identity changes (T1098 Account Manipulation)
Config changes to security.privileged, security.nesting, raw.*, new disk or PCI devicesLifecycle events in both projectsThe classic route to host escape (T1611)
New instances outside automationLifecycle eventsUnauthorised workloads (T1610 Deploy Container)
Execution of lxc and lxd binariesauditd rules, as in the LXD hardening guideHost-side record of who used the CLI
Group membership changes for lxd or incus-adminHost auth logsSilent privilege escalation to host root

For the snap, LXD documents daemon.syslog=true and daemon.verbose=true. Network ACLs can log unmatched traffic with security.acls.default.ingress.logged and security.acls.default.egress.logged.

Metrics

  • Privileged instances: count of instances with security.privileged=true or security.nesting=true (target zero outside an approved exception list).
  • Isolation coverage: percentage of containers with security.idmap.isolated=true on multi-tenant hosts.
  • Trust store hygiene: unrestricted certificates (target: admins only) and certificates unused for 90 days.
  • Confinement: percentage of non-admin users confined to restricted projects.
  • Patch lag: days between an LXD or Incus security release and deployment on all hosts.

Hardening baseline

  1. Inventory first: list instances with security.privileged, security.nesting, any raw.* key, disk devices with host sources and PCI or USB devices; list trust store entries and admin group members.
  2. Lock down access: reduce admin group membership, create the confined group, restrict every automation certificate to its projects, and shorten token expiry to 15 minutes.
  3. Remote API: bind core.https_address to a management IP and port (for example 192.0.2.10:8443, or 127.0.0.1:8443 for local HTTPS only), never :8443, which listens on every interface. Firewall it. If a proxy fronts it, list it in core.https_trusted_proxy only.
  4. Build a hardened profile: CPU, memory and process limits, security.idmap.isolated=true, security.nesting=false, NIC security.mac_filtering, ipv4_filtering and ipv6_filtering, read-only disk devices where possible. Apply it to a pilot project first and confirm workloads still start (isolated maps change file ownership on existing containers, so test on copies).
  5. Restricted projects for tenants: set restricted=true and loosen individual keys only with a documented reason.
  6. Network: apply network ACLs with default ingress and egress reject; the bridge otherwise accepts IPv6 router advertisements from instances (accept_ra is set to 2 on the bridge).
  7. Logging: forward lifecycle and security events to Loki or your SIEM, add auditd rules, alert on the signals above.
  8. Roll back safely: keep the previous profile version, snapshot instances before applying idmap or device changes, and use security.protection.delete=true on critical instances.

Decision guidance

  • Container or VM: use system containers for trusted Linux workloads that benefit from density; use VMs for untrusted code, anything needing kernel modules or privileged mode, and tenants you do not control.
  • Incus or LXD fine-grained access: LXD's authorisation is built in and managed with lxc auth; Incus delegates to an OpenFGA server you operate. Small teams with one or two admins can rely on restricted certificates and projects in either.
  • Single host or cluster: clusters add join tokens and inter-member certificates; shorten join token expiry and treat each member as a tier zero asset, as you would a hypervisor. See the Proxmox VE Security Hardening Guide for comparable hypervisor-level practice.

Common misconfigurations

  • Developers in the admin group for convenience: any member can attach / from the host into an instance. Use the confined group and restricted projects instead.
  • Privileged plus nesting: the LXD hardening guide warns that security.nesting=true with security.privileged=true provides root access to the host.
  • Remote API on every interface: core.https_address=:8443 on an internet-facing host exposes the trust token flow and API to the world.
  • Auto-trusting a CA: enabling core.trust_ca_certificates makes every certificate your CA ever issued an admin unless restricted.
  • Shared idmaps on multi-tenant hosts: containers share host UIDs, so a compromised container can exhaust resources owned by that UID in others.
  • Bridges without filtering: untrusted instances can spoof MAC and IP addresses on lxdbr0 or incusbr0 and send router advertisements.

Framework mapping

ControlNIST SP 800-53 Rev. 5NIST CSF 2.0ATT&CK addressed
Admin group and trust store reviewAC-2, AC-6, IA-5PR.AA-05T1078 Valid Accounts, T1098 Account Manipulation
Restricted projects and fine-grained authorisationAC-3, AC-6PR.AA-05T1610 Deploy Container
Unprivileged containers, isolated idmaps, no nestingSC-39, CM-7PR.PS-01T1611 Escape to Host, T1068 Exploitation for Privilege Escalation
Remote API binding, TLS, firewallSC-7, SC-8PR.IR-01T1133 External Remote Services
Hardened profiles and configuration baselinesCM-6PR.PS-01T1611 Escape to Host
Security and lifecycle events, auditdAU-2, AU-6PR.PS-04, DE.CM-09T1610 Deploy Container

Practitioner checklist

  • Limit the lxd or incus-admin group to people trusted with sudo, and audit it monthly.
  • Confine non-admin users with a separate group and restricted projects.
  • Restrict every automation certificate to named projects; remove unused certificates.
  • Keep core.trust_ca_certificates=false and set trust and join token expiry to 15 minutes.
  • Bind core.https_address to one management IP and port behind a firewall.
  • Apply a hardened profile: limits, security.idmap.isolated=true, nesting off, NIC spoofing filters.
  • Run untrusted or privileged workloads as VMs, not privileged containers.
  • Route image downloads through a filtering proxy.
  • Forward security and lifecycle events to your SIEM and alert on privilege and device changes.
  • Stay on a supported LTS or current feature release and patch promptly.

Standards and references

Related NHI Mgmt Group resources: Container Security Guide · Linux Namespaces and cgroups Security Guide · Proxmox VE Security Hardening Guide · Secrets Management Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 8 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.