I needed to configure Docker container that can recognize a USB device. I used
--device option for the first release. It mounts a specific path device to the container. However, it will never recognize it again when the USB device is disconnected/connected again while the container is running.
I needed to fix this problem. Namely, I have to set up Docker container to let the container recognize the USB that is connected while the container is running.
It took me a whole day to find out the solution. I hope this post helps you out. By the way, the Docker container runs on a Linux system. If you need to try a USB device in a container on Linux system but your host system is Windows, you need to use WSL or VM.
This might help if you use WSL but some devices are not available. Use VirtualBox if your device is not available in WSL.
See this post to set up USB2.0/3.0 in VirtualBox.
Combination of mount and device-cgroup-rule
Let’s look at the solution first. Add the following two options at the same time.
--mount=type=bind,source=/dev/bus/usb,target=/dev/bus/usb --device-cgroup-rule=a *:* mrw
--device-cgroup-rule=a *:* mrw means that it allows all resources to Docker. It’s not a good idea. This will be explained later.
Device option can’t be used for USB hotplug
The device that I used was assigned under
/dev/bus/usb. It’s a scanner. When it’s connected, it’s assigned to
/dev/bus/usb/001/002 for example. However, it’s assigned to
/dev/bus/usb/001/003 when it’s disconnected and connected again.
--device=/dev/bus/usb:/dev/bus/usb, it can use USB devices that are already connected before the container starts. When a USB is newly connected, it can’t be opened due to a permission error. It shows an error like below. Privileged is added to for the existing devices but not for the newly added devices. That’s why permission error occurs.
open /dev/bus/usb/001/001: operation not permitted
--device option is not recommended to use for ephemeral devices.
The –device option cannot be safely used with ephemeral devices. You shouldn’t add block devices that may be removed to untrusted containers with –device.https://docs.docker.com/engine/reference/commandline/run/#device
Automatic binding by mount option when USB is connected
To bind a new path to the container, the target path
/dev/bus/usb needs to be bound.
I first used –volume but it’s better to use –mount because it doesn’t write anything there, doesn’t have to be shared with another container, and doesn’t need a backup. More detail for the good use case of volume/mount is described in the official page.
I tried all Linux capabilities by adding
--cap-add option but it didn’t work.
Allow the target device by device-cgroup-rule
To make it work, the device needs to be an allowed device. It can be added by using
--device-cgroup-rule=a *:* mrw
The first character is type. The possible values are as follows.
- a: all
- c: char
- b: block
*:* is major:minor of a device number. Details including type are here.
The last one is rights.
- m: mknod
- r: read
- w: write
It’s not a good idea to allow all types and devices because it leads to security risks. It’s necessary to find out which type the desired device is.
ls -l /dev. Then, I disconnected the device followed by
ls -l /dev again. Comparing the two results, I found the target device.
# when the device is connected crw-rw-rw- 1 root tty 5, 2 Oct 25 08:43 ptmx # after it's disconnected crw-rw-rw- 1 root tty 5, 2 Oct 25 2023 ptmx
As you can see the numbers
5, 2 in the result above, they are the major and minor numbers for the device.
Since the device that I needed was a scanner, it should be enough to set read right with char
--device-cgroup-rule=c 5:2 r but it actually didn’t work for some reason. It worked only when the type was set to all
--device-cgroup-rule=a 5:2 r.
To bind the newly created path under
/dev/bus/usb, the path needs to be bound. Then, the device needs to be added to the cgroup.
--mount=type=bind,source=/dev/bus/usb,target=/dev/bus/usb --device-cgroup-rule=c 5:2 r
If it’s necessary to use the options in devcontainer in VSCode, the following configurations need to be added to
"runArgs": [ "--device-cgroup-rule=a 5:* r", ], "mounts": [ "type=bind,source=/dev/bus/usb,target=/dev/bus/usb" ],
If it still doesn’t work as expected, it might be related to a user that is used in Docker container. Check which user is used in Docker container and which user owns the target path in the host machine.
If you also need a scanner in Golang, check the following post too.