Docker Compose Syntax: Volume or Bind Mount?
Tags: docker, webdev, devops, linux
Published: 2020-04-03 15:15:57

*This article was originally published a day earlier at https://maximorlov.com/docker-compose-syntax-volume-or-bind-mount/*

Docker Compose allows you to configure **volumes** and **bind mounts** using a short syntax. A few examples:

`./public:/usr/share/nginx/html`
`/var/lib/postgresql/data`
`/some/content:/usr/share/nginx/html`
`~/configs:/etc/configs`
`postgresql:/var/lib/postgresql/data`

Which of these are *volumes* and which are a *bind mounts*?

Whenever I had to read a `docker-compose.yml` file, I had to **look up the official documentation** or **run a quick local experiment** to figure out *how* Docker Compose would mount the directories into the container.

I wrote this article so that next time you read a Docker Compose file, you **won't have to guess** anymore. You'll simply *know* by looking at the syntax whether a volume or a bind mount is used behind the scenes.

The different variations are essentially **three unique forms**. I list and explain them in this article below.

## Two `volumes` keys in docker-compose.yml
Before we talk about the different ways to specify volumes, let's first clarify which `volumes` key we're referring to. In `docker-compose.yml`, the `volumes` key can appear in two different places.

```yaml
version: "3.7"

services:
  database:
    # ...
    volumes: # Nested key. Configures volumes for a particular service.

volumes: # Top-level key. Declares volumes which can be referenced from multiple services.
  # ...
```

In this article, we'll talk about the *nested* `volumes` key. That's where you configure volumes for a specific service/container such as a database or web server. This configuration has a short (and a long) syntax format.

## Short syntax format and its variations
The volume configuration has a short syntax format that is defined as:

`[SOURCE:]TARGET[:MODE]`

SOURCE can be a named volume or a (relative or absolute) path on the host system. TARGET is an absolute path in the container. MODE is a mount option which can be read-only or read-write. Brackets mean the argument is optional.

This optionality leads to three unique variations you can use to configure a container's volumes. Docker Compose is smart about recognising which variety is used and whether to use a volume or bind mount.

1. **No SOURCE - eg. `/var/lib/postgresql/data`**

When only a target is specified, without a source, Docker Compose will create an *anonymous* directory and mount it as a volume to the target path inside the container.

The directory's path on the host system is by default `/var/lib/docker/volumes/<uuid>/_data`, where `<uuid>` is a random ID assigned to the volume as its name.

2. **A non-path SOURCE - eg. `postgresql-data:/var/lib/postgresql/data`**

If a source is present and it's not a path, then Docker Compose assumes you're referring to a named volume. This volume needs to be declared in the same file in the top-level `volumes` key declaration.

Top-level `volumes` key always declares volumes, never bind mounts. Bind mounts don't have a name and they can't be named.

3. **A path SOURCE - eg. `/some/content:/usr/share/nginx/html` or `./public:/usr/share/nginx/html`**

If source is a path, absolute or relative, Docker Compose will bind mount the folder into the container. Relative paths starting with `.` or `..` are relative to the location of `docker-compose.yml`.

> Bind mounts are discouraged for database containers since that makes them less portable. Bind mounts are specific to the host system and Docker doesn't manage them. The official docs have a section about [volumes vs bind mounts](https://docs.docker.com/storage/) that explains the differences between the two in more detail.

## Summing it up
Docker Compose allows you to configure volumes by using a short syntax string. Whether you end up with a volume or a bind mount, depends on which short syntax variation you use.

When you don't specify a source, Docker Compose will create an anonymous volume. If source is not a path, Docker Compose will assume source is a named volume. Sources that are relative or absolute paths are bind-mounted into the container.

If you liked this type of article that uncovers a primary feature, you might find my article about [the EXPOSE instruction](https://maximorlov.com/exposing-a-port-in-docker-what-does-it-do/) in Dockerfile interesting. I clarify a few misconceptions around exposing a port and explain what EXPOSE does, and doesn't do.

## Become a skilled Node.js developer

Every other Tuesday I send an email with tips on building solid Node.js applications. If you want to grow as a web developer and move your career forward with me, [drop your email here 💌](https://maximorlov.ck.page/0076aaf499).