Steps to Deploy Django with Postgres, Nginx, and Gunicorn on Ubuntu 18.04
2021-11-04
Sampling from a multivariate Gaussian (Normal) distribution with Python code
2021-12-04
Show all

Volumes in Docker Compose tutorial

5 mins read

The purpose of this post is to review how we can use volumes in 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)
  • The Declarative way (Docker Compose YAML file or Docker Dockerfile)

In this post, you’ll see only how to do it in a declarative manner using a docker-compose file. But it’s worth mentioning 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

TIPS:

  1. From Docker Compose version 3.4 the name of the volume can be dynamically generated from environment variables placed in a .env file (this file has to be in the same folder as docker-compose.yml is).
  2. To increase the security of 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 an 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 makes sense if we are using just Docker. For Docker-compose we can use top-level volumes as we did in the 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.

Either you need to remove unused volumes, the persisted data from a running container, or its configuration, you can use the following commands to remove a Docker volume:

First of all, you should list all current volumes:

$ docker volume ls
DRIVER              VOLUME NAME
local               named_volume
local               0a0bce5c74f249a9954120ce3d6cbc5fb388d1fadc27fd55c2a008d2c1bd8d1a
local               1e3f5e108a2e2542f018d0302f3d598bb90ded4cd604fed352c9530d30d35a56
...

Named volumes are defined by the user and there is no issue to identify them. Things change a little bit for auto-generated volumes. These volumes can be tricky to be identified and if you need to delete one of them from a known container you should try to locate it:

$ docker inspect containerid
...
"Mounts": [{volume 6d29ac8a196.. }{ ... }]

The volume name to be deleted is 6d29ac8a196..

Delete a specific Docker volume

$ docker volume rm [volume_name]

Delete all Docker unused volumes

$ docker volume prune

# using --filter you can filter which unused volumes you want to delete as we did before with stopped containers and networks.
$ docker volume prune --filter 'NAME=VALUE'

Final thoughts

One of the main benefits of using Docker volumes is the ability to change 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 s 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 to it and it is not possible to recreate it, there is always the option of copying files directly to a running container.

Leave a Reply

Your email address will not be published. Required fields are marked *