Examples of volumes in Docker Compose

Purpose

The purpose of this post is to review how we can use volumes in Docker and Docker Compose. These are some possible scenarios:

  • Use one/various volumes by one service/container.
  • Use one/various volumes by one set of services (defined in the same docker-compose.yml file).
  • Use one/various volumes across the Docker installation.

Before you begin

In this tutorial, we’ll learn how to use Docker Compose volumes. A GNU Linux/Mac OS/Windows machine with Docker and Docker Compose installed is required to follow this tutorial.

How to declare volumes in Docker

There are two ways of declaring volumes in Docker: The imperative way (Docker client) and the declarative way (Docker Compose yaml file or Docker Dockerfile).

In the this post you’ll see only how to do it in a declarative manner using a docker-compose file. But it’s worth mention that is also possible to declare volumes in Docker using their command line client:

docker volume create [OPTIONS] [VOLUME]

Types of volumes in Docker

1. Docker host-mounted volumes

Syntax: /host/path:/container/path

Host path can be defined as an absolute or as a relative path.

Example:

version '3'

services:
  app:
    image: nginx:alpine
    ports:
      - 80:80
    volumes:
      - /var/opt/my_website/dist:/usr/share/nginx/html:ro

2. Docker named volumes

Syntax: named_volume_name:/container/path

Named volumes can be defined as internal (default) or external.

2.1. Docker internal named volumes

Docker compose internal named volumes have the scope of a single Docker-compose file and Docker creates them if they don’t exist.

Docker Compose file example with a named volume web_data:

version '3'

volumes:
  web_data:

services:
  app:
    image: nginx:alpine
    ports:
      - 80:80
    volumes:
      - web_data:/usr/share/nginx/html:ro

TIP 1: From Docker Compose version 3.4 the name of the volume can be dynamically generated from environment variables placed in an .env file (this file has to be in the same folder as docker-compose.yml is).

TIP 2: To increase the security in our system we can mount the volume as read-only if the container only needs to read the mounted files. This will prevent an attacker to modify or create new files in the host of the server for example.

Example of .env file:

VOLUME_ID=my_volume_001

Example of a Docker Compose file with and internal docker named volume based on an environment variable:

version '3.4'

volumes:
  web_data:
    name: ${VOLUME_ID}

services:
  app:
    image: nginx:alpine
    ports:
      - 80:80
    volumes:
      - web_data:/usr/share/nginx/html:ro

docker-compose up will generate a volume called my_volume_001.

2.2. Docker external named volumes

Docker compose external named volumes can be used across the Docker installation and they need to be created by the user (otherwise fails) using the docker volume create command.

Example:

Defines web_data volume:

docker volume create --driver local \
    --opt type=none \
    --opt device=/var/opt/my_website/dist \
    --opt o=bind web_data

docker-compose.yml file with a named volume web_data defined externally:

version '3'

volumes:
  web_data:
    external: true

services:
  app:
    image: nginx:alpine
    ports:
      - 80:80
    volumes:
      - web_data:/usr/share/nginx/html:ro

There are different volume types like nfs, btrfs, ext3, ext4 and also 3rd party plugins to create volumes.

External named volumes can be defined dynamically from environment variables using a name section as we did in the previous example.

3. Sharing volumes

Syntax: --volumes-from container_name

We can start a new container using volumes defined in another. Similar to -v or --volume but without having to define a volume or mounting paths.

$ docker run -it --name [my_new_container] --volumes-from [another_container] [image:tag] [command]

Note: --volumes-from make sense if we are using just Docker. For Docker-compose we can use top-level volumes as we did in previous section and make them available to more than one service. Example sharing web_data to app and app2:

version '3'

volumes:
  web_data:
    external: true

services:
  app:
    image: nginx:alpine
    ports:
      - 80:80
    volumes:
      - web_data:/usr/share/nginx/html:ro
  app2:
    image: nginx:alpine
    ports:
      - 8080:80
    volumes:
      - web_data:/usr/share/nginx/html:ro

Remove Docker volumes

If you followed this tutorial you might have lots of Docker populated volumes. If you need to delete them, you can use the following post to delete the existing Docker volumes running in your system.

Final thoughts

One of the main benefits of using Docker volumes is the ability of changing the content/configuration of a container without the need of recreating it.

You should take into account that if the content of a container will never change probably is better to copy content once you are building its Docker image.

Finally, if you need to provide changes to a container that has no volumes attached with it and it is not possible to recreate it, there is always the option of copy files directly to a running container.

Recommended books to expand your Docker knowledge: