Docker Volumes vs Bind Mounts: Container Storage Explained

Updated on
10 min read

Docker containers are designed to be replaceable, but the data used by an application often is not. A database, uploaded file, dependency cache, or local source tree needs a storage boundary that survives container recreation. Docker volumes and bind mounts are the two storage mechanisms most developers encounter first. They both make files available inside a container, but they differ in who controls the source path, how portable the setup is, and what security assumptions it makes.

This guide explains Docker volumes vs. bind mounts from the runtime’s point of view, shows the commands needed to create and inspect each one, and provides a practical decision framework for local development, databases, backups, and production deployment.

What Are Docker Volumes and Bind Mounts?

A Docker volume is a named data store managed by Docker. You create it through the Docker API or CLI, and Docker chooses and maintains the host-side location. The container sees the volume at its database data directory, but the application does not need to know where Docker stores the underlying files. Docker’s storage overview distinguishes managed volumes from other ways of persisting container data.

A bind mount maps an explicitly selected file or directory from the host into the container. The host path is part of the command or Compose file, with a host source mapped to a container workspace directory. The container sees the host directory directly through that mount. The Docker documentation for bind mounts describes this as a host-path mapping rather than a Docker-managed data store.

Both mechanisms are mounted at container start. Neither copies a complete operating system into the container, and neither automatically provides replication, a backup, or encryption. They only define where a container reads and writes data.

Why These Storage Options Exist

The writable layer attached to a container is convenient for temporary changes, but it has the wrong lifecycle for important state. Replacing a container replaces that layer. Image rebuilds, scaling events, and routine cleanup can therefore remove files that were written inside the container without a separate mount.

The two mount types solve different operational problems:

  • Volumes keep application data outside the container while hiding host filesystem details behind a Docker-managed name.
  • Bind mounts make a host directory available when the host path itself is part of the workflow, such as live source-code editing or sharing a configuration file.

That distinction explains why a database normally uses a named volume while a development server commonly uses a bind mount for source code. The database needs a stable runtime-owned data location. The developer needs edits made on the host to appear immediately inside the container.

How Mounts Work

At a high level, the lifecycle is:

  1. Docker creates or resolves a source, such as a named volume or host path.
  2. Docker starts the container with a destination path inside its filesystem namespace.
  3. Reads and writes at that destination are directed to the mounted source.
  4. Removing the container leaves the source behind unless a separate cleanup command removes it.

The mount hides whatever was already present at the destination while the mount is active. For example, if an image contains files under its application data directory, mounting an empty volume there makes the mounted volume’s contents visible at that destination; the image’s original directory is not the active data location. This behavior is useful for initialization, but it can surprise operators who expect files in the image to remain visible after mounting.

The container runtime and image format are separate concerns. The Open Container Initiative runtime specification describes the standard runtime configuration model, including mounts, while Docker supplies the higher-level commands and volume lifecycle. For Docker itself, the project homepage provides the broader platform context beyond storage.

Docker volumes vs. bind mounts

Feature Docker volume Bind mount
Source location Chosen and managed by Docker Explicit host file or directory
Portability Name can travel between compatible hosts, but data must be migrated Host path and directory layout must exist
Best default Application state and runtime-managed data Source code, local configuration, and host integration
Host visibility Usually accessed through Docker commands or the volume path Directly visible and editable on the host
Permission model Still subject to container and host permissions, but path details are abstracted Host ownership, ACLs, labels, and path rules directly matter
Backup approach Discover or snapshot the volume, then copy data consistently Back up the host path using host tools
Security exposure Limits accidental access to unrelated host directories Can expose or modify any selected host path
Compose syntax db_data:container-data ./data:container-data

Neither choice is universally safer or faster. The right answer depends on whether the host directory is an intentional interface or merely an implementation detail.

Key Concepts and Variants

Named and anonymous volumes

A named volume has a stable identifier:

docker volume create app-data
docker volume ls
docker volume inspect app-data

An anonymous volume has no convenient user-chosen name and may be created by an image’s VOLUME instruction or a command that supplies only a container destination. Anonymous volumes can be useful for disposable dependencies, but named volumes are easier to identify, back up, and remove deliberately.

Docker also supports volume drivers. The default local driver stores data on the local Docker host. Other drivers can connect Docker to a storage system, but the driver determines availability, performance, credentials, and failure behavior. A volume name alone does not make data highly available across machines.

Read-write and read-only mounts

Mounts are read-write by default. Add :ro or readonly when a process only needs to read data:

docker run --rm \
  --mount type=bind,src="$PWD/config",dst=/etc/example,readonly \
  example/app:latest

Read-only mounts reduce accidental modification and narrow the impact of a compromised process. They do not prevent a process from reading secrets that are present in the mounted path, so the source directory still needs an appropriate scope and permission model.

Bind mounts on desktop systems

