Understand named volumes and bind mounts, when to use each, and how to persist data across container restarts and rebuilds.
Volumes and Bind Mounts
By default, every file a container writes goes into its writable layer - a thin, container-specific filesystem that is thrown away the moment you remove the container. For stateless workloads (web servers, API handlers) that is fine. For anything that holds state - databases, uploaded files, log aggregators - you need a way to survive container restarts and replacements.
Docker solves this with two mechanisms: named volumes and bind mounts. They look similar from the outside (both use the -v flag) but serve different purposes and have different ownership semantics.
The Three Mount Types
Named Volumes
A named volume is a directory that Docker creates and manages on the host. You reference it by a logical name, and Docker handles the actual path.
Creating and listing volumes
docker volume inspect output:
Using a named volume
Stop and remove the container - the data in pgdata survives:
Auto-created volumes
You do not have to docker volume create explicitly. If you reference a volume name that does not exist, Docker creates it automatically:
Volume initialisation behaviour
When Docker mounts a named volume into a container and the volume is empty, Docker copies the existing contents of the container's directory into the volume. This is how official database images seed the volume with initial data - Postgres pre-populates /var/lib/postgresql/data on first run.
Bind mounts do not have this behaviour - they simply overlay whatever is on the host.
Bind Mounts
A bind mount maps a specific directory or file from the host filesystem into the container. Changes on either side are immediately visible on the other.
Now every file change you save in your editor is instantly visible inside the container. Pair this with a process watcher like nodemon and you get hot-reloading without rebuilding the image:
Bind mount syntax variants
The --mount form is verbose but removes ambiguity, which is useful in scripts and Compose files where clarity matters.
Read-Only Mounts
Mounting a volume or bind mount as read-only prevents the container from writing to it. This is a useful security measure for configuration files.
Sharing Volumes Between Containers
Multiple containers can mount the same volume simultaneously. This is how sidecar patterns work - for example, a log shipper container reading log files written by the main app.
Be careful with concurrent writes from multiple containers - you are responsible for handling any write conflicts at the application level.
Removing Volumes
Volumes are not removed when you remove a container. This is intentional - Docker does not want to silently destroy your database data.
Anonymous volumes - those created without a name, such as the implicit volume created by a VOLUME instruction in a Dockerfile - are removed with the container when you pass -v to docker rm.
tmpfs Mounts
A tmpfs mount lives in the host's RAM and is never written to disk. It disappears when the container stops. Use it for sensitive data (API keys, session tokens) that must not be persisted, or for high-speed temporary scratch space.
Volume Drivers
The default local driver stores volumes on the host filesystem. Docker also supports third-party volume driver plugins that store data elsewhere (cloud block storage, network shares, cluster-aware filesystems). Browse the Docker Hub volume plugin list for what is currently maintained.
The built-in local driver can already mount NFS shares directly - no plugin required - by passing driver options:
For most developers, the local driver is all you need. Third-party plugins become relevant when running Docker on multiple nodes (Swarm) or when you need cloud storage backends with lifecycle features the local driver does not provide.
When to Use Each
Use named volumes when:
- You are running a database (Postgres, MySQL, MongoDB, Redis)
- You need data to survive container replacement in production
- You do not care about the exact location of files on the host
- You want Docker Compose to manage the lifecycle
Use bind mounts when:
- You are developing locally and want your code changes to reflect immediately
- You need to inject configuration files from the host (nginx.conf, .env)
- You need to read host files from inside a container (e.g., SSH keys for a CI runner)
- You want to write output to a known host path (reports, exports)
Never use bind mounts for:
- Database data in production (host path dependencies make deployments fragile)
- Secrets management (use Docker secrets or a vault instead)
Docker Compose Volumes
In Compose, volumes are declared at two levels: under the service (where to mount) and at the top level (volume definition).
Running docker compose down does not remove named volumes by default. Pass --volumes to also remove them:
Inspecting Volume Data
Sometimes you need to peek at what is in a volume without starting the application container. The quickest way is to mount it into a utility container:
Or copy data out of a volume to the host:
Summary
Data persistence is one of the first things developers get wrong with Docker. Understanding the difference between named volumes (Docker-managed, portable) and bind mounts (host-coupled, convenient for development) will save you from unexpected data loss and help you design robust production setups.