Skip to content

Create device nodes with ownership tailored to the container, not the host #18430

Description

@jonathan-conder

Please confirm

  • I have searched existing issues to check if an issue already exists for my feature request.

Is your feature request related to a problem? Please describe.

When adding an amd.com/gpu device, or a unix-hotplug device with ownership.inherit=true, the render group ID is taken from the host. This ID is different for all recent Ubuntu LTSs. For example, when running an ubuntu:26.04 container on a 24.04 host, /dev/kfd inside the container is owned by root:clock rather than root:render.

Describe the solution you'd like

If possible I'd like to make ownership work out of the box. On bare metal the devices we care about are managed by udev. For example kfd devices are configured by this line in /lib/udev/rules.d/50-udev-default.rules:

SUBSYSTEM=="kfd", GROUP="render", MODE="0660"

Most LXD containers already run systemd, including systemd-udevd, but it doesn't currently work:

  • Kernel uevents don't appear in the container's network namespace
  • systemd-udev-trigger.service fails to synthesize uevents because it can't write to /sys/.../uevent (I assume because the container is unprivileged)

However, there's an exception. After adding a unix-hotplug device, LXD forwards uevents related to that device into the container's netns.

My proposal is to:

  1. Synthesize uevents when adding a unix-hotplug or gpu device. Since we can't just write to /sys, I reconstructed the format of kernel uevent packets from uevent_store in the kernel. Proof-of-concept code is here: lxd-6.8...jonathan-conder:lxd:feature/add-uevent
  2. Synthesize uevents when removing a unix-hotplug or gpu device.
  3. Consider extending this to other device types.
  4. Adjust the existing unix-hotplug behaviour. It listens for uevents marked with libudev and forwards these into the container. It makes sense to listen for libudev events, because they have more information we can use for filtering. But I don't think it makes sense to forward them into the container. Instead we should synthesize a kernel-like uevent as above. This brings us closer to what happens on bare metal.
  5. Synthesize uevents when starting (and stopping?) a container. This is the hard part, because systemd-udevd won't see the events if it isn't running yet. Either we time it carefully, or find a way to make systemd-udev-trigger.service work properly (maybe intercepting system calls or patching udevadm upstream).

Describe alternatives you've considered

ACLs

Group-based ownership of device nodes is outdated, on bare metal most devices are tagged with uaccess and made available to users with an active logind seat.

LXD could add an acl config option to gpu and unix-hotplug devices, to allow clients to give certain UIDs access instead of certain GIDs. UIDs are easy to manipulate with cloud-init, and for our use case it will always be 1000.

The only reason this isn't my top suggestion is that ACLs on bare metal are also managed by udev. So implementing uevents properly would kill two birds with one stone.

Group ID mapping

LXD could expose a config option for mapping group names to IDs inside the container. This could be a device config option, a container option, or stored in the image metadata. When using CDI or ownership.inherit=true, LXD could look up the group name on the host, then consult the configured table to map the name back to an ID.

The main downside of this is that it requires extra work from either image maintainers or LXD clients. It might be difficult to maintain a comprehensive list, if some groups and udev rules are only distributed as optional packages.

Additional context

Original issue: canonical/workshop#832

Activity

  1. added this to the lxd-6.10 milestone on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions