Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 18

Docker Volumes for Persistent Data

Recommended resources

Hello! Welcome back to our module on "Advanced Networking and Data Persistence."

In our last lesson, we established the crucial differences between volumes, bind mounts, and tmpfs mounts. We concluded that named volumes are the preferred, most robust method for persisting data in production environments.

Today, we'll put that theory into practice. This lesson is dedicated to the full lifecycle of named volumes, covering how to create, manage, and, most importantly, share them between multiple containers. This is a foundational pattern for building multi-service applications, a common task in your role as a DevOps Engineer.

1. The Anatomy of a Named Volume

A named volume is a data storage unit managed by Docker that exists independently of any container's lifecycle. You interact with it using a human-readable name, and Docker handles the details of where it's stored on the host machine.

Let's start by learning the basic commands to manage these volumes.

Docker Volumes Explained

The video 'Docker Volumes Explained' provides a very clear and concise walkthrough of the essential docker volume commands. It will show you how to create, list, inspect, and remove volumes.

Watch the section from 00:44 to 02:50. Pay attention to the commands docker volume create, docker volume ls, docker volume inspect, and docker volume rm.

As you saw, the core commands are straightforward:

  • docker volume create <volume_name>: Creates a new, named volume.
  • docker volume ls: Lists all volumes on your system.
  • docker volume inspect <volume_name>: Provides detailed information, including the Mountpoint on the host filesystem where the data is actually stored. While you can look at this directory, it's best practice to let Docker manage it.
  • docker volume rm <volume_name>: Deletes a volume. This will fail if the volume is currently in use by a running container.

2. Attaching a Volume and Proving Persistence

Now that we can create a volume, let's attach it to a container to store data. We'll then demonstrate that the data persists even after the container is gone.

You can attach a volume using one of two flags: --volume (or -v) and --mount.

  • --volume syntax: docker run -v <volume_name>:/path/in/container ...
  • --mount syntax: docker run --mount source=<volume_name>,target=/path/in/container ...

While -v is shorter, --mount is the more explicit and recommended syntax. It uses key-value pairs, which makes the command easier to read and allows for more advanced options that we'll encounter later.

Let's see this in action.

Docker Volumes Explained

The same video provides an excellent hands-on demonstration of this concept. It shows how to create a container with a volume, modify a file within that volume, remove the container, and then launch a new container that re-attaches the same volume to see the changes persist.

Watch the segment from 04:09 to 07:41. Follow the steps of running an Nginx container, modifying the index.html file inside the volume, and verifying that the change persists in a new container.

This simple exercise is powerful. It confirms that the volume my-nginx-volume and the data inside it have a lifecycle completely separate from any container that uses it.


3. Sharing a Single Volume Between Multiple Containers

This is where the real power of volumes comes into play for building applications. Many architectures require multiple services to access the same set of data. For example:

  • A web server that saves user uploads to a directory.
  • A backend worker service that reads from that same directory to process the uploaded files.

A shared named volume is the perfect solution for this.

Sharing Named Volumes Between Docker Containers
This diagram illustrates the core concept: a single named volume on the Docker host can be mounted into multiple containers, allowing them to share data seamlessly.

Sharing via the Docker CLI

You can achieve this by simply running multiple containers and pointing them all to the same named volume using the --mount flag.

What is Docker Volume | How to create Volumes | What is Bind Mount | Docker Storage

To see a practical example of this, let's watch a segment from the 'What is Docker Volume' video. It demonstrates sharing a volume between two different Jenkins containers, a realistic scenario where you might want to share configuration or job data.

Watch the section from 08:18 to 11:11. Notice how two separate docker run commands are used, but both specify the same volume name (-v my-volume-one...). The result is that the job created in the first container is immediately visible in the second.

Sharing via Docker Compose

While the CLI approach works, in a professional DevOps context, you'll almost always define multi-service applications using Docker Compose. Compose makes sharing volumes between services incredibly simple and declarative.

How to Share Files Between Docker Containers Using Volumes

The article 'How to Share Files Between Docker Containers Using Volumes' provides excellent examples of how to do this in a docker-compose.yml file.

Read the sections 'Sharing a Volume Between Containers' and 'Docker Compose Volume Sharing'. Focus on the structure of the Compose file: a top-level volumes key is used to declare the named volume, and then each service mounts it under its own volumes section.

The key pattern in Docker Compose is:

  1. Declare a top-level volumes block: This is where you list the named volumes your application needs.
  2. Mount the volume in each service: Under each service definition, use the volumes list to map the named volume to a path inside that service's container.

Here's a simple example based on the article, showing a web service and an api service sharing an uploads volume:

version: '3.8'

services:
  web:
    image: nginx
    volumes:
      - uploads:/var/www/html/uploads:ro # Mounts as read-only

  api:
    image: my-api
    volumes:
      - uploads:/app/uploads # Mounts as read-write

volumes:
  uploads: # Declares the named volume 'uploads'

Notice the :ro (read-only) flag on the web service. This is a crucial security practice. If a service only needs to read data from a volume, you should always mount it as read-only to prevent accidental or malicious modifications.

Test your understanding!

You are building a system with two services defined in a docker-compose.yml file:

  1. A producer service that continuously writes log lines to a file at /app/data/events.log.
  2. A consumer service that needs to read /app/data/events.log to process the logs.

Both services need to access the same events.log file. How would you structure your docker-compose.yml to achieve this using a shared named volume called log-data? Ensure the consumer cannot accidentally delete or modify the log file.

Show answer

Here is a correct docker-compose.yml structure:

version: '3.8'

services:
  producer:
    image: my-producer-app
    # This service writes to the volume
    volumes:
      - log-data:/app/data

  consumer:
    image: my-consumer-app
    # This service reads from the volume, so we use read-only
    volumes:
      - log-data:/app/data:ro

# Define the shared named volume at the top level
volumes:
  log-data:

Key elements:

  • A top-level volumes key defines the named volume log-data.
  • Both producer and consumer services mount this same volume.
  • The consumer service mounts the volume with the :ro flag to ensure it has read-only access, following the principle of least privilege.

Conclusion

You've now mastered the practical skills for managing and sharing data between containers using named volumes. This is a critical building block for designing resilient, stateful, and multi-service applications with Docker.

Key Takeaways:

  • Named volumes are Docker-managed persistent storage with a lifecycle independent of containers.
  • You can manage volumes with the docker volume create|ls|inspect|rm commands.
  • A single named volume can be mounted into multiple containers, allowing them to share data.
  • Docker Compose provides a declarative and simple way to define and share volumes among services in an application stack.
  • Using read-only (:ro) mounts is a best practice for services that only need to read data from a shared volume.

In our next lesson, we'll address a critical operational concern: implementing a backup and restore strategy for Docker volumes. After all, persistent data is only useful if you can protect it from loss.