Containers·7 min read·

Docker Volumes vs Bind Mounts: Choosing Storage

Learn when to persist data with named volumes and when a bind mount is the right call, including backups, permissions, and state migration.

NB

Netbay Developer Relations

Netbay Engineering

On this page

Every running service that writes state eventually forces the same question: where does this data live, and what happens to it when the container is recreated? Docker offers two answers — named volumes and bind mounts — and they look interchangeable on the surface while behaving very differently underneath. Volumes are managed storage living inside the Docker root and owned by the engine; bind mounts are arbitrary host paths you hand to a container as-is. Choosing wrong shows up as permission errors, missing data after an upgrade, or a host directory that fills with container garbage. This guide maps the trade-off precisely and gives you the commands to migrate safely either way.

Two ways to persist, different owners VOLUME docker-managed path portable + inspectable backups via container best default for state BIND MOUNT points at a host path host controls perms great for dev / logs less isolated Rule of thumb: state in volumes, configuration as files

What a Named Volume Actually Gives You

A named volume is a chunk of the Docker engine's own storage area, mounted into the container at a path you choose. Because the engine manages it, you get tooling for free: docker volume ls lists them, docker volume create provisions one ahead of time, and docker volume rm deletes it with the config that created it. Volumes behave well under recreation — docker compose up -d --force-recreate tears down and rebuilds controllers while the named volume keeps its contents untouched — and they are the recommended default for anything that holds database files.

bash
# Named volume in use
docker volume create pgdata
docker run -d --name db   -v pgdata:/var/lib/postgresql/data   -e POSTGRES_PASSWORD=hunter2   postgres:16-alpine

# The engine owns the path; find it to back up from the host
docker volume inspect pgdata --format '{{ .Mountpoint }}'

The inspect --format printout is the mental model in one command: the mountpoint is a path the engine picked under its own data directory, not a directory you chose. You generally should not browse it directly — the correct way to read volume contents is through a container mounted at that path, which is also how backups work.

When a Bind Mount Makes Sense

A bind mount skips the engine's ownership: you name a host directory, and the container sees that exact directory, complete with the host's ownership and permissions. That is a feature during development — editing sources in your editor reflects instantly in the container, no rebuild — and it is also a liability in production, because a container running as a user that does not match the host owner trips over permission errors immediately. Bind mounts are the right tool for config you manage on the host, for log directories you want on a specific disk, and for local development loops.

bash
# Bind mount: host path : container path
docker run -d --name web   -v /srv/www:/usr/share/nginx/html:ro   -p 8080:80   nginx:alpine

# Read-only bind means the container cannot mutate host files
docker run -d --name app   -v /etc/app/config.yaml:/app/config.yaml:ro   my-app:latest

The :ro flag matters more than most people think: it turns an accidental container bug from a host-data-corruption incident into a clean permission denial. A classic failure mode is the container that chowns or rewrites the mounted host directory on startup; :ro blocks exactly that.

The Decision Rules

Ask three questions in order and the choice stops being guesswork. Does the data need to survive container replacement? Volumes win, because their lifecycle is independent of any container. Do you need to edit the data from the host as ordinary files right now? Bind mounts win, but only with deliberately matched permissions. Is the data generated continuously, like logs? Bind mounts to a chosen disk are transparent and easy to rotate — provided the writing container runs as a host-mapped UID.

The Permissions Trap, Named

The single most common failure is a bind-mounted directory owned by root on the host while the container's process runs as a non-root user. Unlike volumes — where Docker copies ownership from the image path — bind mounts present the host UID/GID verbatim. The fix is deliberate: create the host directory with the container user's UID, or run the container with that UID explicitly. Skipping it produces the industry-standard "cannot open directory: Permission denied" at the worst possible moment.

Backing Up Either Kind

Both persistences need a backup story, and both use the same trick: run a throwaway container that mounts the data and streams it to an archive.

bash
# Volume backup
docker run --rm -v pgdata:/source:ro   -v "$PWD:/backup" alpine   tar czf /backup/pgdata.tar.gz -C /source .

# Bind-mount backup is a plain host command
tar czf pgdata.tar.gz -C /srv/postgres/data .

The volume backup deliberately routes through a container because the engine-owned path should not be poked at from the host; the bind-mount backup is just tar on a familiar directory. Restore is the mirror image: extract into an empty volume, or into the bind target, and recreate the container.

Migration When You Already Made the Wrong Choice

Moving a bind mount to a volume (or back) is three commands: stop the container, copy the data through a scratch container, and restart with the new mount type. The copy step uses the same tar pattern in both directions, and the only real risk is a container writing while you copy — stop first, verify after with a checksum, then start again. Once you have moved state into a named volume, docker compose down no longer has the power to destroy it accidentally, which is the lasting win.

Takeaway

Volumes give you lifecycle-managed, portable, commonly backup-able storage and are the sensible default for databases and anything stateful; bind mounts expose host paths directly for the cases where you genuinely want to manage files on the host, with permissions discipline as the cost of entry. Naming the trade-off explicitly — owner, permissions, backup path — is what stops the two from being interchangeable in your head. A Netbay VPS starts you with generous SSD capacity and a fresh Docker-ready OS, so the only storage question left is the one this guide answers: which mount for which data — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance