Docker Mount Volume – How To Mount a Local Directory

As a full-stack developer, you know that containerizing your applications with Docker provides numerous benefits – it ensures a consistent runtime environment, simplifies deployment, and enables scalability and portability. In fact, according to a recent survey by Datadog, Docker adoption has grown to 25% across all companies, and over 50% in large enterprises.

However, by default, any data created inside a container is only stored within the writable layer of the container itself. This is due to Docker‘s use of a Union File System (UFS), which allows multiple filesystems to be overlaid in a single mount point. While this provides a highly efficient and performant mechanism for container filesystem management, it means that when the container is destroyed, that data is lost along with it.

Clearly, for many real-world use cases, we need a way to persist data outside the life of a given container. This is where Docker volumes come in. Volumes provide a mechanism to store data separately from the container‘s UFS and lifecycle.

In this article, we‘ll take an in-depth look at a specific type of Docker volume – binding a directory from the host filesystem and "mounting" it into a container. We‘ll walk through concrete examples of how to achieve this using the docker run command. By the end, you‘ll have the knowledge and tools to leverage host-mounted volumes in your own Docker deployments.

What are Docker Volumes?

Before we dive into the specifics of mounting directories, let‘s establish a solid understanding of Docker volumes in general.

A volume is simply a directory or filesystem that exists outside of the Union File System that Docker uses for containers. Volumes can be created and managed by Docker itself, or they can exist elsewhere on the host filesystem and be "mounted" into containers.

There are three main types of Docker volumes:

  1. Anonymous volumes: Created automatically by Docker when you use the -v flag without specifying a host directory. These are managed by Docker and you generally don‘t interact with them directly.

  2. Named volumes: Similar to anonymous volumes, but you give them an explicit name when you create them. This makes them easier to reference and share between containers.

  3. Host volumes (bind mounts): Allow you to mount a directory from the host system into a container. The host directory is referenced by its absolute path on the host machine. This is the type of volume we‘ll be focusing on.

Here‘s a visual representation of these different types of mounts:

Types of Docker Mounts

Some of the key benefits of using volumes include:

  • Decoupling container lifecycle from data lifecycle
  • Sharing data between containers and the host system
  • Incremental backups and restores
  • Migrating data between host systems

In a 2019 survey by Portworx, 85% of respondents indicated they use volumes for persistent data storage with containers, underscoring the importance of this feature.

Mounting a Host Directory as a Volume

Now that we have a high-level understanding of volumes, let‘s get into the practicalities of mounting a local directory into a Docker container.

The basic syntax for this is:

docker run -v /path/on/host:/path/in/container ...

Or, using the --mount flag:

docker run --mount type=bind,source=/path/on/host,target=/path/in/container ...

Let‘s break this down:

  • -v or --mount specifies that we‘re creating a volume
  • /path/on/host is the absolute path to the directory on the host system that we want to mount
  • /path/in/container is the path where the directory will be mounted inside the container

Here are a few concrete examples using different images:

MySQL:

docker run -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=secret -d mysql:latest

Nginx:

docker run -v /web/html:/usr/share/nginx/html:ro -d nginx:latest

PostgreSQL:

docker run -v /data/postgres:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret -d postgres:latest

In each case, the -v flag is used to bind mount a directory from the host system into the container at a specific location.

Now, any data that the application writes to the specified path inside the container will actually be stored in the mounted directory on the host system. If we destroy and recreate the container, the data will still be there.

Common Use Cases

Mounting local directories into containers is useful across a wide variety of scenarios. Here are a few common ones:

  1. Persistence of Database Data: Databases like MySQL, PostgreSQL, and MongoDB all store their data in a data directory. By mounting this directory from the host system, you ensure that your data survives even if the container is destroyed.

  2. Sharing Configuration Files: Often, you‘ll want to provide configuration files to your containerized applications. Mounting a directory containing these files allows you to easily update the configuration without having to rebuild the image.

  3. Development Environments: When developing applications, it‘s often convenient to mount your source code as a volume. This allows you to make changes on your host system and have them immediately reflected in the running container.

  4. Sharing Data Between Containers: If you have multiple containers that need to operate on the same data, mounting that data from a shared host directory is often the simplest solution.

Best Practices and Considerations

While mounting local directories is straightforward, there are a few things to keep in mind to ensure a smooth experience:

  1. File Permissions: Docker bind mounts maintain the same permissions as the host system. This means that if your application inside the container is running as a non-root user (as it should be!), you need to ensure that the mounted directory has the appropriate permissions.

  2. SELinux: If you‘re running Docker on a system with SELinux in enforcing mode, you may run into permission issues with mounted volumes. To resolve this, you can either put SELinux into permissive mode, or use the :z or :Z suffix on your volume definition to give the container the appropriate SELinux context.

  3. Mounting into Non-Empty Directories: If you mount a host directory into a non-empty directory in the container, the files in the container‘s directory will be hidden and inaccessible (though they still exist in the container‘s filesystem). Be aware of this when designing your container‘s filesystem layout.

  4. Performance: While volumes are generally quite performant, there can be some overhead associated with bind mounts, especially if you‘re mounting a large number of files. If performance is critical, you may want to consider using named volumes instead.

Volume Drivers and Advanced Topics

While we‘ve focused primarily on bind mounts in this article, Docker‘s volume system is quite flexible and extensible. Here are a few more advanced topics worth exploring:

Volume Drivers: Docker supports a wide range of volume drivers, which allow you to store your data on remote hosts or cloud providers. Some popular volume drivers include:

  • local: The default driver, which stores data on the Docker host
  • azure: Stores data in Microsoft Azure File Storage
  • gce: Stores data on Google Compute Engine disks
  • s3: Stores data on Amazon S3
  • cifs: Stores data on a CIFS/Samba share

You can even write your own custom volume driver if you have specific storage needs.

Read-Only Volumes: In some cases, you may want to mount a volume as read-only. This is useful for sharing configuration files or other static data that shouldn‘t be modified by the container. To do this, simply add :ro to the end of your -v or --mount specification.

Volume Labels: You can add labels to your volumes using the --label flag when creating them with docker volume create. Labels allow you to add metadata to your volumes, which can be useful for organization and automation.

Filtering Volumes: When listing volumes with docker volume ls, you can filter the results based on labels using the -f or --filter flag. For example, docker volume ls -f label=backup would only list volumes with the backup label.

Real-World Examples

To bring this all together, let‘s look at a couple of real-world examples of how bind mounts are used in large-scale Docker deployments.

GitLab: GitLab is a popular DevOps platform that makes extensive use of Docker for its build and CI/CD processes. In their default Docker installation, GitLab uses bind mounts to persist data for PostgreSQL, Redis, and Gitaly (GitLab‘s Git RPC service). This allows them to upgrade and replace these containers without losing data.

Discourse: Discourse is an open-source discussion platform that also relies heavily on Docker. They use bind mounts to persist data for their PostgreSQL and Redis containers. In addition, they mount the Discourse application code as a volume, which allows them to update the code without rebuilding the entire Docker image.

Conclusion

Mounting local directories into Docker containers is a crucial skill for any Docker user. It allows you to persist data, share files between the host and the container, and customize the container‘s runtime environment.

In this article, we‘ve covered the basics of Docker volumes, with a specific focus on mounting local directories using the -v or --mount flags. We‘ve looked at several common use cases and discussed some best practices and potential pitfalls.

We‘ve also explored some more advanced topics, such as volume drivers, read-only volumes, and volume labeling and filtering. Finally, we saw some real-world examples of how bind mounts are used in large-scale Docker deployments.

Armed with this knowledge, you‘re well-equipped to start using host-mounted volumes in your own Docker projects. Remember, effective data management is key to building scalable, maintainable, and portable containerized applications. Happy Dockering!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *