Learning paths / Containers / Persistent storage

Volumes and bind mounts

Reading · 6 min · Module 7, lesson 2 of 531 min left in this module

Module 7 · Persistent storageLesson 2 of 5

Goal: Choose between a volume and a bind mount for a given piece of data.

Key idea

Both put storage from outside the container at a path inside it, so files written there outlive the container. A volume is storage Docker creates and manages by name. A bind mount is a folder on your own machine. Use volumes for data, and bind mounts for files you edit while you develop.

Volumes

docker volume create notes-data
docker run --rm -v notes-data:/data alpine:3.22 sh -c 'echo saved >> /data/notes.txt'

-v notes-data:/data mounts the volume notes-data at /data inside the container. The file lands on the volume, not in the writable layer, so the next container that mounts notes-data sees it. If the volume doesn't exist yet, docker run creates it.

Docker keeps volumes in its own storage area, and you manage them by name with docker volume ls, inspect and rm. Removing a container never removes a named volume: you delete one on purpose.

docker volume inspect notes-data shows where the files live, such as /var/lib/docker/volumes/notes-data/_data. On Docker Desktop, that path is inside Docker's own small Linux machine, not a folder you can open on your Mac or PC. You reach a volume's files through a container.

Bind mounts

mkdir notes-folder
docker run --rm -v "$PWD/notes-folder:/data" alpine:3.22 sh -c 'echo saved >> /data/notes.txt'

When the left side is a path on your machine ($PWD makes it absolute), Docker mounts that folder instead. The file appears in notes-folder at once, and edits from either side show up on the other.

That makes bind mounts the tool for development: mount your source code, and a server that reloads on change picks up your edits without a rebuild. It also ties the container to one machine's folder layout, which is why bind mounts don't suit production data.

Choosing

  • Data the app must keep, such as a database's files or uploads: a volume. Docker manages it, and it works the same on any machine.
  • Files you edit while developing, such as source code or a config file: a bind mount.
  • Scratch files nobody needs later: neither. The writable layer is fine.

Two things to know about both

  • Mount a folder your app only writes to, such as /data, never your code folder. A bind mount hides what the image had at that path. A new, empty volume gets a copy of it instead, and keeps that old copy after you ship a new image.
  • Compose declares them too (lesson 3.6.1). docker compose down keeps a stack's volumes; docker compose down -v deletes them.
If a non-root app can't write to its volume

A new volume's folder belongs to root, so an app running as a non-root user (lesson 3.4.3) gets Permission denied writing to it. The fix goes in the Dockerfile: create the folder and give it to your user.

RUN mkdir /data && chown 10001:10001 /data
USER 10001

When Docker mounts a new, empty volume on a folder the image already has, it copies that folder's owner, so your user can write. Bind mounts don't do this: the folder keeps the owner it has on your machine.

Check yourself

You're running Postgres in a container on your laptop for development, and you want its data to survive recreating the container. What do you mount at its data folder?