# Docker Volumes vs Bind Mounts: Which to Use

> Originally published on [DevToolHub](https://devtoolhub.com/docker-volumes-vs-bind-mounts/).

![Where a Docker volume and a bind mount store data, on Docker's side or on your host](https://devtoolhub.com/wp-content/uploads/2026/11/2026-11-05-docker-volumes-vs-bind-mounts-diagram.webp)

Use a Docker volume when the container owns the data, such as a database or a model cache. Use a bind mount when you need to edit the files on your host, such as source code or a config file. The docker volumes vs bind mounts choice also changes startup behavior and speed. It decides how easy it is to lose data, too. So it's worth testing before you pick.

I ran everything below on Docker Engine 29.4.3 with Docker Desktop for Mac. I didn't test a Linux host, so I flag the spots where it may differ.

## What Is the Difference Between Docker Volumes vs Bind Mounts?

A volume is storage that Docker creates and manages for you. A bind mount maps a folder that already exists on your host into the container. Docker's [volumes documentation](https://docs.docker.com/engine/storage/volumes/) calls volumes the preferred way to persist container data. Its [bind mounts page](https://docs.docker.com/engine/storage/bind-mounts/) points bind mounts at sharing source code and host config files.

| | Volume | Bind mount |
|---|---|---|
| Who manages it | Docker | You |
| Where data lives | Docker's storage area | A path you choose on the host |
| Host path must exist | No | Yes, unless Docker creates it |
| Fills from the image on first use | Yes (copy-up) | No, it hides the image's files |
| Easy to edit from the host | No | Yes |
| Best for | Databases, caches, app state | Source code, config files |

## When Should You Use a Docker Volume?

Use a volume for any data the container creates and you want to keep, but never edit by hand. Volumes survive the container, and Docker handles their location. I wrote a file into a volume, let the container exit, and read it back from a new one:

```bash
docker run --rm -v dvbtest-data:/data alpine sh -c 'echo saved > /data/note.txt'
docker run --rm -v dvbtest-data:/data alpine cat /data/note.txt
```

```text
saved
```

The `--rm` flag deleted both containers, but the volume stayed, and `docker volume ls` still listed it. `docker volume inspect` shows where Docker stores it:

```text
local /var/lib/docker/volumes/dvbtest-data/_data
```

⚠️ Note: that path does not exist on a Mac. On Docker Desktop the volume lives inside the Linux VM, so `ls` on my host returned `No such file or directory`. If you need to browse the files from your host, a volume is the wrong tool.

The same pattern powers the model cache in the [Ollama Docker setup guide](https://devtoolhub.com/ollama-docker-setup-gpu-volumes/). Without a named volume, those downloads vanish with the container.

## When Should You Use a Bind Mount?

Use a bind mount when your host and the container need the same files. Source code you edit in an IDE is the classic case. So is a config file you maintain. Per the docs, a bind mount gives the container write access to your host files by default. Add `:ro` when the container only needs to read:

```bash
docker run --rm -v /tmp/dvb/auto-created:/data:ro alpine sh -c 'echo z > /data/y'
```

```text
sh: can't create /data/y: Read-only file system
```

The container can't change the host folder now. That matters because a bug or a bad command inside the container can otherwise delete files on your machine.

On ownership: my container wrote as root, and the file still showed up on my Mac owned by my own user. That mapping is a Docker Desktop behavior. I didn't test Linux, where a root process in the container may leave root-owned files on the host.

## Does a Mount Hide the Files Already in the Image?

A bind mount always hides what the image has in that folder. A new named volume does the opposite: Docker copies the image's files into it first. I mounted both over the NGINX web root, `/usr/share/nginx/html`, which ships with `index.html` and `50x.html`.

An empty host folder as a bind mount:

```text
total 4
drwxr-xr-x    2 root     root            64 Nov  5 13:57 .
```

That folder looked empty, and `index.html` was gone from view. A new named volume at the same path:

```text
50x.html
index.html
```

Docker copied both files into the volume on first use. To switch that copy off, use `volume-nocopy`:

```bash
docker run --rm --mount type=volume,src=dvbtest-nocopy,dst=/usr/share/nginx/html,volume-nocopy nginx:alpine ls -A /usr/share/nginx/html
```

That listed zero files. Note the copy-up only happens when the volume is empty. A volume that already holds data hides the image's files, just like a bind mount.

## Docker Volumes vs Bind Mounts With -v and --mount

The two flags differ in one way that bites people: what happens when the host path is missing. With `-v`, Docker silently creates the folder. With `--mount`, it stops with an error. Here is `-v` pointing at a path that didn't exist:

```bash
docker run --rm -v /tmp/dvb/auto-created:/data alpine sh -c 'echo hi > /data/x'
```

It ran fine and created `/tmp/dvb/auto-created` on my host. The same mismatch with `--mount`:

```bash
docker run --rm --mount type=bind,src=/tmp/dvb/nope,dst=/data alpine true
```

```text
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /host_mnt/private/tmp/dvb/nope
```

It exited with code 125. The `/host_mnt/...` prefix looks like Docker Desktop's file-sharing path translation. Add `bind-create-src` to the `--mount` options if you want the folder created, as the docs describe.

A typo in a `-v` path therefore creates an empty folder and starts the container happily. That is a classic source of "my config isn't loading" bugs. Prefer `--mount` in scripts.

There is a second trap in the `-v` syntax. A bare name is a volume, and a path starting with `./` is a bind mount:

```bash
docker run --rm -v data:/app alpine sh -c 'echo a > /app/f'    # creates a named volume "data"
docker run --rm -v ./data:/app alpine sh -c 'echo b > /app/f'  # writes to ./data on your host
```

After the first command, `docker volume ls` listed a volume called `data` and my project folder stayed empty. The second wrote `f` into `./data`. Forget the `./` and you create a hidden volume instead of the folder you meant.

## How Much Slower Is a Bind Mount on Docker Desktop?

On my Mac, writing 3,000 small files into a bind mount took about five times longer than into a volume. Each run launched a container and created the files with a shell loop, so both numbers include roughly the same startup cost:

| Run | Bind mount | Named volume |
|---|---|---|
| 1 | 4.49 s | 0.70 s |
| 2 | 3.27 s | 0.73 s |
| 3 | 3.78 s | 0.67 s |

This is a rough test: three runs, one machine, one workload. The gap likely comes from Docker Desktop crossing the VM boundary on every file operation. Expect it to matter for `node_modules`, package caches and builds. On a Linux host, a bind mount is a direct path and I'd expect no such penalty, but I haven't measured it.

## How Do You Delete Volumes Safely?

Containers don't delete volumes, so unused ones pile up until you remove them. Compose shows the difference well. I started a service with a volume, then ran `docker compose down`. The volume survived:

```text
dvbtest_appdata
```

⚠️ Note: `docker compose down -v` deletes every named volume declared in that Compose file, along with its data. Run `docker compose down` without `-v` unless you really want a clean slate.

```bash
docker compose down -v
```

```text
 Volume dvbtest_appdata Removed
```

The earlier run's log line was still there when I brought the service back up, so the data had persisted until I used `-v`. For one volume, use `docker volume rm <name>`. `docker volume prune` removes all unused volumes, so read what it lists first.

## Docker Volumes vs Bind Mounts: Which Should You Use?

Pick a volume for data the container owns and a bind mount for files you edit. If you're unsure, start with a volume, because it's portable and faster on Docker Desktop.

| Situation | Use |
|---|---|
| Databases, caches, uploads, anything you back up with Docker | Volume |
| Source code, config files, build output you want on your host | Bind mount |
| Config the container should read but never change | Bind mount with `:ro` |
| Temporary state you don't need to keep (Docker's docs point to `tmpfs`) | Neither |

For the rest of your container setup, the [Docker best practices guide](https://devtoolhub.com/docker-best-practices-a-beginners-guide/) covers the surrounding habits, and the [Dockerfile best practices](https://devtoolhub.com/5-best-practices-for-writing-dockerfiles/) cover the image side.

## Frequently Asked Questions

**Q: Should I use a Docker volume or a bind mount for a database?**
A: Use a volume. Docker manages its location, it survives container removal, and on Docker Desktop it avoided the slowdown I saw with bind mounts. A bind mount makes sense for a database only if you must inspect the files directly from the host.

**Q: What is the difference between `-v` and `--mount`?**
A: `--mount` uses explicit `key=value` options and errors if a bind source path is missing. `-v` is shorter and silently creates a missing host folder. Both can create volumes or bind mounts, but `--mount` is the clearer choice for scripts.

**Q: Do Docker volumes persist after the container is deleted?**
A: Yes. Named volumes stay until you remove them with `docker volume rm`, `docker volume prune` or `docker compose down -v`. Anonymous volumes created with `docker run --rm` are removed along with the container.

**Q: Why is my bind mount empty inside the container?**
A: A bind mount hides whatever the image has at that path. If the host folder is empty, the container sees an empty folder. A new named volume works differently, because Docker copies the image's files into it first.

**Q: Are Docker bind mounts slower than volumes?**
A: On Docker Desktop for Mac, yes. Creating 3,000 small files took 3.3 to 4.5 seconds on a bind mount and about 0.7 seconds on a volume in my tests. I didn't measure a Linux host.

**Quick Summary:**
- Docker manages a volume, while a bind mount maps a host folder you choose
- Volumes survived container removal in my test, and `docker compose down -v` is what deletes them
- New volumes get the image's files copied in first, but a bind mount hides them
- `-v` creates a missing host path silently, and `--mount` stops with exit code 125
- `-v data:/app` makes a volume, and `-v ./data:/app` makes a bind mount
- Creating 3,000 files took about five times longer on a bind mount than a volume on Docker Desktop for Mac
- Any bind mount the container should only read needs `:ro`

Before your next `docker run -v`, decide which side owns the data. In the docker volumes vs bind mounts choice, that answer settles it. Container-owned data goes in a volume. Files you edit go in a bind mount.
