Accessing devices from inside a docker container is a bit tricky.
The most common suggestion is to use the
--privileged flag while starting a docker container. But from a security perspective, that is a terrible thing to do. Using the
--privileged flag would give the container access and all capabilities to all the devices connected to the host (i.e. everything under the
/dev directory). This article from Trend Micro explains the risks in much more detail. You could use this site as a reference for linux capabilities.
While looking for a more secure solution, I came across this post by Marc Merlin. I used Marc's post as a reference to solve my problem.
Connect a USB device to your PC. Run the command
lsusb in your terminal. Find your USB device from the output list and note down the bus number and device number. You could use the bus number and device number to locate your device inside the
/dev directory at
This guide works for other kinds of devices as well like a block storage device, or a loop device, or an audio device. You just need to know where the device is located in your file system.
Now, start a container and try to locate the USB device inside the
/dev directory inside the container. You will notice that it's missing.
sudo docker run \ --rm -it \ ubuntu:20.04 ls /dev
The goal is to access the connected device from inside the container and be able to use it.
The most common solution is to start a container with the
sudo docker run \ --rm -it \ --privileged \ ubuntu:20.04 ls /dev
But as mentioned earlier, this should be avoided.
--device exposes devices to a container. You could mount either a single device like
/dev/ttyUSBxyz, or a directory of devices like
sudo docker run \ --rm -it \ --device /dev/bus/usb \ ubuntu:20.04 ls /dev
This method works fine. But it gives you access only to the devices that were connected to the host while starting the continer. If you reconnect a device, or connect a new device, then you won't be able to access it from inside the container.
--device-cgroup-rule flag allows you add a more permissive rule to a container allowing it access to a wider range of devices. In a way, this flag also allows you to limit access only to some devices.
Before creating the container, you need to know the major and minor number of the device you want the container to have access to. You can find the major and minor number of a device by running the
ls -l command on your device or device directory.
Here the major number is
189. And the number after
189 is the minor number for the corresponding device.
ls -l /dev/bus/usb/001/ total 0 crw-rw-r-- 1 root root 189, 0 Dec 13 18:09 001 crw-rw-r-- 1 root root 189, 1 Dec 13 18:09 002 crw-rw-r-- 1 root root 189, 2 Dec 13 18:09 003 crw-rw-r-- 1 root root 189, 3 Dec 13 18:09 004 crw-rw-r-- 1 root root 189, 4 Dec 13 18:09 005 crw-rw-r-- 1 root root 189, 5 Dec 13 18:09 006 crw-rw----+ 1 root audio 189, 6 Dec 13 19:48 007
device-cgroup-rule is written in the following format:
type major:minor mode type: a (all), or b (block), or c (char) major and minor: either a number, or * for all mode: a composition of r (read), w (write), and m (mknod)
Now start a container with the
--device-cgroup-rule flag. This gives the container access to all the devices with major number
sudo docker run \ --rm -it \ --device /dev/bus/usb \ --device-cgroup-rule 'a 189:* rwm' \ ubuntu:20.04 ls /dev
If you want the container to have access to all the devices, you could use
--device-cgroup-rule 'a *:* rwm'.
So everything should work fine now, right? Not really. If you try to reconnecting a device, or connect a new device, you still won't be able to access it from inside the container. You need to run the
mknod command inside the container each time there is a device change. That's too much work. There is a better way to solve this problem.
Instead of using the
--device flag, mount the devices or device directory as a volume.
sudo docker run \ --rm -it \ -v /dev/bus/usb:/dev/bus/usb \ --device-cgroup-rule 'a 189:* rwm' \ ubuntu:20.04 ls /dev
Now it would work perfectly fine with both old and new devices, as long as the device has a major number
Note: this step is completely optional.
In some cases, you may actually want the docker container to have some capabilities. For this, you could use the
--cap-add flag. You could use this site as a reference for linux capabilities.
sudo docker run \ --rm -it \ -v /dev/bus/usb:/dev/bus/usb \ --device-cgroup-rule 'a 189:* rwm' \ --cap-add SYS_PTRACE \ ubuntu:20.04 ls /dev
Mounting the devices as a volume along with using the
--device-cgroup-rule flag is a good way to avoid creating privileged containers.