[GH-ISSUE #6] sytemd-nspawn Bind cannot bind mount with dev option #4

Closed
opened 2026-10-30 21:32:21 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @Markus328 on GitHub (Feb 17, 2026).
Original GitHub issue: https://github.com/joleuger/vuinputd/issues/6

I have an issue about systemd-nspawn containers and --placement on-host. It seems like systemd-nspawn cannot bind mount with dev option a path within a nodev /run

Firstly, I was not using --placement on-host and it worked fine, but I want to use nspawn with --private-users=pick and I got permission issues of devices created on /dev/input. seatd rejects inputs with messed ownership even though it has read permission (libinput debug-events works very well, but sway cannot enumerate any device because seatd doesn't want to).

I tried many ways of mounting /dev/input with dev, but I cannot afford to do it while using idmap mounts (to fix ownership). I found out a way that simply mounts a tmpfs on /run/.../dev-input with dev option and here systemd-nspawn copies those options in container, and I can have access to input devices on container.

mkdir -p /run/vuinputd/vuinput/dev-input
mount -t tmpfs -o rw,dev tmpfs /run/vuinputd/vuinput/dev-input

and the passing Bind in nspawn I could get a valid /dev/input inside container.

Originally created by @Markus328 on GitHub (Feb 17, 2026). Original GitHub issue: https://github.com/joleuger/vuinputd/issues/6 I have an issue about systemd-nspawn containers and `--placement on-host`. It seems like systemd-nspawn cannot bind mount with dev option a path within a `nodev` `/run` Firstly, I was not using `--placement on-host` and it worked fine, but I want to use nspawn with `--private-users=pick` and I got permission issues of devices created on `/dev/input`. `seatd` rejects inputs with messed ownership even though it has read permission (`libinput debug-events` works very well, but sway cannot enumerate any device because seatd doesn't want to). I tried many ways of mounting `/dev/input` with `dev`, but I cannot afford to do it while using `idmap` mounts (to fix ownership). I found out a way that simply mounts a tmpfs on /run/.../dev-input with `dev` option and here systemd-nspawn copies those options in container, and I can have access to input devices on container. ```bash mkdir -p /run/vuinputd/vuinput/dev-input mount -t tmpfs -o rw,dev tmpfs /run/vuinputd/vuinput/dev-input ``` and the passing `Bind` in nspawn I could get a valid `/dev/input` inside container.
Author
Owner

@joleuger commented on GitHub (Feb 19, 2026):

Hi @Markus328 , thanks a lot for digging into this and for documenting your findings so clearly. I guess what is missing is appropriate documentation. Currently, it is only written down as a side note under https://github.com/joleuger/vuinputd/blob/main/docs/USAGE.md#--placement-on-host :

run/vuinputd/{devname}/dev-input must have the mount option dev

I guess explicitly mentioning the option of mounting a tmpfs or providing an example systemd service file would have helped you a lot. For this reason I have added a warning message ( see code ). Seems it was not helpful.

Another option seems to be to create the device with the uid and gid of the parent directory. I actually plan to checkin such a solution, but I haven't managed it yet to do my test setup to confirm that such a solution is working.


pub fn acquire_gid_and_uid_of_path(path: &str) -> anyhow::Result<()> {
    let path = Path::new(path);

    // 1. Get the raw UID/GID of the directory from the Host's perspective
    //    This returns the mapped ID (e.g., 100000), not the container ID (0).
    let metadata = fs::metadata(path)?;
    let target_uid = metadata.uid();
    let target_gid = metadata.gid();

    // 2. Temporarily switch filesystem identity
    //    We use libc directly here. Note: setfsgid usually needs to happen before setfsuid
    //    because changing UID might drop privileges to change GID.
    unsafe {
        libc::setfsgid(target_gid);
        libc::setfsuid(target_uid);
    }
    Ok(())
}

Thanks again for the detailed report. Do you mind to attach your service files? Those might help other users having similar issues.

<!-- gh-comment-id:3925407112 --> @joleuger commented on GitHub (Feb 19, 2026): Hi @Markus328 , thanks a lot for digging into this and for documenting your findings so clearly. I guess what is missing is appropriate documentation. Currently, it is only written down as a side note under https://github.com/joleuger/vuinputd/blob/main/docs/USAGE.md#--placement-on-host : > run/vuinputd/{devname}/dev-input must have the mount option dev I guess explicitly mentioning the option of mounting a tmpfs or providing an example systemd service file would have helped you a lot. For this reason I have added a warning message ( see [code](https://github.com/joleuger/vuinputd/blob/fd55bffddb9e3511ba57b7e6c2af3dfe1382e903/vuinputd/src/input_realizer/host_fs.rs#L63) ). Seems it was not helpful. Another option seems to be to create the device with the uid and gid of the parent directory. I actually plan to checkin such a solution, but I haven't managed it yet to do my test setup to confirm that such a solution is working. ``` pub fn acquire_gid_and_uid_of_path(path: &str) -> anyhow::Result<()> { let path = Path::new(path); // 1. Get the raw UID/GID of the directory from the Host's perspective // This returns the mapped ID (e.g., 100000), not the container ID (0). let metadata = fs::metadata(path)?; let target_uid = metadata.uid(); let target_gid = metadata.gid(); // 2. Temporarily switch filesystem identity // We use libc directly here. Note: setfsgid usually needs to happen before setfsuid // because changing UID might drop privileges to change GID. unsafe { libc::setfsgid(target_gid); libc::setfsuid(target_uid); } Ok(()) } ``` Thanks again for the detailed report. Do you mind to attach your service files? Those might help other users having similar issues.
Author
Owner

@Markus328 commented on GitHub (Feb 23, 2026):

Sorry for late reply, I just forgot this issue. I really noticed the container would have the /dev mounted as dev option, I think using other container solution I could pass dev in the bind mount. On nspawn at least, it wasn't possible. If mounting the actual host's /run/vuinputd/.../dev-input as tmpfs using dev option should be done, I liked it would be done automatically by the tool.

As I said, I don't knew or know if podman, lxc or so can give me the option to just bind mount passing dev option to the mounted dir in the container (I need to read the docs, but I don't use them as often), and if not, it would be good to clarify better at the docs or even mount the host's /run/.../dev-input automatically to avoid those issues or manual mouting.

There's nothing special with my service configs, I'm using a nixos file to define the whole nspawn container as well as firewall setup and vuinputd daemon on host. I've used a ExecStartPre with a script that mounts /run/vuinputd/vuinput/dev-input as tmpfs with dev option to make the container work with :idmap bind mount.

<!-- gh-comment-id:3942403555 --> @Markus328 commented on GitHub (Feb 23, 2026): Sorry for late reply, I just forgot this issue. I really noticed the container would have the /dev mounted as dev option, I think using other container solution I could pass dev in the bind mount. On nspawn at least, it wasn't possible. If mounting the actual host's `/run/vuinputd/.../dev-input` as tmpfs using dev option should be done, I liked it would be done automatically by the tool. As I said, I don't knew or know if podman, lxc or so can give me the option to just bind mount passing dev option to the mounted dir in the container (I need to read the docs, but I don't use them as often), and if not, it would be good to clarify better at the docs or even mount the host's `/run/.../dev-input` automatically to avoid those issues or manual mouting. There's nothing special with my service configs, I'm using a nixos file to define the whole nspawn container as well as firewall setup and vuinputd daemon on host. I've used a `ExecStartPre` with a script that mounts `/run/vuinputd/vuinput/dev-input` as `tmpfs` with dev option to make the container work with `:idmap` bind mount.
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
joleuger/vuinputd#4
No description provided.