How to use a USB device in a Docker container on Mac
I have been playing around with Intel Movidius Neural Compute Stick recently and while they’re finally adding official support for macOS to their SDK, I still prefer to use it in a virtualised or even better container environment to keep things clear. This is (relatively) easy in VMs but I realised very soon that talking to USB devices from a Docker container running on Mac is not straight-forward.
Accessing USB devices in containers is easy in Linux hosts using --privileged
and --device
flags which respectively enable access to all or individual devices from the host inside the container. Unfortunately these flags don’t work in macOS. Latest versions of Docker for macOS use HyperKit, a native lightweight virtualisation solution built on top of the Hypervisor framework which does not support USB passthrough.
Before Docker ran natively on Mac it relied on another tool, docker-machine
, which created a virtual machine using drivers such as VirtualBox, ran Docker on that VM and coordinated interactions between the host, docker and the VM.
docker-machine
is still available today to provision Docker hosts on remote systems or run Docker on older systems. Since VirtualBox allows passing USB devices to its VMs we can use docker-machine
with VirtualBox driver to mount USB devices in containers! Assuming that Docker, VirtualBox and VirtualBox Extension Pack are already installed on your Mac the process can be summarised as:
- Install
docker-machine
and create a default VM - Add the USB device to the new VM
- Tell
docker
to use the new VM instead of Hypervisor - Create/start containers
Install docker-machine
You can follow the official documentation for more details but all you need is:
$ base=https://github.com/docker/machine/releases/download/v0.14.0 &&
curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/usr/local/bin/docker-machine &&
chmod +x /usr/local/bin/docker-machine
Make sure the installation has been successful and docker-machine
binary is on your PATH
:
$ docker-machine version
docker-machine version 0.14.0, build 89b8332
Create the default VM
Since this is our first VM we are going to name it default
. Most of the docker-machine
commands assume that the given operation should be run on a machine named default
if no machine name is specified. The command below downloads a lightweight Linux distribution (boot2docker) with the Docker daemon installed, and creates and starts a VirtualBox VM with Docker running:
docker-machine create --driver virtualbox default
You can use docker-machine ls
to confirm that the VM is installed successfully:
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Running tcp://192.168.99.100:2376 v18.03.1-ce
Add the USB device to the new VM
- Stop the default VM first so that you can change its settings in VirtualBox:
docker-machine stop
- From VirtualBox settings for
default
VM, enable USB Controller and add the connected USB device. - Start the VM again:
docker-machine start
Tell Docker to talk to the VM
We need to set a few environment variables to tell Docker to use the new VM instead of the native mode. docker-machine env
commands tells us how to do that:
$ docker-machine env default
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/milad/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
# Run this command to configure your shell:
# eval $(docker-machine env default)
So all we need to do is:
eval "$(docker-machine env default)"
Note this command only sets the environment in your current shell
We’re done!
Go ahead and create/start your containers. You should be able see the USB device in your container now. With everything set up, the next time I need a container with a USB device I follow these steps:
docker-machine stop
: Make sure the defaultdocker-machine
VM is stopped- Add the USB device to the VM through VirtualBox GUI
docker-machine start
eval "$(docker-machine env default)"
: Switchdocker
todocker-machine
instead of native macOS- Use
docker
as you normally would to create containers or start existing ones eval $(docker-machine env -u)
: Once done switch back to the original docker environment