On Linux, a bind mount refers to a path on the Docker host. With Docker Desktop, containers run inside a managed Linux or Windows environment depending on the selected mode. Docker Desktop makes common host paths available, but filesystem sharing, performance, case sensitivity, file notifications, and permission translation can vary by platform.

Use an explicit absolute path when scripting across environments. In Compose, a relative path is resolved relative to the Compose project directory, which is convenient but should be documented for teammates and CI.

Practical Guide: Choosing and Using Each Type

Use a volume for a database

The following example keeps PostgreSQL data in a Docker-managed volume. The container can be replaced without removing the database files:

docker volume create postgres-data

docker run -d \
  --name app-postgres \
  -e POSTGRES_PASSWORD=change-this-locally \
  --mount type=volume,src=postgres-data,dst=/var/lib/postgresql/data \
  postgres:16

docker inspect app-postgres --format '{{json .Mounts}}'

Use a secret-management mechanism rather than committing a real password to a script. The volume protects the data from ordinary container replacement; it does not protect it from a privileged host user, a destructive docker volume rm, disk failure, or an application that deletes its own records.

Use a bind mount for source code

For a local development container, mount the working tree and optionally make dependency or build directories separate:

docker run --rm -it \
  --name app-dev \
  --mount type=bind,src="$PWD",dst=/workspace \
  --mount type=volume,src=node-modules,dst=/workspace/node_modules \
  -w /workspace \
  node:22 \
  npm run dev

The source code remains editable in the host editor, while node_modules is kept in a volume so host-specific binaries do not overwrite the container’s dependency tree. On Windows PowerShell, $PWD is an object rather than the same string expression used by every shell; Docker Desktop generally accepts the path, but a Compose file is often more portable for a team.

Declare both types with Compose

Compose makes the distinction visible in configuration:

services:
  app:
    image: example/app:dev
    working_dir: /workspace
    volumes:
      - type: bind
        source: .
        target: /workspace
      - type: volume
        source: app-cache
        target: /workspace/.cache
      - type: bind
        source: ./config/app.yaml
        target: /etc/example/app.yaml
        read_only: true

volumes:
  app-cache:

Validate and start the stack with:

docker compose config
docker compose up -d
docker compose ps
docker compose exec app sh

The top-level app-cache declaration creates a named volume managed as part of the Compose project. The . source is a bind mount, so the command must run from the intended project directory. For a broader explanation of multi-container configuration, see our Docker Compose local development guide.

Backups, Permissions, and Troubleshooting

A mount is not a backup policy. For a quiescent application, a simple volume backup can stream a tar archive through a temporary container:

docker run --rm \
  --mount type=volume,src=postgres-data,dst=/source,readonly \
  --mount type=bind,src="$PWD/backups",dst=/backup \
  alpine \
  tar czf /backup/postgres-data.tgz -C /source .

For databases, prefer the database’s logical dump or a storage snapshot coordinated with the database. Copying live files can produce an unusable backup even when every file was copied successfully.

When a mount fails, inspect the effective configuration rather than guessing:

docker inspect app-postgres --format '{{range .Mounts}}{{println .Type .Source "->" .Destination .RW}}{{end}}'
docker volume inspect postgres-data
docker compose config

Common symptoms have predictable causes:

  • Empty directory: The source is new, the destination is being hidden by the mount, or the application writes to a different path.
  • Permission denied: The container user does not have the required UID/GID or the host filesystem applies ACL, SELinux, or platform-sharing rules.
  • Changes do not appear: A bind path is wrong, a different Compose project is running, or a desktop file-sharing and notification boundary is involved.
  • Data disappeared: The container wrote to its writable layer, an anonymous volume was removed, or the named volume was explicitly deleted.

Do not solve permission errors by making a whole host directory world-writable. Identify the process UID, adjust ownership or ACLs narrowly, and use a read-only mount when writes are unnecessary.

Common Misconceptions

“A volume is automatically durable”

A volume survives container removal by default, but durability means more than survival of one Docker command. You still need backups, restore tests, capacity monitoring, and a plan for host or disk failure. A local volume remains local unless its driver provides a different storage system.

“Bind mounts are only for development”

Bind mounts are common in development because they provide live file sharing, but they can also be appropriate for controlled host integrations such as a read-only certificate directory or a device-specific agent. Their explicit host coupling makes them a poor default for portable application state, not an absolute prohibition.

“Volumes and bind mounts make containers stateful in the same way”

The mount stores files, but the application defines how those files form valid state. A database needs locking, crash recovery, and consistent snapshots. A log directory may only need append access. Choosing a mount type does not replace application-level storage design.

For a wider discussion of ephemeral and persistent container data, read our container storage guide. To learn how the container runtime, images, and networks fit together, see Docker containers for beginners. Storage is only one container boundary; our container security best practices guide explains why host-path exposure, privileges, and read-only filesystems matter.

TBO Editorial

About the Author

TBO Editorial writes about the latest updates about products and services related to Technology, Business, Finance & Lifestyle. Do get in touch if you want to share any useful article with our community.