Skip to content

Lesson 3.2: Volumes

Welcome to Lesson 3.2! Now that you understand the ephemeral nature of containers, it's time to learn about volumes – Docker's recommended solution for persistent data. Volumes are managed by Docker, independent of the container lifecycle, and provide a clean, portable way to store and share data across containers. By the end of this lesson, you'll be able to use volumes to persist data, share data between containers, and manage your volume lifecycle effectively.


Learning Objectives

TIP

By the end of this lesson, you will be able to:

  • Create and manage Docker volumes using CLI commands.
  • Mount a volume into a container with the -v or --mount flag.
  • Understand the difference between named volumes and anonymous volumes.
  • List and inspect volumes with docker volume ls and docker volume inspect.
  • Remove unused volumes with docker volume prune and docker volume rm.
  • Explain use cases for volumes (databases, stateful apps, sharing data between containers).
  • Back up and restore data stored in volumes.

1. What Are Volumes?

A volume is a persistent data store managed by Docker. It exists outside the container's writable layer and is stored in a part of the host filesystem managed by Docker (typically /var/lib/docker/volumes/ on Linux). Volumes can be:

  • Named: Given a specific name (e.g., my-data) for easy reference.
  • Anonymous: Automatically generated with a random ID when you don't specify a name.

Volumes have several advantages over bind mounts:

  • Portability: Volume behavior is consistent across different Docker hosts (Docker Desktop, Linux, etc.).
  • Backup and restore: Volumes can be backed up and restored using standard Docker commands.
  • Management: Docker CLI provides commands to list, inspect, and prune volumes.
  • Performance: On Linux, volumes leverage the native filesystem and bypass the storage driver, often providing better performance for write-heavy workloads.
  • Security: Volumes are isolated from the host filesystem and can be managed with Docker's built-in permissions.

2. Working with Volumes

2.1. Creating Volumes

You can create a volume explicitly with docker volume create:

bash
docker volume create mydata

This creates a named volume called mydata. If you don't specify a driver, it uses the local driver (the default).

2.2. Listing Volumes

bash
docker volume ls

You'll see a list of all volumes, including anonymous ones. Example:

DRIVER    VOLUME NAME
local     mydata
local     0a3e2f1b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f

2.3. Inspecting a Volume

bash
docker volume inspect mydata

Output is JSON containing the volume's name, driver, mountpoint (the path on the host where data is stored), and labels. The mountpoint is where you can directly access files from the host if needed.


3. Mounting Volumes into Containers

When you run a container, you can mount a volume using either the -v (or --volume) flag or the more explicit --mount flag. Both work, but --mount is preferred for clarity in scripts and Dockerfiles.

3.1. Using -v (Volume)

Syntax:

-v VOLUME_NAME:CONTAINER_PATH[:OPTIONS]
  • VOLUME_NAME can be a named volume or an anonymous volume (if you omit a name, Docker creates an anonymous one).
  • CONTAINER_PATH is where the volume is mounted inside the container.
  • Options: ro for read-only, rw for read-write (default), z or Z for SELinux labeling.

Example (named volume):

bash
docker run -d --name db -v mydata:/var/lib/mysql mysql:8

This mounts the volume mydata at /var/lib/mysql inside the container.

Example (anonymous volume):

bash
docker run -d --name temp -v /data busybox sleep 3600

This creates an anonymous volume (random name) mounted at /data.

3.2. Using --mount

The --mount flag is more explicit and recommended for automation.

Syntax:

--mount type=volume,source=VOLUME_NAME,target=CONTAINER_PATH[,readonly]
  • type=volume indicates a volume mount.
  • source is the volume name (or for anonymous, you can omit source).
  • target is the mount point inside the container.
  • readonly (optional) mounts as read-only.

Example:

bash
docker run -d --name db --mount type=volume,source=mydata,target=/var/lib/mysql mysql:8

3.3. Mounting with Read-Only Access

To prevent the container from writing to the volume, add readonly to --mount or :ro to -v:

bash
docker run -d --name app --mount type=volume,source=config,target=/config,readonly myapp

4. Volumes in Practice: A Database Example

Let's run a MySQL container with a named volume to persist the database data.

  1. Create a volume:

    bash
    docker volume create mysql-data
  2. Run MySQL container using the volume:

    bash
    docker run -d --name mysql-db \
      -e MYSQL_ROOT_PASSWORD=secret \
      --mount type=volume,source=mysql-data,target=/var/lib/mysql \
      mysql:8
  3. Check that MySQL is running and data is being written to the volume:

    bash
    docker logs mysql-db
  4. Stop and remove the container:

    bash
    docker stop mysql-db
    docker rm mysql-db
  5. Run a new MySQL container with the same volume:

    bash
    docker run -d --name mysql-db-new \
      -e MYSQL_ROOT_PASSWORD=secret \
      --mount type=volume,source=mysql-data,target=/var/lib/mysql \
      mysql:8

    The database will contain the same data as before because the volume persisted.

This pattern is essential for stateful services like databases, message brokers, or any application that writes to disk.


5. Sharing Volumes Between Containers

Volumes can be mounted into multiple containers simultaneously. This allows containers to share data. However, be careful with concurrent writes unless the application handles locking.

