How to Mount a Docker Volume While Excluding a Subdirectory

Estimated read time 4 min read


Docker logo

Docker Volumes provide persistent storage to your containers. Data stored in volumes is stored independently of containers so it can be restored after restarts and replacements. Volumes support use with multiple containers simultaneously, facilitating data sharing situations.

Mounting a Docker volume makes its contents available at a specific directory path inside the target container. Everything within the volume’s filesystem tree becomes accessible. This can create a challenge if you want to exclude specific subdirectories in the volume data. In this article, you’ll learn a simple technique for mounting volumes while avoiding certain paths.

Why Would You Do This?

The default behavior of mounting the entire volume is normally desirable. Volumes are intended to store data created by containers so their content should be relevant to your applications.

Volumes can also be populated using host bind mounts. These directly map a directory on your machine to a path inside your container. Changes made within the host directory will be automatically reflected in the container.

Bind mounts are commonly used to accelerate application development. You can modify source code and observe your changes without having to rebuild your Docker image. Project working directories often contain some folders which you don’t want to be mirrored though, such as node_modules and vendor. These may already exist in your container, populated during the image build steps. Excluding your local folders from being mounted lets you reliably test your code using the dependencies provided by your image.

How to Exclude Subdirectories from Docker Volume Mounts

Subdirectories can be excluded from a volume mount using a simple technique: create another mount at the path you want to ignore. If you’re mounting ~/app to /opt/app in your container, you can exclude the ~/app/node_modules directory by mounting a second empty volume to /opt/app/node_modules:

$ docker run --name app \
    -v ~/app:/opt/app \
    -v /opt/app/node_modules \
    app-image:latest

This container will start with the contents of your host’s ~/app directory accessible at /opt/app. However /opt/app/node_modules will contain the original content provided by the base image, instead of your host’s ~/app/node_modules directory.

This works because Docker automatically populates newly created empty volumes with the existing content of the destination path they’re mounted to. If you run npm install as part of your Dockerfile, /opt/app/node_modules will already contain all your dependencies. The first volume mount binds your host directory into the container but the second one overrides it with an empty volume at /opt/app/node_modules. This is then populated with the files and folders included in the image.

The order of your volume mounts is important – the subdirectory mount needs to apply after the less specific parent bind. Otherwise the contents of ~/app, including its version of node_modules, will end up overriding the empty volume that’s meant to create the exclusion.

Excluding Files

You can use a similar technique to effectively exclude individual files. Mounting your host’s /dev/null to the file path will blank it out, as if it had no content.

$ docker run --name app \
    -v /dev/null:/opt/app/config.yaml \
    app-image:latest

This only works to exclude files – /dev/null won’t map onto directory paths. The method doesn’t work to omit a file while retaining the original version from your base image either. It’ll map /dev/null onto the path, replacing any existing file that’s there.

Using Docker Compose

These techniques both work with Docker Compose too. Adjust your service definition’s volumes section to include your regular bind mount and an appropriate empty volume override.

services:
  app:
    image: app-image:latest
    build: .
    volumes:
      - ~/app:/opt/app
      - /dev/null:/opt/app/config.yaml      # Exclude file
      - /opt/app/node_modules               # Exclude directory

Running docker-compose up will have the same effect as the plain docker run example shown above.

Summary

Docker volume mounts replace everything in the container’s destination path with the contents of the bound host directory. In some situations you might want to customize this behavior by excluding specific host paths from being mounted.

In this article you’ve seen how mounting an empty volume to a subdirectory inside the container will override the first bind. The content of the container subdirectory will revert to the original content provided by your image. A variation of this technique can also be used for files, although the path will be overwritten with /dev/null instead.





Source link

You May Also Like

More From Author