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:

  1. Install docker-machine and create a default VM
  2. Add the USB device to the new VM
  3. Tell docker to use the new VM instead of Hypervisor
  4. 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

  1. Stop the default VM first so that you can change its settings in VirtualBox: docker-machine stop
  2. From VirtualBox settings for default VM, enable USB Controller and add the connected USB device.
  3. 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:

  1. docker-machine stop : Make sure the default docker-machine VM is stopped
  2. Add the USB device to the VM through VirtualBox GUI
  3. docker-machine start
  4. eval "$(docker-machine env default)": Switch docker to docker-machine instead of native macOS
  5. Use docker as you normally would to create containers or start existing ones
  6. eval $(docker-machine env -u): Once done switch back to the original docker environment

Relaed: Docker Machine with USB support on Windows/macOS