A volume is a directory on the host filesystem, bind-mounted into the container’s namespace.
What Actually Happens
When Docker creates a named volume, it makes a directory on the host:
/var/lib/docker/volumes/<volume-name>/_data/
That’s it. A normal directory. No special filesystem, no format, no encryption — you could ls it from the host right now.
When a container mounts it, the kernel performs a bind mount — a Linux feature that makes a directory appear at a second location in the filesystem tree simultaneously:
host: /var/lib/docker/volumes/mydata/_data/
↕ (same inodes, same blocks)
container: /app/data/
Both paths point to the exact same inodes and disk blocks. There’s no copying, no syncing — it’s one directory visible from two places at once. Writes from the container are immediately visible on the host, and vice versa.
How This Differs from the Container’s Writable Layer
The OverlayFS writable layer (the container’s copy-on-write layer) lives inside Docker’s overlay stack and is destroyed when the container is removed. A bind-mounted volume sits outside that stack entirely — it bypasses OverlayFS and goes straight to the host filesystem. That’s why data survives container removal.
The Three Storage Types at OS Level
| Type | OS mechanism | Lives at | Survives container removal |
|---|---|---|---|
| Volume | Bind mount → host dir | /var/lib/docker/volumes/ | Yes |
| Bind mount | Bind mount → host dir | Wherever you specify | Yes |
| tmpfs | tmpfs kernel mount | RAM only | No (gone on stop) |
Volume vs Bind Mount — What’s the Difference?
Functionally identical at the kernel level — both are bind mounts. The difference is who manages the directory:
- Volume — Docker creates and manages the directory; you reference it by name (
-v mydata:/app/data) - Bind mount — you specify an exact host path yourself (
-v /home/user/code:/app)
In practice: use named volumes for databases and persistent application data. Use bind mounts in development to mount your source code into the container.
One sentence: a volume is a host directory bind-mounted into the container’s filesystem namespace — the kernel exposes the same inodes at two paths simultaneously, with no virtualisation or copying involved.