Docker mount revisited
Redowan Delowar
October 22, 2024
I always get tripped up by Docker's different mount types and their syntax, whether I'm
stringing together some CLI commands or writing a docker-compose file. Docker's docs cover
these, but for me, the confusion often comes from how "bind" is used in various contexts and
how "volume" and "bind" sometimes get mixed up in the documentation.
Here's my attempt to disentangle some of my most-used mount commands.
Volume mounts
[Volume mounts] let you store data outside the container in a location managed by Docker.
The data persists even after the container stops. On non-Linux systems, volume mounts are
faster than bind mounts because data doesn't need to cross the virtualization boundary.
The -v option
The -v flag is the older and more common way to define volume mounts in the Docker CLI.
For example:
Here's what each part means:
- myvolume: The name of the Docker-managed volume on the host.
- /usr/share/nginx/html: The mount point inside the container.
- :ro: Mounts the volume as read-only inside the container. The host can still write to
the volume, but the container cannot.
The general syntax is:
It can be tricky to remember which part is the host and which is the container, especially
since with volumes, the SOURCE is a volume name, not a host path.
The --mount option
To make things clearer, Docker introduced the --mount option, which uses key-value pairs.
The same volume mount using --mount looks like this:
Or using shorthands:
I find this syntax more explicit and less error-prone, even if it's a bit more verbose.
In docker-compose.yml
In docker-compose, volumes can be defined using both the old and new syntax. Here's how
they compare:
Old style
New style
I prefer the new style because it reduces ambiguity and makes the configuration clearer.
Bind mounts
[Bind mounts] let you directly mount a file or directory from the host into the container.
This is especially useful in development when you want the container to have access to your
code or data.
The key difference between volume mounts and bind mounts is that volumes are fully managed
by Docker and stored in a special location, while bind mounts rely on specific host paths.
Volumes are more portable and isolated from the host, whereas bind mounts give you direct
access to host files but can introduce permission issues and depend on the exact file
structure of the host.
The -v option
Using the -v syntax for bind mounts:
Here:
- /path/on/host: The path on the host machine. This must be an absolute path.
- /usr/share/nginx/html: The mount point inside the container.
- :ro: Mounts the directory as read-only inside the container.
The --mount option
Using --mount for a bind mount:
This syntax makes it clear that you're using a bind mount and specifies exactly which paths
are involved.
In docker-compose.yml
In docker-compose, bind mounts can be specified like this:
Old style
New style
> Note: In docker-compose, if you specify a source that doesn't start with / (an
> absolute path) or ./ (a relative path), Docker might think you're referring to a volume.
> To ensure it's interpreted as a bind mount, start the path with ./ or /.
Tmpfs mounts
[Tmpfs mounts] store data in the host's memory, not on disk. This makes them ideal for
temporary storage that doesn't need to persist after the container stops. They're great for
things like caches or scratch space.
The --tmpfs option
Docker provides a --tmpfs option to create a tmpfs mount more concisely:
- /app/tmp: The target directory inside the container.
- rw: This option allows read and write access to the tmpfs mount.
- size=64m: Sets the size of the tmpfs mount to 64 MB.
The --mount option
Alternatively, using the more flexible --mount option:
Here's what each part means:
- type=tmpfs: Specifies that this is a tmpfs mount, using the host's memory.
- target=/app/tmp: The directory inside the container where the tmpfs mount is mounted.
- tmpfs-size=64m: Limits the size of the tmpfs mount to 64 MB.
- tmpfs-mode=1777: Sets permissions for the tmpfs mount (1777 grants read, write, and
execute permissions to everyone).
In docker-compose.yml
In docker-compose, tmpfs mounts can be defined using both the old and new syntax.
Old style
New style
Build cache mounts
[Build cache mounts] help speed up Docker image builds by caching intermediate files like
package downloads or compiled artifacts. They're used during the build process and aren't
part of the final container image.
In a Dockerfile, you might use a build cache like this:
Here's what each option does:
- --mount=type=cache: Defines a cache mount that stores the files from the apt-get
commands to speed up future builds by reusing the downloaded packages.
- target=/var/cache/apt: Specifies the location inside the container where the cache will
be stored during the build process.
In my Python projects, I cache my dependencies and install them with uv like this:
Above, the cache mount at /root/.cache/uv saves dependencies so they don't need to be
re-downloaded in future builds if nothing changes. The bind mounts provide access to
uv.lock and pyproject.toml from the host, allowing the container to read these config
files during the build. Any changes to the files are picked up, while cached dependencies
are reused unless the configurations have been updated.
[volume mounts]:
https://docs.docker.com/storage/volumes/
[bind mounts]:
https://docs.docker.com/storage/bind-mounts/
[tmpfs mounts]:
https://docs.docker.com/storage/tmpfs/
[build cache mounts]:
https://docs.docker.com/build/cache/optimize/#use-cache-mounts
Discussion in the ATmosphere