Managing Volumes
Docker volumes are where long-lived container data should live. Managing them well means you can recreate containers, upgrade images, back up databases, and clean old storage without guessing where important files are stored.
A volume is not a container and it is not an image layer. It is a separate Docker-managed storage object that can be attached to one or more containers at a chosen path.
Overview: How Volume Management Works
Every container starts with the image’s read-only layers plus a thin writable container layer. If a process writes to an ordinary path, the data goes into that writable layer. When the container is removed, that layer is removed too. A volume changes this for selected paths: Docker mounts external storage into the container before the process starts, so reads and writes under that path bypass the container layer.
Volume management is the set of operations around that storage object: creating it, naming it, inspecting it, attaching it to containers, backing it up, restoring it, and removing it only when the data is no longer needed. Docker’s local volume driver is the default. On Linux Engine, local volumes are usually stored below Docker’s data root, commonly /var/lib/docker/volumes. On Docker Desktop for macOS and Windows, Linux container volumes live inside the Docker Desktop VM. In both cases, the safe interface is the Docker CLI, not editing internal directories by hand.
A named volume such as notes-data has a lifecycle independent of any one container. You can remove a container with docker rm, then start a replacement that mounts the same volume and sees the same files. An anonymous volume is similar storage without a meaningful name; Docker creates one when an image declares a VOLUME or a run command requests a volume without naming it. Anonymous volumes are easy to orphan, so named volumes are usually better for application data.
Volumes differ from bind mounts. A bind mount maps a specific host path into the container, such as $PWD/src to /app. A volume is managed by Docker and referred to by name. Use bind mounts for local development source files and explicit host paths. Use volumes as the default for databases, queues, uploads, indexes, and other service state.
Syntax
The main volume management command is:
docker volume COMMAND [OPTIONS]
| Command | Purpose |
|---|---|
docker volume create NAME |
Create a named volume before using it. |
docker volume ls |
List volumes known to the Docker daemon. |
docker volume inspect NAME |
Show driver, labels, mountpoint, and metadata. |
docker volume rm NAME |
Remove an unused volume. Docker refuses if a container still uses it. |
docker volume prune |
Remove all unused local volumes, optionally filtered. Treat this as destructive. |
To attach a volume to a container, prefer the explicit --mount form:
docker run --mount type=volume,source=notes-data,target=/data IMAGE
| Part | Meaning |
|---|---|
type=volume |
Tell Docker this is a managed volume mount. |
source=notes-data |
The volume name. If it does not exist, Docker creates it. |
target=/data |
The path where the volume appears inside the container. |
readonly |
Optional. Mount the volume read-only inside the container. |
-v notes-data:/data |
Common shorthand. Useful, but less self-documenting than --mount. |
Examples
Example 1: Create, label, list, and inspect a volume
Labels make volumes easier to find later, especially on a machine with many projects:
docker volume create --label app=notes --label tier=data notes-data
docker volume ls --filter label=app=notes
docker volume inspect notes-data
Output:
notes-data
DRIVER VOLUME NAME
local notes-data
[
{
"Name": "notes-data",
"Driver": "local",
"Labels": {
"app": "notes",
"tier": "data"
},
"Mountpoint": "/var/lib/docker/volumes/notes-data/_data"
}
]
The first command creates the volume using the default local driver. The list command filters by label, and inspect shows metadata. The mountpoint is useful for understanding where Docker stores data on Linux Engine, but production scripts should not depend on that internal path.
Example 2: Write data, remove the container, and read it again
This sequence uses short-lived Alpine containers to prove the data belongs to the volume, not to the container:
docker run --name notes-writer --mount type=volume,source=notes-data,target=/data alpine:3.20 sh -c 'printf "first note\n" > /data/note.txt'
docker rm notes-writer
docker run --rm --mount type=volume,source=notes-data,target=/data alpine:3.20 cat /data/note.txt
Output:
notes-writer
first note
The first container exits after writing a file. Removing that container deletes its writable layer, but the file remains in notes-data. The second container mounts the same volume and reads the same file.
Example 3: Back up and restore a volume with a helper container
A common pattern is to run a temporary utility container that mounts the source volume read-only and writes an archive or copied files somewhere else:
docker volume create notes-restore
docker run --rm --mount type=volume,source=notes-data,target=/from,readonly --mount type=volume,source=notes-restore,target=/to alpine:3.20 sh -c 'cd /from && tar -cf - . | tar -xf - -C /to'
docker run --rm --mount type=volume,source=notes-restore,target=/data alpine:3.20 cat /data/note.txt
Output:
notes-restore
first note
The helper container sees two mounted volumes. It reads from /from, streams a tar archive through standard output, and extracts it into /to. For real databases, prefer application-aware backups such as pg_dump or a consistent snapshot; copying live database files while the database is writing can produce a broken backup.
Example 4: Manage a named volume in Compose
Compose lets a project declare the volume alongside the services that use it:
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: changeme
POSTGRES_USER: appuser
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
labels:
app: notes
tier: data
Run the project with modern Compose V2:
docker compose up -d
docker compose down
Output:
[+] Running 3/3
✔ Network notes_default Created
✔ Volume notes_pgdata Created
✔ Container notes-db-1 Started
[+] Running 2/2
✔ Container notes-db-1 Removed
✔ Network notes_default Removed
docker compose down removes containers and the default network, but it does not remove named volumes by default. That is deliberate: service state should survive ordinary container recreation. Use docker compose down --volumes only when you intend to delete the project’s volume data.
How It Works Step By Step
- The Docker CLI sends a volume create, inspect, remove, or container create request to the Docker daemon.
- For
docker volume create, the daemon asks the selected volume driver to prepare storage and saves metadata such as name, driver, options, and labels. - When a container is created with a volume mount, Docker resolves the volume name. If the named volume does not exist, Docker creates it automatically.
- Docker prepares the container filesystem from the image layers and writable layer.
- Before starting the container process, Docker mounts the volume at the target path. Files from the image at that exact path are hidden while the mount is present.
- Writes under the target path go to the volume. Writes elsewhere go to the container writable layer.
- When the container stops or is removed, the volume remains until
docker volume rm,docker volume prune, ordocker compose down --volumesremoves it.
Common Mistakes
Assuming docker rm deletes volume data
This removes a container, not the named volume it used:
docker rm notes-writer
If the data must be deleted, remove the volume explicitly after confirming no container still uses it:
docker volume rm notes-data
Running broad prune commands without checking
This command deletes every unused local volume that matches the filter. Without a filter, it can remove old database and upload data from unrelated projects:
docker volume prune --force
A safer workflow is to list, inspect, back up if needed, then remove specific names:
docker volume ls --filter label=app=notes
docker volume inspect notes-restore
docker volume rm notes-restore
Mounting over initialized image data by accident
If an image contains starter files in /app/data, an empty volume mounted there hides those files. The image data is not deleted, but the container sees the volume at that path. Choose target paths deliberately and document which paths are persistent.
Copying live database files as a backup
A tar copy of a volume is fine for simple files and stopped services. For a running database, use the database’s backup tool or stop writes first. Docker protects storage from container deletion; it does not make arbitrary file copies transactionally consistent.
Best Practices
- Use named volumes for persistent service state; avoid anonymous volumes unless the data is disposable.
- Name volumes by purpose, such as
pgdata,uploads-data, orredis-data. - Add labels for project, environment, owner, or tier so cleanup commands can be filtered.
- Prefer
--mountin scripts and lessons because each field is explicit. - Back up important volumes before image upgrades, schema migrations, and prune operations.
- Use application-aware backups for databases instead of raw file copies while the service is running.
- Inspect volumes before removal, and remember Docker refuses to remove a volume still referenced by a container.
- In Compose, treat
docker compose down --volumesas a data-deleting command. - Do not bake secrets into images or volume seed files. Use mounted secret files or your orchestrator’s secret store for real credentials.
- On Docker Desktop, manage volumes through Docker commands because the actual storage is inside the Desktop VM.
Practice Exercises
- Create a volume named
uploads-datawith labels forapp=galleryandtier=data. List only volumes with the gallery label. - Use two short-lived Alpine containers: one writes a file into a named volume, and another reads it. The expected result is that the second container prints the file after the first container has been removed.
- Write a Compose file for PostgreSQL with a named volume. Bring it up, bring it down without deleting volumes, then identify the Docker-created volume name.
Summary
- Volumes are Docker-managed storage objects with lifecycles separate from containers.
- Volume management includes create, list, inspect, attach, back up, restore, and remove operations.
- Removing a container does not remove named volume data.
- Labels and meaningful names make volume cleanup safer.
- Compose preserves named volumes on ordinary
docker compose down. - Use raw volume copies only when the application state is safe to copy; databases often need their own backup tools.
