Docker Data Persistence and Mounts
The type of pitfall Docker beginners fall into most often is not "the container won't start" but "the data is gone when I deleted the container" or "the mounted files don't look the way I expected."
First, remember a basic fact:
The container's own writable layer is suitable for temporary runtime, not for saving data you truly care about.
3 Common Storage Methods
| Method | Best For | Typical Command |
|---|---|---|
| Container writable layer | Temporary debugging, one-time runs | Default behavior |
| Named volume | Database storage, long-term persistence | --mount type=volume,... |
| Bind mount | Local code, config files, host directory sharing | --mount type=bind,... |
How I Generally Choose
Choose Named Volume
Suitable for:
- Database data directories
- Cache directories
- When you want Docker to manage the data path without managing the specific host directory yourself
For example:
docker volume create postgres-data
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=secret \
--mount type=volume,src=postgres-data,target=/var/lib/postgresql/data \
postgres:16
View data volumes:
docker volume ls
docker volume inspect postgres-data
Choose Bind Mount
Suitable for:
- Mounting local source code into a container
- Mapping existing host configuration files into a container
- When you explicitly want to see the host path directly
For example, mount the current directory into the container:
docker run --rm -it \
--mount type=bind,src="$PWD",target=/workspace \
-w /workspace \
python:3.12 \
bash
If you only want read access:
docker run --rm -it \
--mount type=bind,src="$PWD",target=/workspace,readonly \
python:3.12 \
bash
How to Choose Between -v and --mount
Both work, but my personal default preference is:
- Simple commands:
-v - Slightly more complex or long-term maintenance commands:
--mount
The reason is not a big functional difference, but that --mount has more explicit fields, making it easier to understand what was mounted when you revisit the command later.
For example, these two are essentially equivalent:
docker run -v redis-data:/data redis:7
docker run --mount type=volume,src=redis-data,target=/data redis:7
A Common Misconception: Mounts Override Container Content at the Same Path
If you mount a host directory to /app in the container, the original content of /app in the container image will be "hidden."
For example:
docker run --rm -it \
--mount type=bind,src="$PWD",target=/app \
my-image:latest
In this case, the /app inside the container will primarily show the host's current directory, not the content that was built into the image.
How to Decide Which to Use
You can follow this decision order:
- Does the data need to be kept long-term?
- Do I need to directly operate on real files on the host?
- Is this path a database, cache, or application runtime state directory?
In most cases:
- Data directories: prefer volume
- Code directories, config directories: prefer bind mount
Backing Up and Migrating Volumes
The simplest approach is to temporarily spin up a container to package the data volume:
docker run --rm \
-v postgres-data:/source \
-v "$PWD":/backup \
busybox \
tar czf /backup/postgres-data.tar.gz -C /source .
Restoring works the same way: unpack the archive back.
What I Check First When Troubleshooting Mount Issues
- Whether the host path actually exists
- Whether the path type is reversed (e.g., mounting a file as a directory)
- Whether the mount hid the original image content
- Whether the container process user has read/write permissions
- Whether I'm actually using a volume or a bind mount
View the container's actual mount information:
docker inspect my-container