Last time I checked, Docker didn't have any means to give container access to host serial or USB port. Is there a trick which allows doing that?
-
3If you're on a Mac here's a link on how to do it: https://dev.to/rubberduck/using-usb-with-docker-for-mac-3fdd – Pierz Feb 03 '21 at 15:24
-
2It's a great article, but unfortunately requires installation of VirtualBox. – akauppi Feb 21 '22 at 20:11
11 Answers
There are a couple of options. You can use the --device
flag that use can use to access USB devices without --privileged
mode:
docker run -t -i --device=/dev/ttyUSB0 ubuntu bash
Alternatively, assuming your USB device is available with drivers working, etc. on the host in /dev/bus/usb
, you can mount this in the container using privileged mode and the volumes option. For example:
docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash
Note that as the name implies, --privileged
is insecure and should be handled with care.

- 32,811
- 7
- 87
- 85
-
14
-
30
-
Using this solution I don't see devices from a docker container... Here are details http://stackoverflow.com/questions/37213812 of my problem. Appreciate of any help! Thanks. – kashesandr May 19 '16 at 20:36
-
5Still doesn't work if the USB device is connected after Docker is already running. – Franklin Dattein Oct 25 '16 at 19:38
-
2I mean, it doesn't map the device under /tty/USBX despite lsusb being able to list it. – Franklin Dattein Oct 25 '16 at 19:53
-
You can map USB devices in Windows, since you would most likely be using either VirtualBox or HyperV (Docker Windows) there is a possibility to map the USB device into the Docker host - and then follow the run command in this answer. – Evgeny Feb 06 '17 at 03:17
-
@ben-whaley Your first approach worked for me with Ubuntu 14.04 (already tried mapping /dev/bus/usb and it didn't work), but both methods didn't work on Ubuntu 16.04... Can anyone help me figuring it out – jemlifathi Jan 22 '18 at 10:46
-
You’d need to give a bit more info about the error for anyone to help! – Ben Whaley Jan 22 '18 at 13:18
-
Using this solution I can see devices from a docker container after 1 more addition step. First, I did "adb kill-server" in the host machine. Then, log into the docker I can see my android device with "adb devices" – Nam Pham Dec 10 '18 at 14:40
-
[It won't work on macOS](https://stackoverflow.com/a/54331344/55075) as the daemon runs inside a VM. – kenorb Jan 23 '19 at 16:07
-
Even tought this is the most upvoted answer and easiest solution, it is not proper for a production environment. For a more secure approach I suggest to check the awesome explanetion of Wout_bb just below. – daniele piscaglia Dec 16 '20 at 08:18
-
Exposing the usb device to a WSL2 VM using USB IP is supported for a while: https://learn.microsoft.com/en-us/windows/wsl/connect-usb The Docker Desktop for Windows VM was meant to be immutable, so running the usbip piece there may be challenging - I didn't try. – Saturnus Mar 23 '23 at 19:35
-
@Pascal If we're talking about natively with docker desktop not in the least. As saturnus pointed out a usb device can be exposed via usb ip to the underlining wsl hypervisor but in my experiences setting up asterisk pbx, mysticbbs, and even uucp over serial lines one has better luck using ser2net or socat to expose the com port over raw tcp (aka telnet) then run socat in the container to bring the tcp down to a unix domain socket. – Dwight Spencer Apr 26 '23 at 07:33
With current versions of Docker, you can use the --device
flag to achieve what you want, without needing to give access to all USB devices.
For example, if you wanted to make only /dev/ttyUSB0
accessible within your Docker container, you could do something like:
docker run -t -i --device=/dev/ttyUSB0 ubuntu bash

- 30,738
- 21
- 105
- 131

- 3,357
- 30
- 31
-
4just note that the device cannot be a symlink at the moment. https://github.com/docker/docker/issues/13840 – wligtenberg Nov 18 '15 at 20:37
-
7using the `--device` flag, how do I determine which `/dev/
` is the associated Android device on the host machine, especially when using Docker Quickstart Terminal (VirtualBox Host) for Windows or Mac? – DanCat Nov 24 '15 at 19:03 -
1This works well if your device name never changes. But if you are using something dynamic which uses devices within /dev/bus/usb then this won't work because the device name changes when you plug and unplug it. You'll need the above -v (volumes) solution instead. – Brad Grissom Apr 07 '16 at 16:48
-
3@DanCat udev rules can ensure that your device mounts to a static path – C. Reed Jun 21 '16 at 00:00
-
--device=/dev/ttyUSB0 only works if the USB device was plugged before Docker started. Even so, if you unplug it doesn't auto detect and crash nodeserial library from node and probably libs in other programming languages. – Franklin Dattein Oct 26 '16 at 10:03
-
2Why would anyone be interested in having access to just one usb device?? Usb devices are meant to be connected qnd disconnected and that needs to be done during the apps runtime. USB is not SATA or something, you can't expect something to always be there... And I don't think people just start apps via docker for single runs and quit them as soon as the usb devices are disconnected, right? I'd imagine more like service type apps, not single run jars... But thanks, indeed that might help some for which that very limited scenario would suit – Arturas M Sep 22 '17 at 18:34
-
how will this work in windows 10? I have usb device that I want to access in docker image – user482963 Dec 28 '17 at 13:55
-
4@arturas-m e.g. Lots of sensors are USB these days. Sensors don't need to be plugged out in many usecases IMO. – IsaacS May 09 '18 at 03:56
-
1@DanCat both lsusb and dmesg can help here and also check syslog for udev entries. but generally for debian/ubuntu and redhat based systems it's going to be one of /dev/ttyUSB* or /dev/ttyS* where 0 is the first device attached/udev configured. – Dwight Spencer Apr 26 '23 at 07:39
--device
works until your USB device gets unplugged/replugged and then it stops working. You have to use cgroup devices.allow get around it.
You could just use -v /dev:/dev
but that's unsafe as it maps all the devices from your host into the container, including raw disk devices and so forth. Basically this allows the container to gain root on the host, which is usually not what you want.
Using the cgroups approach is better in that respect and works on devices that get added after the container as started.
See details here: Accessing USB Devices In Docker without using --privileged
It's a bit hard to paste, but in a nutshell, you need to get the major number for your character device and send that to cgroup:
188 is the major number of /dev/ttyUSB*, which you can get with 'ls -l'. It may be different on your system than on mine:
root@server:~# echo 'c 188:* rwm' > /sys/fs/cgroup/devices/docker/$A*/devices.allow
(A contains the docker containerID)
Then start your container like this:
docker run -v /dev/bus:/dev/bus:ro -v /dev/serial:/dev/serial:ro -i -t --entrypoint /bin/bash debian:amd64
without doing this, any newly plugged or rebooting device after the container started, will get a new bus ID and will not be allowed access in the container.

- 2,026
- 5
- 19
- 25

- 566
- 4
- 6
-
15To folks who -1'ed this, please help and say what you'd like to be improved. I wrote this page to help others who hit the problem we did. I'll be a bit honest in saying that I'm being turned off from trying to share back and help folks on stackoverflow too :-/ – Marc Merlin Dec 25 '18 at 04:33
-
If you read my answer, you'll see that adding the volume '-v /dev:/dev' will give access to dynamically plugged devices. – rrpilot Jan 03 '19 at 15:02
-
8rrpilot: -v /dev:/dev does give you all of /dev, including /dev/sda and other things you really don't want to expose to a root user in the container. In other words, your solution does work, but it's unsafe. Mine gets around that problem. I'll edit my answer to point that out. – Marc Merlin Feb 28 '19 at 03:29
-
3The answer could be made better by showing how to get the major number and clarifying that `189` must be replaced. A description of what to send `devices.allow` can be found here: https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt – Craig Younkins Apr 08 '19 at 02:43
-
-
5There's a new-ish feature of Docker that makes this slightly simpler: "--device-cgroup-rule" (https://docs.docker.com/engine/reference/commandline/create/#dealing-with-dynamically-created-devices---device-cgroup-rule) – tianon Oct 03 '19 at 14:56
-
It's probably because your answer does not contain the relevant code snippets, merely a link. Without the contents of that blog, this answer is hardly useful, it appears – phil294 Nov 18 '21 at 18:51
-
/dev/serial is not a common device, do applaud the note about device magic numbers, but the general rule is serial devices so up as /dev/ttyS* or /dev/ttyUSB* can be detected in syslog (udev), lsusb, or dmesg. – Dwight Spencer Apr 26 '23 at 07:46
The Safe and Proper way of accessing tty devices without --privileged mode
Just follow the instruction line by line, all steps are explained
Idea is to configure cgroup rules properly. First of all, lets find cgroup properties of your USB device. Run following command:
$ ls -l /dev/ | grep ttyUSB
crw-rw-rw- 1 root dialout 188, 0 Mar 1 18:23 ttyUSB0 #Example output
Based on the output you can see that the major group of tty devices is 188
in my case, so I will proceed with that.
You can run docker image allowing access to range of devices with specific major number, docker will add required rules for you in your host machine (this will run docker in detached mode, we will attach to it later):
docker run --device-cgroup-rule='c 188:* rmw' -itd --name my_container ubuntu
Now the idea is to add a script which would be run every time your USB device is plugged in or plugged out. Some explanation about custom rules here and here on passing arguments. On ubuntu, you should create file /etc/udev/rules.d/99-docker-tty.rules
as superuser (sudo):
ACTION=="add", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'added' '%E{DEVNAME}' '%M' '%m'"
ACTION=="remove", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'removed' '%E{DEVNAME}' '%M' '%m'"
This file adds new entry to your rules, basically saying: Every time tty device is plugged in - add
or plugged out - remove
run the provided script and pass some parameters. If you want to be more specific, you can use udevadm info --name=<device name>
to find other parameters by which you can filter devices. You can test the rules as suggested here. To apply those rules:
root@~$ udevadm control --reload
Now we need to create following script in /usr/local/bin/docker_tty.sh
also as superuser (sudo). You can see it was set to be run in udev rules we created previously.
#!/usr/bin/env bash
echo "Usb event: $1 $2 $3 $4" >> /tmp/docker_tty.log
if [ ! -z "$(docker ps -qf name=env_dev)" ]
then
if [ "$1" == "added" ]
then
docker exec -u 0 env_dev mknod $2 c $3 $4
docker exec -u 0 env_dev chmod -R 777 $2
echo "Adding $2 to docker" >> /tmp/docker_tty.log
else
docker exec -u 0 env_dev rm $2
echo "Removing $2 from docker" >> /tmp/docker_tty.log
fi
fi
This script will create the tty device in your running docker container, or delete it depending if the device was plugged in or plugged out (similar to whats happening with the Ubuntu machine - every time the device is plugged in, you can see it under /dev/
directory). Tip: Check the file /tmp/docker_tty.log
for some debug output on your host machine, also, debug bash script as suggested here.
Dont forget to make script executable:
root@~$ chmod +x /usr/local/bin/docker_tty.sh
Now attach to the docker and see if the device appears in /dev/
directory when you plug it in and out:
docker exec -it my_container bash

- 811
- 6
- 12
-
1
-
1Should not there be `docker exec -u 0 $id mkdir -p (dirname $usb_path)` before `docker exec -u 0 $id mknod $usb_path c $argv[3] $argv[4]`? – DimanNe Dec 20 '21 at 19:24
I wanted to extend the answers already given to include support for dynamically connected devices that aren't captured with /dev/bus/usb
and how to get this working when using a Windows host along with the boot2docker VM.
If you are working with Windows, you'll need to add any USB rules for devices that you want Docker to access within the VirtualBox manager. To do this you can stop the VM by running:
host:~$ docker-machine stop default
Open the VirtualBox Manager and add USB support with filters as required.
Start the boot2docker VM:
host:~$ docker-machine start default
Since the USB devices are connected to the boot2docker VM, the commands need to be run from that machine. Open up a terminal with the VM and run the docker run command:
host:~$ docker-machine ssh
docker@default:~$ docker run -it --privileged ubuntu bash
Note, when the command is run like this, then only previously connected USB devices will be captures. The volumes flag is only required if you want this to work with devices connected after the container is started. In that case, you can use:
docker@default:~$ docker run -it --privileged -v /dev:/dev ubuntu bash
Note, I had to use /dev
instead of /dev/bus/usb
in some cases to capture a device like /dev/sg2
. I can only assume the same would be true for devices like /dev/ttyACM0
or /dev/ttyUSB0
.
The docker run commands will work with a Linux host as well.

- 393
- 3
- 12
-
1Good point in mounting /dev:/dev instead. That gives more flexibility in terms of capturing other devices, and also helps with the dynamic element. – kotakotakota Feb 20 '19 at 19:35
-
1and also compromises the security and isolation of the your host machine. – Exadra37 Jul 07 '20 at 11:57
-
1@Exadra37 It does... and if that matters in your application, you shouldn't use this. However, its important to note there are some applications where you don't care and aren't using docker for its isolation. In my specific case, you get to run a packaged Linux application on Windows. – rrpilot Jul 08 '20 at 12:52
-
nice solution. you just saved my ton of time to figure out how to automatic detect new pluged usb devices. – temple Sep 18 '21 at 00:47
If you would like to dynamically access USB devices which can be plugged in while the docker container is already running, for example access a just attached usb webcam at /dev/video0, you can add a cgroup rule when starting the container. This option does not need a --privileged container and only allows access to specific types of hardware.
Step 1
Check the device major number of the type of device you would like to add. You can look it up in the linux kernel documentation. Or you can check it for your device. For example to check the device major number for a webcam connected to /dev/video0, you can do a ls -la /dev/video0
. This results in something like:
crw-rw----+ 1 root video 81, 0 Jul 6 10:22 /dev/video0
Where the first number (81) is the device major number. Some common device major numbers:
- 81: usb webcams
- 188: usb to serial converters
Step 2
Add rules when you start the docker container:
- Add a
--device-cgroup-rule='c major_number:* rmw'
rule for every type of device you want access to - Add access to udev information so docker containers can get more info on your usb devices with
-v /run/udev:/run/udev:ro
- Map the /dev volume to your docker container with
-v /dev:/dev
Wrap up
So to add all usb webcams and serial2usb devices to your docker container, do:
docker run -it -v /dev:/dev --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 81:* rmw' ubuntu bash

- 387
- 4
- 9
Another option is to adjust udev, which controls how devices are mounted and with what privileges. Useful to allow non-root access to serial devices. If you have permanently attached devices, the --device
option is the best way to go. If you have ephemeral devices, here's what I've been using:
1. Set udev rule
By default, serial devices are mounted so that only root users can access the device. We need to add a udev rule to make them readable by non-root users.
Create a file named /etc/udev/rules.d/99-serial.rules. Add the following line to that file:
KERNEL=="ttyUSB[0-9]*",MODE="0666"
MODE="0666" will give all users read/write (but not execute) permissions to your ttyUSB devices. This is the most permissive option, and you may want to restrict this further depending on your security requirements. You can read up on udev to learn more about controlling what happens when a device is plugged into a Linux gateway.
2. Mount in /dev folder from host to container
Serial devices are often ephemeral (can be plugged and unplugged at any time). Because of this, we can’t mount in the direct device or even the /dev/serial folder, because those can disappear when things are unplugged. Even if you plug them back in and the device shows up again, it’s technically a different file than what was mounted in, so Docker won’t see it. For this reason, we mount the entire /dev folder from the host to the container. You can do this by adding the following volume command to your Docker run command:
-v /dev:/dev
If your device is permanently attached, then using the --device option or a more specific volume mount is likely a better option from a security perspective.
3. Run container in privileged mode
If you did not use the --device option and mounted in the entire /dev folder, you will be required to run the container is privileged mode (I'm going to check out the cgroup stuff mentioned above to see if this can be removed). You can do this by adding the following to your Docker run command:
--privileged
4. Access device from the /dev/serial/by-id folder
If your device can be plugged and unplugged, Linux does not guarantee it will always be mounted at the same ttyUSBxxx location (especially if you have multiple devices). Fortunately, Linux will make a symlink automatically to the device in the /dev/serial/by-id folder. The file in this folder will always be named the same.
This is the quick rundown, I have a blog article that goes into more details.

- 3,005
- 19
- 17
It's hard for us to bind a specific USB device to a docker container which is also specific. As you can see, the recommended way to achieve is:
docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb ubuntu bash
It will bind all the devices to this container. It's unsafe. Every containers were granted to operate all of them.
Another way is binding devices by devpath. It may looks like:
docker run -t -i --privileged -v /dev/bus/usb/001/002:/dev/bus/usb/001/002 ubuntu bash
or --device
(better, no privileged
):
docker run -t -i --device /dev/bus/usb/001/002 ubuntu bash
Much safer. But actually it is hard to know what the devpath of a specific device is.
I have wrote this repo to solve this problem.
https://github.com/williamfzc/usb2container
After deploying this server, you can easily get all the connected devices' information via HTTP request:
curl 127.0.0.1:9410/api/device
and get:
{
"/devices/pci0000:00/0000:00:14.0/usb1/1-13": {
"ACTION": "add",
"DEVPATH": "/devices/pci0000:00/0000:00:14.0/usb1/1-13",
"DEVTYPE": "usb_device",
"DRIVER": "usb",
"ID_BUS": "usb",
"ID_FOR_SEAT": "xxxxx",
"ID_MODEL": "xxxxx",
"ID_MODEL_ID": "xxxxx",
"ID_PATH": "xxxxx",
"ID_PATH_TAG": "xxxxx",
"ID_REVISION": "xxxxx",
"ID_SERIAL": "xxxxx",
"ID_SERIAL_SHORT": "xxxxx",
"ID_USB_INTERFACES": "xxxxx",
"ID_VENDOR": "xxxxx",
"ID_VENDOR_ENC": "xxxxx",
"ID_VENDOR_FROM_DATABASE": "",
"ID_VENDOR_ID": "xxxxx",
"INTERFACE": "",
"MAJOR": "189",
"MINOR": "119",
"MODALIAS": "",
"PRODUCT": "xxxxx",
"SEQNUM": "xxxxx",
"SUBSYSTEM": "usb",
"TAGS": "",
"TYPE": "0/0/0",
"USEC_INITIALIZED": "xxxxx",
"adb_user": "",
"_empty": false,
"DEVNAME": "/dev/bus/usb/001/120",
"BUSNUM": "001",
"DEVNUM": "120",
"ID_MODEL_ENC": "xxxxx"
},
...
}
and bind them to your containers. For example, you can see the DEVNAME of this device is /dev/bus/usb/001/120
:
docker run -t -i --device /dev/bus/usb/001/120 ubuntu bash
Maybe it will help.

- 39
- 2
There is also a simpler way of sharing usb devices without the --privileged
flag. To do so:
docker run ... --device=/dev/bus/usb --device=/dev/usb <container>

- 169
- 2
With latest versions of docker, this is enough:
docker run -ti --privileged ubuntu bash
It will give access to all system resources (in /dev for instance)

- 33
- 2
-
3priviledged is a terrible option to use for security, even though yes, it works. – Marc Merlin Apr 01 '19 at 06:43
-
3If used for programing stuff like Arduino related stuff this solution is good – Jose Cabrera Zuniga Jun 11 '19 at 17:00
Adding to the answers above, for those who want a quick way to use an external USB device (HDD, flash drive) working inside docker, and not using priviledged mode:
Find the devpath to your device on the host:
sudo fdisk -l
You can recognize your drive by it's capacity quite easily from the list. Copy this path (for the following example it is /dev/sda2
).
Disque /dev/sda2 : 554,5 Go, 57151488 octets, 111624 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Mount this devpath (preferable to /media
):
sudo mount <drive path> /media/<mount folder name>
You can then use this either as a param to docker run
like:
docker run -it -v /media/<mount folder name>:/media/<mount folder name>
or in docker compose under volumes:
services:
whatevermyserviceis:
volumes:
- /media/<mount folder name>:/media/<mount folder name>
And now when you run and enter your container, you should be able to access the drive inside the container at /media/<mount folder name>
DISCLAIMER:
- This will probably not work for serial devices such as webcams etc. I have only tested this for USB storage drives.
- If you need to reconnect and disconnect devices regularly, this method would be annoying, and also would not work unless you reset the mount path and restart the container.
- I used docker 17.06 + as prescribed in the docs

- 2,334
- 1
- 37
- 79