Docker

Docker Volumes – Guide with Examples

Guide to Docker Volumes

Spacelift and Docker

Customize your workflow by bringing your own Docker image and using it as a runner to accelerate deployments that leverage third-party tools.

Book a demo

Docker containers are easiest to use with stateless applications because their filesystems are ephemeral in nature. Changes made to a container’s environment are lost when the container stops, crashes, or gets replaced.

You can Dockerize stateful applications such as databases and file servers by attaching volumes to your containers. Volumes provide persistent storage that’s independent of individual containers. You can reattach volumes to a different container after a failure or use them to share data between several containers simultaneously.

In this article, you’ll learn what volumes are and the use cases they enable. We’ll also cover some practical examples of using volumes with Docker and Docker Compose.

What Are Docker Volumes?

Volumes are a mechanism for storing data outside containers. All volumes are managed by Docker and stored in a dedicated directory on your host, usually /var/lib/docker/volumes for Linux systems.

Volumes are mounted to filesystem paths in your containers. When containers write to a path beneath a volume mount point, the changes will be applied to the volume instead of the container’s writable image layer. The written data will still be available if the container stops – as the volume’s stored separately on your host, it can be remounted to another container or accessed directly using manual tools.

Volumes work with both Linux and Windows containers. Several different drivers are available to store volume data in different services. Local storage on your Docker host is the default, but NFS volumes, CIFS/Samba shares, and device-level block storage adapters are available as alternatives. Third-party plugins can add extra storage options too.

Bind Mounts vs. Docker Volumes

Bind mounts are another way to give containers access to files and folders on your host. They directly mount a host directory into your container. Any changes made to the directory will be reflected on both sides of the mount, whether the modification originates from the host or within the container.

Bind mounts are best used for ad-hoc storage on a short-term basis. They’re convenient in development workflows. For example: bind mounting your working directory into a container automatically synchronizes your source code files, allowing you to immediately test changes without rebuilding your Docker image.

Volumes are a better solution when you’re providing permanent storage to operational containers. Because they’re managed by Docker, you don’t need to manually maintain directories on your host. There’s less chance of data being accidentally modified and no dependency on a particular folder structure. Volume drivers also offer increased performance and the possibility of writing changes directly to remote locations.

When to Use Docker Volumes?

Volumes are designed to support the deployment of stateful Docker containers. You’ll need to use a volume when a container requires persistent storage to permanently save new and modified files.

Typical volume use cases include the following:

  • Database storage – You should mount a volume to the storage directories used by databases such as MySQL, Postgres, and Mongo. This will ensure your data persists after the container stops.
  • Application data – Data generated by your application, such as file uploads, documents, and profile photos, should be stored in a volume.
  • Essential caches – Consider using a volume to persist the contents of any caches, which would take significant time to rebuild.
  • Convenient data backups – Docker’s centralized volume storage makes it easy to backup container data by mirroring /var/lib/docker/volumes to another location. Community tools and Docker Desktop extensions can automate the process, providing a much simpler experience than manually copying individual bind-mounted directories.
  • Share data between containers – Docker volumes can be mounted to multiple containers simultaneously. Containers have real-time access to the changes made by their neighbors.
  • Write to remote filesystems – You’ll need to use a volume when you want containers to write to remote filesystems and network shares. This can facilitate simpler workflows for applications that interact with your LAN resources.

You don’t need to mount a volume to containers that don’t have writable filesystem paths or that only store disposable content. As a general rule, create a volume when your container’s writing data which will cause disruption if it’s lost.

Example: Using Docker Volumes

Let’s see how volumes work. You can start a container with a volume by setting the -v flag when you call docker run.

The following command starts a new Ubuntu 22.04 container and attaches your terminal to it (-it), ready to run demonstration commands in the following steps. A volume called demo_volume is mounted to /data inside the container. Run the command now:

$ docker run -it -v demo_volume:/data ubuntu:22.04

List the contents of your container’s /data directory:

$ ls /data

The path exists, indicating the volume has mounted successfully, but no files have been created yet.

Add a test file with some arbitrary content:

$ echo "foobar" > /data/foo

$ cat /data/foo
foobar

Next, detach from your container by pressing Ctrl+C or running exit:

$ exit
exit

The container will immediately stop because there’s no other process running within it.

Now, start a new container that attaches the same volume:

$ docker run -it -v demo_volume:/app alpine:latest

The demo_volume volume already exists so Docker will reuse it instead of creating a new one. This time, the volume is mounted to a different path inside the container, but when you list the path’s content, you’ll see the file that the first container created:

$ cat /app/foo
foobar

Docker persisted the volume’s content after the first container stopped, allowing it to be reused with your replacement container.

Manually Creating and Linking Volumes

This example above demonstrated how Docker automatically creates volumes when you reference a new name for the first time. You can manually create volumes ahead of time with the docker volume create command:

$ docker volume create app_data
app_data

The volume can then be mounted to your containers in the same way as before:

