Just putting out into a blog post some of the things I understand about Docker volumes and bind-mounts. Been doing a bit of reading on these and taking notes.
We have two types of locations that can be mounted into a Docker container. One is where we give the absolute path to a folder or file, the other is where we let Docker manage the location. The first is called a bind mount, the second is volumes. With volumes we just create a volume by name and Docker puts it in a location managed by it. For instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ docker volume create my-test-volume my-test-volume $ docker inspect my-test-volume [ { "CreatedAt": "2020-07-17T18:03:57+01:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/my-test-volume/_data", "Name": "my-test-volume", "Options": {}, "Scope": "local" } ] |
As you can see Docker created a folder under /var/lib/docker/volumes
and that’s what will get mounted into the container. (Note: this is in the case of Linux. For macOS Docker runs inside a Linux VM on macOS, so the volume path shown won’t be a path on the macOS file system but will be something in the Linux VM and we can’t access that directly. Not sure how it’s on Windows as I haven’t tried Docker on Windows yet).
There’s two ways to mount a bind-mount or volume into a container – using a -v
(or --volume
) switch or using a --mount
switch. The former is the old way, the latter is the new and preferred way. With the --mount
switch one can be more explicit.
The two switches behave similarly except for one difference when it comes to bind-mounts. If the source file or directory does not exist, the -v
switch silently creates a new directory while the --mount
switch throws an error. This is because traditionally the -v
switch created a directory and that behaviour can’t be changed now. In the case of volumes either switch simply creates the volume if it does not exist.
Between volumes and bind-mounts there’s one more difference to keep in mind (irrespective of the -v
or --mount
switches). If one were to mount an empty volume to a path which has data in a container, the data in that path is copied into this empty volume (and any further changes are of course stored in this volume).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# create a new (empty) volume $ docker volume create my_empty_volume my_empty_volume # mount this to a path with data in the container; as you can see the existing data is visible $ docker run -it --rm --mount type=volume,source=my_empty_volume,target=/etc alpine ls /etc alpine-release inittab opt services apk issue os-release shadow conf.d logrotate.d passwd shells # now if I mount this previously empty container elsewhere and inspect its data I will see that the contents above were copied into it $ docker run -it --rm --mount type=volume,source=my_empty_volume,target=/tmp2 alpine ls /tmp2 alpine-release inittab opt services apk issue os-release shadow conf.d logrotate.d passwd shells |
On the other hand if I were to do the same for a bind-mount, the existing contents of that path are hidden and only any changes are visible.
1 2 3 4 5 6 |
$ mkdir docker-data/empty $ docker run --rm -it --mount type=bind,source=$(pwd)/docker-data/empty,target=/etc alpine ls /etc hostname hosts resolv.conf $ ls docker-data/empty/ hostname hosts resolv.conf |
The three files in there are what Docker added for name resolution. And only these three files get copied into the path we mapped.
Something else I found interesting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# create a new container and mount a volume in it $ docker run -dit --mount type=volume,source=my-data-volume,target=/my-configs alpine # even though the container has exited I can see it still has a reference to this volume $ docker inspect 887f359a6a772a03c0edd0fc | jq '.[] | .Mounts' [ { "Type": "volume", "Name": "my-data-volume", "Source": "/var/lib/docker/volumes/my-data-volume/_data", "Destination": "/my-configs", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ] # now I can create a new container and refer to the volumes in the previous container. it will automatically mount and map them all. docker run -dit --volumes-from 887f359a6a77 alpine 64131192e38f9b5abdc00fa9c5923bfa626d286b365fd21eaf8e5243f90ebb8c rakhesh@pi1:~ $ docker inspect 64131192e38f9b5abdc | jq '.[] | .Mounts' [ { "Type": "volume", "Name": "my-data-volume", "Source": "/var/lib/docker/volumes/my-data-volume/_data", "Destination": "/my-configs", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ] |
This seems useful. I am still playing with Docker so I haven’t figured out how it would be useful, but it seems to be. :)