Mixing named volumes and bind mounting in Docker?
Host volumes: For a host volume, defined with a path in your docker compose file like:
volumes: - "./wordpress/uploads:/var/www/html/wp-content/uploads"
you will not receive any initialization of the host directory from the image contents. This is by design.
Named volumes: You can define a named volume that maps back to a local directory:
version: "2"services: your-service: volumes: - uploads:/var/www/html/wp-content/uploadsvolumes: uploads: driver: local driver_opts: type: none o: bind device: /path/on/host/to/wordpress/uploads
This will provide the initialization properties of a named volume. When your host directory is empty empty, on container creation docker will copy the contents of the image at /var/www/html/wp-content/uploads to /path/on/host/to/wordpress/uploads.
Nested mounts with Docker: If you have multiple nested volume mounts, docker will still copy from the image directory contents, not from a parent volume.
Here's an example of that initialization. Starting with the filesystem:
testvol/ data-image/ sub-dir/ from-image data-submount/ Dockerfile docker-compose.yml
The Dockerfile contains:
FROM busyboxCOPY data-image/ /data
The docker-compose.yml contains:
version: "2"services: test: build: . image: test-vol command: find /data volumes: - data:/data - subdir:/data/sub-dirvolumes: data: subdir: driver: local driver_opts: type: none o: bind device: /path/on/host/test-vol/data-submount
And the named volume has been initialized:
$ docker run -it --rm -v testvol_data:/data busybox find /data/data/data/sub-dir/data/sub-dir/from-named-vol
Running the test shows the copy comes from-image
rather than from-named-vol
:
$ docker-compose -f docker-compose.bind.yml up...Attaching to testvol_test_1test_1 | /datatest_1 | /data/sub-dirtest_1 | /data/sub-dir/from-imagetestvol_test_1 exited with code 0
And docker has copied this to the host filesystem:
$ ls -l data-submount/total 0-rw-r--r-- 1 root root 0 Jan 15 08:08 from-image
Nested mounts in Linux: From your question, there appears to be some confusion on how a mount itself works in Linux. Each volume mount runs in the container's mount namespace. This namespace gives the container its own view of a filesystem tree. When you mount a volume into that tree, you do not modify the contents from the parent filesystem, it simply covers up the contents of the parent at that location. All changes happen directly in that newly mounted directory, and if you were to unmount it, the parent directories will then be visible in their original state.
Therefore, if you mount two nested directories in one container, e.g. /data
and /data/a
, and then mount /data
in a second container, you will not see /data/a
from your first container in the second container, only the contents of /data
will be there, including any folders that were mounted over top of.
I believe the answer is to configure bind propagation.
will report back.
Edit: Seems you can only configure bind propagation on bind mounted volumes and only on linux host system.
I've tried to get this to work for hours, but I've come to the conclusion that it just won't. My case was adding a specific plugin to a CMS as a volume for local development. I want to post this here because I haven't come across this workaround anywhere.
So the following would suffer from the overlapping volumes issue, causing the folders to be empty.
services: your-service: volumes: - web-data:/var/www/html - ./wordpress/plugins:/var/www/html/wp-content/plugins - ./wordpress/themes:/var/www/html/wp-content/themes
This is how you avoid that, by binding your themes and plugins to a different directory, not inside /var/www/html
.
services: your-service: volumes: - web-data:/var/www/html - ./wordpress/plugins:/tmp/plugins - ./wordpress/themes:/tmp/themes
But now you have to get these files in the correct place, and have them still be in sync with the files on your host.
Simple version
Note: These examples assume you have a shell script as your entrypoint.
In your Docker entrypoint:
#!/bin/bashln -s /tmp/plugins/my-plugin /var/www/html/wp-content/plugins/my-pluginln -s /tmp/themes/my-theme /var/www/html/wp-content/themes/my-theme
This should work as long as your system/software resolves symlinks.
More modular solution
I only wrote this for plugins, but you could process themes the same way. This finds all plugins in the /tmp/plugins
folder and symlinks them to /var/www/html/wp-content/plugins/<plugin>
, without you having to write hard-coded folder/plugin names in your script.
#!/bin/bashTMP_PLUGINS_DIR="/tmp/plugins"CMS_PLUGINS_DIR="/var/www/html/wp-content/plugins"# Loop through all paths in the /tmp/plugins folder.for path in $TMP_PLUGINS_DIR/*/; do # Ignore anything that's not a directory. [ -d "${path}" ] || continue # Get the plugin name from the path. plugin="$(basename "${path}")" # Symlink the plugin to the real plugins folder. ln -sf $TMP_PLUGINS_DIR/$plugin CMS_PLUGINS_DIR/$plugin # Anything else you might need to do for each plugin, like installing/enabling it in your CMS.done