I recently had to work with a device that uses serial over USB to communicate with a Linux machine. While communicating with the device from the host machine is usually trivial, doing the same from a Docker container can be surprisingly tricky. Here are four ways to communicate with a USB device.
All examples assume a Linux host and container.
1. Passing a specific device
Docker allows devices to be passed to the container. However, if the device needs to be already connected when the container starts. Otherwise, Docker will throw an error and refuse to start the container.
Example
docker run --device=/dev/ttyUSB0 ubuntu bash
or with Compose
app:
image: ubuntu
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
2. Privileged container
If you want to access all available devices, regardless of when they are connected, you can use extended privileges.
Side effect is that now, the container has full root access to the host machine. In other words it can read all devices and the whole filesystem. This can be a security issue, so use proceed with caution.
Example
docker run --privileged ubuntu bash
or with Compose
app:
image: ubuntu
privileged: true
3. Share /dev as volume
In Linux, all devices create a file under /dev
. Therefore it’s simple to just share the whole folder. To actually access the device though, we also need to share the permission tables under /run/udev
.
Side effect is that we are sharing all files in /dev
. Depending on the distribution this can cause issues. Most notably the /dev
folder of the host may contain symlinks that do not exist in the container.
Example
docker run -v /dev:/dev -v /run/udev:/run/udev:ro ubuntu bash
or with Compose
app:
image: ubuntu
volumes:
- /dev:/dev
- /run/udev:/run/udev:ro
4. Custom udev rules
If the drawbacks of the above solutions are not acceptable, there is a last, more complicated option. This involves passing a folder containing hard links to the devices. To break this down in steps:
- Create a folder in the host machine named
/dev/dockerdevices
- Mount
/dev/dockerdevices
to the container as a volume - Create a hard link in
/dev/dockerdevices
to the USB device when it is connected to the host.
The compose file looks like this
app:
image: ubuntu
volumes:
- /dev/dockerdevices:/dev/dockerdevices
For the creation of the hard link, you can use udev rules in the host machine such as the following:
SUBSYSTEM=="tty", ATTRS{product}=="Serial Adapter", ACTION=="add" \
, RUN="/bin/bash -c '/bin/mkdir /dev/dockerdevices; /bin/ln $env{DEVNAME} /dev/dockerdevices/mydevice'"
SUBSYSTEM=="tty" ATTRS{product}=="Serial Adapter", ACTION=="remove", RUN="/bin/rm -f /dev/dockerdevices/mydevice'"
Of course you would have to adjust the rules above to the properties of your device.
Comments