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 will 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

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


docker-compose.yml 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

External named volumes can be used across the Docker installation and needs 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.

Finally, for testing purposes and in order to have a clean environment, sometimes you may need to delete the existing Docker volumes running in your system.