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
--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:
docker-machineand create a default VM
- Add the USB device to the new VM
dockerto use the new VM instead of Hypervisor
- Create/start containers
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
$ 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:
- From VirtualBox settings for
defaultVM, enable USB Controller and add the connected USB device.
- Start the VM again:
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
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 default
docker-machineVM is stopped
- Add the USB device to the VM through VirtualBox GUI
eval "$(docker-machine env default)": Switch
docker-machineinstead of native macOS
dockeras you normally would to create containers or start existing ones
eval $(docker-machine env -u): Once done switch back to the original docker environment