Docker Volume bind mount
Delete a container and whatever you wrote into its writable layer is gone. To keep data, put it outside the container. This page makes the four destinations clear. Toggle the four modes below to see where data actually lands and whether it survives deletion:
Without a mount, data is in the container writable layer
A container has a writable layer on top of the read-only image layers. Without a mount, files you write land there, using copy-on-write (CoW): editing an existing file copies the whole file from the read-only layer up to the writable layer first, then edits the copy.
- Changing one byte of a 1 GB file still copies the whole file first, so write-heavy work is noticeably slow.
- The writable layer follows the container lifecycle;
docker rm wipes it all.
So databases, data you need to keep, and high-I/O workloads should never live only in the writable layer.
Comparing the three mount types
| Dimension | Volume | Bind mount | tmpfs |
|---|
| Definition | Docker-managed named storage | A host path mapped into the container | Stored in host memory, never on disk |
| Where data lives | /var/lib/docker/volumes/<name>/_data | A host path you specify | Host RAM |
| Survives container rm? | Yes | Yes (on the host) | No |
| Shareable across containers | Yes | Yes | No |
| Performance (Linux) | Same as native host FS | Same as native | Fastest (RAM I/O) |
| Best for | Databases, persistent data, backup/migration | Dev source sync, config injection | Secrets, scratch cache (Linux only) |
A named volume’s physical location
On Linux:
docker volume inspect my-vol
# "Mountpoint": "/var/lib/docker/volumes/my-vol/_data"
Under the WSL 2 backend, that path is inside the docker-desktop-data WSL VM, not on the Windows filesystem, so you cannot browse into it from File Explorer. To read volume contents:
docker run --rm -v my-vol:/data alpine ls /data # mount it into a container to look
bind mount: -v vs --mount
Two syntaxes for the same thing, but --mount is explicit and officially preferred:
# -v: short, but ambiguous
docker run -v /host/data:/app/data:ro app
# --mount: explicit type, no ambiguity
docker run --mount type=bind,source=/host/data,target=/app/data,readonly app
| Behavior | --mount type=bind | -v |
|---|
| Host path missing | Errors, refuses | Creates an empty directory |
Relative path (./data) | Treated as a bind mount | May be treated as a named volume name |
The -v relative-path trap: -v mydata:/app/data (no leading slash) is treated as “create a named volume called mydata”, not a bind mount. To be sure it is a bind mount, use an absolute path or the explicit --mount type=bind.
Anonymous volumes and the Dockerfile VOLUME trap
-v with only a container path and no name creates a random-UUID anonymous volume. The Dockerfile VOLUME instruction also auto-creates an anonymous volume mounted at that path on docker run.
FROM node:20
WORKDIR /app
COPY . .
RUN npm install # node_modules goes into the image layer
VOLUME /app # trap: at run time an anonymous volume mounts over /app, shadowing node_modules
At docker run an anonymous volume mounts over /app, shadowing the node_modules the image layer installed, and the app will not start. Fix: do not declare VOLUME on a path that gets shadowed, or skip VOLUME and mount explicitly at docker run / Compose.
A container deleted without --rm leaves a dangling anonymous volume; clear it with docker volume prune.
docker volume commands
docker volume create my-vol
docker volume ls
docker volume inspect my-vol # see the Mountpoint
docker volume rm my-vol
docker volume prune # clear unused volumes (anonymous only by default)
High-I/O data (source code, node_modules, database files) should sit on the WSL 2 Linux filesystem and be bind-mounted from there. Do not mount from /mnt/c (Windows paths):
docker run -v ~/my-project:/sources app # correct: WSL 2 Linux filesystem
docker run -v /mnt/c/Users/me/project:/sources app # avoid: cross-OS boundary, slow
Official guidance: bind mounts from the Windows filesystem are noticeably slower, and inotify events only work on the Linux filesystem, so mounting /mnt/c breaks hot reload (Vite, webpack, nodemon) because file-change events never fire. For high-I/O cases, prefer named volumes or bind mounts originating on the WSL 2 Linux side.
Next
Reference: docs.docker.com/engine/storage