Articles

Docker Volumes vs Bind Mounts: What's the Difference

Docker volumes are managed by Docker and portable; bind mounts map a host path directly into a container. When to use each for persistent data.

Chisato Chisato · · 5 min read
Docker whale logo on a shipping container

Volumes and bind mounts are the two main ways Docker persists data outside a container’s writable layer. A volume is a storage location Docker creates and manages itself, independent of the host’s directory structure; a bind mount maps a specific path on the host filesystem directly into the container. Both solve the same underlying problem — a container’s filesystem disappears when the container is removed — but they solve it with very different tradeoffs around portability, performance, and control.

Why containers need either one

By default, everything written inside a running container lives in a thin writable layer on top of its image, and that layer is deleted the moment the container is removed. That’s fine for stateless processes, but it’s a problem for anything that needs to survive restarts: a database’s data directory, uploaded files, or configuration you want to edit without rebuilding the image. Volumes and bind mounts both attach external storage to a container so writes land somewhere that outlives the container itself.

What a volume is

A volume is created and managed entirely by Docker, typically stored under /var/lib/docker/volumes/ on Linux hosts, though the exact location is an implementation detail you’re not meant to touch directly. You create and reference one by name:

docker volume create db-data
docker run -d -v db-data:/var/lib/postgresql/data postgres

Docker owns the lifecycle: docker volume ls, docker volume inspect, and docker volume rm all operate on volumes without you needing to know or care about the underlying host path. Volumes can also be backed by storage drivers for network or cloud storage, which is part of why they’re the more portable option — the same docker run command works whether the volume driver points at local disk or a remote NFS share.

What a bind mount is

A bind mount instead maps an existing path on the host directly into the container, byte for byte:

docker run -d -v /home/user/app/src:/app/src node:20

Whatever’s at /home/user/app/src on the host appears at /app/src inside the container, and changes in either direction are immediately visible in the other. There’s no Docker-managed abstraction in between — you’re pointing directly at a real filesystem path, which means the container can see (and modify) anything that path grants access to, including files well outside what you intended if the path is too broad.

Volumes vs bind mounts

VolumesBind mounts
Managed byDockerYou (raw host path)
LocationDocker-chosen, abstractedExact host path you specify
PortabilityPortable across hosts/driversTied to that host’s directory layout
Performance (Linux)Native filesystem performanceNative filesystem performance
Performance (Docker Desktop on macOS/Windows)Better, avoids the VM filesystem bridgeCan be slower due to host/VM file sharing
Typical useProduction persistent data (databases, queues)Local development (live code mounts)
Initialized from imageYes — volume is pre-populated from image content on first useNo — host content overwrites what’s in the image

When to use volumes

Volumes are the right default for anything that needs durable, production-grade storage: a database’s data directory, a message queue’s persisted state, or any data you want Docker (and orchestration tools built on it) to manage without depending on a specific host’s directory structure. They’re also the better choice when running on Docker Desktop for macOS or Windows, where a bind mount into the Linux VM that backs the Docker engine can be noticeably slower than a volume for filesystem-heavy workloads. When you scale beyond a single host into Kubernetes, the closest equivalent to a Docker volume is a PersistentVolume, which follows the same idea of storage managed independently of any one node.

When to use bind mounts

Bind mounts shine in local development, where the whole point is that edits on the host are reflected instantly inside the running container — mount your source directory and a file-watcher-driven dev server picks up changes without rebuilding the image. They’re also useful for injecting configuration files, certificates, or secrets from a known host path, or for sharing something like the Docker socket itself between the host and a container. Docker Compose commonly uses bind mounts in development configurations for exactly this reason, often paired with a separate production configuration that swaps them for named volumes.

A third option: tmpfs mounts

For data that shouldn’t be written to disk at all — secrets held only in memory, or high-churn temporary files — Docker also supports tmpfs mounts, which live purely in the host’s memory and vanish when the container stops. They’re not a substitute for either volumes or bind mounts, but worth knowing about when persistence is explicitly not what you want.

Common pitfalls

  • Orphaned volumes. Removing a container with docker rm doesn’t delete its volumes by default, which is usually what you want — but it also means volumes can silently accumulate. docker volume prune removes ones no container references.
  • Permission mismatches. A bind-mounted directory keeps the host’s file ownership and permissions, which can conflict with the UID a process runs as inside the container, a frequent source of “permission denied” errors that don’t show up with Docker-managed volumes.
  • Accidentally shadowing image content. Bind-mounting over a directory that already has files baked into the image (like node_modules in some setups) replaces it entirely with the host’s version, which may be empty or stale.

The takeaway

Both volumes and bind mounts attach persistent storage to a container, but they differ in who manages it: Docker manages volumes and abstracts away the host path, while bind mounts point directly at a host directory you control. Reach for volumes when you want portable, Docker-managed persistence — especially for production data like databases — and reach for bind mounts when you want a live, two-way link between a specific host path and the container, which is exactly what local development workflows need.

Chisato Chisato · · 4 min read

Docker vs Podman: Container Runtimes Compared

Docker runs containers through a persistent root daemon; Podman runs them daemonless and rootless by default. What that architectural split changes.

#Docker #DevOps #Containers
The Lycoris Team The Lycoris Team · · 4 min read

Docker Multi-Stage Builds Explained

Multi-stage builds let a Dockerfile use one stage to compile code and a separate, minimal stage to ship it — cutting image size and attack surface.

#Docker #DevOps #Containers