Skip to main content

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

MethodBest ForTypical Command
Container writable layerTemporary debugging, one-time runsDefault behavior
Named volumeDatabase storage, long-term persistence--mount type=volume,...
Bind mountLocal 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:

  1. Does the data need to be kept long-term?
  2. Do I need to directly operate on real files on the host?
  3. 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

  1. Whether the host path actually exists
  2. Whether the path type is reversed (e.g., mounting a file as a directory)
  3. Whether the mount hid the original image content
  4. Whether the container process user has read/write permissions
  5. Whether I'm actually using a volume or a bind mount

View the container's actual mount information:

docker inspect my-container