Skip to content

Docker Compose Syntax: Volume or Bind Mount?

4 min read

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.

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.

  1. 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.

  1. 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.

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 in Dockerfile interesting. I clarify a few misconceptions around exposing a port and explain what EXPOSE does, and doesn't do.

Write clean code. Stay ahead of the curve.

Every other Tuesday, I share tips on how to build robust Node.js applications. Join a community of 1,526 developers committed to advancing their careers and gain the knowledge & skills you need to succeed.

No spam! 🙅🏻‍♀️ Unsubscribe at any time.

You might also like

From PM2 to Docker: Cluster Mode

How do you cluster a Node.js app that lives inside a container? Learn how to utilise all CPU cores with Docker.
Read article

From PM2 to Docker: Automatic Restarts

Migrating from PM2 to Docker? Curious if Docker can live up to PM2? Let's compare automatic restarts between the two tools.
Read article

A Beginner's Guide to Building a Docker Image of Your Node.js Application

In this article you'll learn how to create a Docker image to deploy your Node.js application.
Read article