Can Docker volumes be mounted from a device instead of bind mounting a directory?
I'd like to setup a docker service (docker-compose) on a Linux host with a container mounting an entire [removable] physical hard drive as a docker volume.
I know that it's trivial to setup bind mounts in docker-compose and I can manually mount the drive on the host. That approach does work, but it involves manual steps with an opportunity for human error. Bad things happen when this service starts with a blank directory on the host's drive root partition. Worse things happen if the drive is unplugged while still mounted.
What I'd like to do is have docker mount the device directly as a volume. This would have the advantage of fewer / simpler manual steps and a failsafe that if the drive was missing the service would fail to startup. This also would ensure the drive us unmounted when the service is stopped.
Given that volumes are basically just OS mounts, it feels like this should be simple, but numerous searches through the documentation and I'm still no further forward.
Solution 1:
For docker
/ docker-compose
mounting a device into a container only is not a supported use case and thus there is no straight forward way to do this (at least as far as I know).
Update: actually this is supported by the local
volume driver, see the accepted answer. This answer is just a possible workaround.
But you could pass the block device through to the container with the --device
flag of docker run
(or the devices
attribute for docker-compose
respectively) and manually mount the device in your container - e.g. with an appropriate entrypoint script.
Be aware that mounting a block device requires enhanced capabilities that are normally dropped for a new container, so you also need to add the CAP_SYS_ADMIN
capability, like this:
docker run --device /dev/sdd1 --cap-add CAP_SYS_ADMIN my_image
(for docker-compose
see the cap_add
attribute)
You can then - either manually or in a script/custom entrypoint - mount the disk within the container:
# within the running container
mount /dev/sdd1 /mnt
This approach would only require you to add the mounting of the device to your container but would then meet all your other requirements:
- you don't need to manually mount the device on the host before starting the container
- it will refuse to start the container when the device is not available (but make sure to also manually abort on a failed
mount
!) - since the container has its own mount namespace the mounted volume is only available within the container and when the container stops the device will be automatically unmounted
- it can be set up with
docker-compose
To avoid problems with changing names of the device when un-/plugging it you can use one of the symlinks created for it in /dev/disk/by-label/
or /dev/disk/by-uuid/
.
Solution 2:
On a linux docker host this is possible by first creating named volume with a local
driver.
The named volume is just a specification for what to mount when the container starts so the device does not need to be plugged in when the volume is created.
From docker you first create a named volume. Let's say the device I want to mount is an ext4 drive and will always appear as /dev/disk/by-uuid/d28c6d3a-461e-4d7d-8737-40e56e8f384a
:
# Create "my-volume"
docker volume create --driver=local --opt type=ext4 --opt device=/dev/disk/by-uuid/d28c6d3a-461e-4d7d-8737-40e56e8f384a my-volume
# Run a container with it mounted to a path.
docker run -v my-volume:/my-volume --rm -it alpine sh
Note: Under linux device names can often change. So it's not a good idea to use something like /dev/sdb1
as you can't guarantee it will always be named sdb1
. In the example above I've used the uuid to make sure the right device is always mounted.