Example:

  1. Create a volume:
    bash
    docker volume create shared-data
  2. Run two containers that both mount the same volume:
    bash
    docker run -d --name writer1 --mount type=volume,source=shared-data,target=/data busybox sh -c "while true; do echo $(date) >> /data/log.txt; sleep 5; done"
    docker run -d --name reader --mount type=volume,source=shared-data,target=/data busybox tail -f /data/log.txt
  3. The reader container will display the log entries written by writer1.

6. Populating a Volume with Initial Data

When you mount a volume into a container, if the volume is empty and the container's target directory contains data, Docker copies the data from the image into the volume (but only if the volume is empty). This is useful for seeding a volume with default configuration or initial database content.

Example: If you mount an empty volume at /usr/share/nginx/html for an nginx container, nginx's default content will be copied into the volume. Subsequent runs will keep that content.


7. Managing Volumes

7.1. Removing Volumes

  • Remove a specific volume: docker volume rm VOLUME_NAME
  • Remove all unused volumes: docker volume prune (interactive)
  • Force remove: docker volume rm -f VOLUME_NAME (only if not in use)

Volumes cannot be removed if they are still in use by any container. You must stop and remove the containers first.

7.2. Backup and Restore Volumes

Since volumes are stored on the host, you can back up their contents by running a temporary container that mounts the volume and archives the data.

Backup example:

bash
docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar czf /backup/mydata-backup.tar.gz -C /data .
  • -v mydata:/data mounts the volume to /data.
  • -v $(pwd):/backup mounts the current directory to /backup.
  • The tar command creates a compressed archive of /data and saves it to /backup/mydata-backup.tar.gz.

Restore example:

bash
docker run --rm -v mydata:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/mydata-backup.tar.gz --strip 1"

This extracts the backup into the volume.


8. Volume Drivers

Docker supports volume drivers that allow you to store volumes on remote systems (e.g., NFS, cloud storage, block storage). The default local driver stores volumes on the host filesystem. Third-party drivers (e.g., vsphere, azurefile, nfs) can be installed to provide advanced storage capabilities. This is an advanced topic, but worth knowing that volumes are not limited to local disk.


Hands-On Tasks

Task 1: Create and Use a Named Volume

  1. Create a volume named app-data.
  2. Run an Alpine container that writes a file to the volume, then exits.
    bash
    docker run --rm -v app-data:/data alpine sh -c "echo 'Hello Volume' > /data/hello.txt"
  3. Verify the file is in the volume by running another container that reads it:
    bash
    docker run --rm -v app-data:/data alpine cat /data/hello.txt

Task 2: Inspect and Find Host Location

  1. Inspect the volume: docker volume inspect app-data.
  2. Note the Mountpoint field. On Linux, go to that path and list the files. (On Docker Desktop, the path is inside the VM, but you can still see it with docker run --rm -v app-data:/data alpine ls -l /data.)

Task 3: Database Persistence

  1. Create a volume pg-data.
  2. Run a PostgreSQL container using that volume (use -e POSTGRES_PASSWORD=mysecretpassword).
  3. Connect to the database and create a table.
  4. Stop and remove the container.
  5. Run a new PostgreSQL container with the same volume. Verify the table still exists.

Task 4: Share Data Between Two Containers

  1. Create a volume shared.
  2. Run a producer container that appends a line to a file every 5 seconds.
  3. Run a consumer container that tails the file.
  4. Verify that the consumer sees the logs.

Task 5: Volume Cleanup

  1. List all volumes with docker volume ls.
  2. Remove the volumes you created (except those still in use) using docker volume rm.
  3. Prune all unused volumes with docker volume prune.

Task 6: Backup and Restore a Volume

  1. Create a volume test-backup and put a file in it.
  2. Perform a backup using a temporary container (as shown above).
  3. Delete the volume.
  4. Restore from the backup and verify the file is present.

Summary

Key Takeaways

  • Volumes are Docker-managed persistent storage units, ideal for stateful containers.
  • Use docker volume create to create named volumes, or let Docker create anonymous volumes automatically.
  • Mount volumes with -v or --mount when running containers.
  • Volumes survive container removal and can be shared among multiple containers.
  • Manage volumes with ls, inspect, rm, and prune.
  • Back up and restore volumes using temporary containers with tar.
  • Volumes can be backed by different drivers for remote or specialized storage.

Check Your Understanding

  1. How do you create a named volume called myvol?
  2. What is the difference between a named volume and an anonymous volume?
  3. Write the docker run command to mount a volume named dbdata at /var/lib/mysql inside a container, using --mount.
  4. How can you mount a volume as read-only?
  5. What happens when you mount an empty volume into a container at a path that already contains data in the image?
  6. How do you back up a volume to a tar file?
Click to see answers
  1. docker volume create myvol
  2. A named volume has a user-specified name for easy reference and management. An anonymous volume has a randomly generated name and is harder to manage.
  3. docker run -d --mount type=volume,source=dbdata,target=/var/lib/mysql mysql:8
  4. Add readonly to --mount or :ro to -v.
  5. Docker copies the data from the image into the volume (seeding the volume with the image's initial content).
  6. Run a temporary container: docker run --rm -v myvol:/source -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /source .

Additional Resources


Next Up

In the next lesson, we'll cover Bind Mounts – a powerful alternative for development and configuration management. See you there!