$ docker run -it -v app_data:/app alpine:latest

Populating Volume Content

You can mount volumes to container paths that already contain data. When this happens, Docker will copy the existing container data into your new volume. This prevents accidental data loss. Other containers which use the volume will also see the content that’s been populated from neighboring mount points.

Mounting Volumes as Read-Only

Volumes are mounted in read-write mode by default. To mount a volume in read-only mode, include ro or readonly as the third field in your docker run command’s -v flag:

$ docker run -it -v app_data:/app:ro alpine:latest

The container will be able to read the volume’s content from the mount point but will be prevented from making modifications. This is ideal when a volume’s shared between multiple containers, only some of which are expected to perform writes.

Write operations in containers with a readonly mount will fail with an error:

$ echo "foo" > /app/bar
/bin/sh: can't create /app/bar: Read-only file system

Reusing Volumes When Containers Start

Sometimes you might want to start a new container with the same volumes as an existing container on your host. Instead of repeating the list of -v flags required, you can use --volumes-from to automatically include another container’s volumes:

# Create the first container
$ docker run -d --name db -v app_data:/data database-image:latest

# Create the second container
$ docker run -d --name backup --volumes-from db backup-image:latest

Docker will mount all the volumes that are already attached to the existing container. The same destination paths will be used to mount the volumes into your new container.

This feature is useful when you’re backing up an existing container’s volumes. You can easily mount a target container’s volumes into a new container running a dedicated backup image.

Using Volumes in Dockerfiles

Docker allows images to define volume mount points with the VOLUME Dockerfile instruction. When a container is started from an image, Docker will automatically create new volumes for the mount points listed in the Dockerfile.

The following Dockerfile will always mount a volume to /app_data inside the container, even if you call docker run without the -v flag:

FROM ubuntu:22.04
VOLUME /app_data

You can still manually mount a new or existing volume to paths referenced by VOLUME instructions. The -v flag overrides the Dockerfile’s content:

$ docker run -v custom_volume:/app_data app-image:latest

The VOLUME instruction ensures that critical paths are always persisted when users start a new container. However, it should be treated carefully because users won’t necessarily expect this behavior. Using VOLUME removes the choice of creating a purely ephemeral container for debugging or testing purposes.

Interacting With Docker Volumes

The Docker CLI includes a set of commands for interacting with the volumes on your host.

List all your volumes with docker volume ls:

$ docker volume ls
DRIVER      VOLUME NAME
local       app_data
local       demo_volume

You’ll see the name of each volume and the storage driver it’s backed by. To access more detailed information about a specific volume, use docker volume inspect instead:

$ docker volume inspect demo_volume
[
    {
        "CreatedAt": "2023-03-16T14:05:55Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/demo_volume/_data",
        "Name": "demo_volume",
        "Options": null,
        "Scope": "local"
    }
]

Delete a volume with docker volume rm:

$ docker volume rm demo_volume
demo_volume

Volumes which are currently mounted to a container can’t be deleted unless you add the -f (force) flag:

$ docker volume rm app_data -f

Finally, you can clean up all unused volumes with docker volume prune. The command deletes volumes that aren’t mounted to at least one container. You’ll be shown a confirmation prompt before the prune begins. After it completes, the total amount of freed disk space will be displayed in your terminal.

$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Total reclaimed space: 6B

Check out other Docker CLI commands in our Docker cheat sheet.

Using Volumes With Docker Compose

Volumes can also be defined and used in Docker Compose. In your docker-compose.yml file, add a top-level volumes field that lists the volumes to create, then mount your volumes into your containers within the services section:

services:
  app:
    image: app-image:latest
    volumes:
      - app_data:/data
volumes:
  app_data:

Compose automatically creates and mounts your volumes when you run docker compose up. To use an existing volume, add it to the volumes section of your docker-compose.yml file and set the external flag to true:

volumes:
  demo_volume:
    external: true

Key Points

Docker volumes provide persistent storage for your containers. Docker manages the data in your volumes separately to your containers. Volumes can be attached to multiple containers simultaneously, remain accessible after the containers they’re mounted to are stopped, and can be centrally managed using the Docker CLI.

Mount a volume whenever your containerized applications need to permanently store filesystem changes. Data stored in volumes is protected against container failures and restarts, but changes to any other paths in the container will be lost.

Looking for more information about Docker and its features? Check out our beginner’s tutorial, or browse the other articles on the Spacelift blog.

Packaging software as a container makes it more portable, allowing you to eliminate discrepancies between environments. You can use the container on your laptop, in production, and within your CI/CD infrastructure. Take a look at how Spacelift uses Docker containers to run CI jobs. You have the possibility of bringing your own Docker image and using it as a runner to speed up the deployments that leverage third party tools. Spacelift’s official runner image can be found here.

The Most Flexible CI/CD Automation Tool

Spacelift is an alternative to using homegrown solutions on top of a generic CI. It helps overcome common state management issues and adds several must-have capabilities for infrastructure management.

Start free trial