Reputation: 5799
This post is somewhat lenglthy but bear with me for a while...
Suppose you have an app that sits in /app
in your local (host) filesystem with the following structure
app
|-- index.php
|-- foo
| `-- file-h1
`-- bar
`-- file-h2
Now suppose we have an image (tagged myrepo/app
) that exploits the following data structure
opt
|-- app
| `-- foo
| `-- file-c1
If we run a container from that image by mounting host's /app
to container's /opt/app
as follows
docker container run \
-v /app:/opt/app \
myrepo/app
The resulting data structure of the container will be the following
opt
|-- app
| |-- index.php
| |-- foo
| | `-- file-h1
| `-- bar
| `-- file-h2
So far, so good...
Now, lets say that we want to use both a named volume called data
to be mounted on /opt/app/foo
and a bind-mount for mounting /app
to /opt/app
docker container run \
-v /app:/opt/app \
-v data:/opt/app/foo
myrepo/app
The resulting data structure inside the container will be:
opt
|-- app
| |-- index.php
| |-- foo
| | `-- file-c1
| `-- bar
| `-- file-h2
As it is stated in various posts (like this and this) the docker mounts are performed in lexicographic order (i.e shortest path first). According to this, I would expect docker first to execute the bind-mount ( -v /app:/opt/app
) and then the volume (-v data:/opt/app/foo
).
Hence, I would expect that the contents of host's /app
would replace/obscure the contents of container's /opt/app
and thus file-h1
to be inside /opt/app/foo
. Finally, file-h1
would be copied in the newly created data
volume and the volume would be mounted on /opt/app/foo
(so file-h1
should be shown instead of file-c1
)
My questions raised when I tried to understand this answer on SO
Upvotes: 21
Views: 4842
Reputation: 5799
Finally, and with a lot of help by github user cpuguy83, I figured out what actually docker engine does when we try to run a container that uses multiple mounts of different type (e.g both a bind-mount and a volume) as, for instance:
docker container run \
-v /app:/opt/app \
-v data:/opt/app/foo
myrepo/app
The key point to understand here is that docker executes the process in two steps that are done in the following order:
First, it creates a new storage-space (i.e. volume) in the host filesystem (... data/
) for the container to persist files and then (since that newly created volume is empty) it copies the container's files (i.e whatever is inside /opt/app/foo/*
) to the volume's storage location (... data/
)
Then after that, it performs all mounts (binds, volumes, tmpfs mounts, etc... are all lumped together) in lexicographic order (first mounts /app
to /opt/app
then ... data/
to /opt/app/foo
)
So, because of that, when we run the container with the mounts in our example, the docker first will copy the file-c1
to the ... data/
location in the host filesystem and second it will mount first the contents of host's /app
to the container's /opt/app
and then the host's ... data/
(which contains file-c1
) to container's /opt/app/foo
overwriting/obscuring its contents (i.e overwriting file-h1
with file-c1
). Therefore, if we take a look inside the running container after the mounts are done, the result will be the following:
opt
|-- app
| |-- index.php
| |-- foo
| | `-- file-c1
| `-- bar
| `-- file-h2
Upvotes: 31