Skip to main content

Volume Management

Volumes are Docker's recommended mechanism for persisting data. Unlike data written to the container's writable layer, volume data survives container removal and can be shared between containers. This lesson covers creating, inspecting, and managing all three mount types.

Mount Types at a Glance

flowchart LR
subgraph host["Host Machine"]
V["/var/lib/docker/volumes/\n(Docker-managed)"]
B["/home/user/project/\n(Your directory)"]
T["RAM\n(Memory)"]
end

subgraph container["Container"]
CV["/data"]
CB["/app"]
CT["/tmp/secrets"]
end

V <-->|"Volume"| CV
B <-->|"Bind Mount"| CB
T <-->|"tmpfs"| CT

style V fill:#e8f5e9,stroke:#2e7d32
style B fill:#e3f2fd,stroke:#1565c0
style T fill:#fff3e0,stroke:#ef6c00
FeatureNamed VolumeBind Mounttmpfs
LocationDocker-managed directoryAny host pathMemory (RAM)
Created bydocker volume create or autoUser specifies path--tmpfs flag
Content on first usePopulated from imageHost directory contentEmpty
Portable across hosts✅ Easy (backup/restore)❌ Path-dependent❌ Not persisted
Managed by Docker✅ Yes❌ No❌ No
PerformanceNativeNativeVery fast (RAM)
Best forDatabase data, app stateDev code, host configsSecrets, scratch space

Named Volumes

Named volumes are the recommended approach for persistent data.

Creating and Using

# Create a named volume
docker volume create pgdata

# Use it with a container
docker run -d \
--name db \
-v pgdata:/var/lib/postgresql/data \
postgres:16

Auto-Creation

If a named volume does not exist, Docker creates it automatically:

# pgdata2 is created automatically
docker run -d -v pgdata2:/var/lib/postgresql/data postgres:16

Volume CRUD Operations

# Create
docker volume create mydata

# List all volumes
docker volume ls

# Inspect a volume (shows mount point, driver, etc.)
docker volume inspect mydata

# Remove a specific volume
docker volume rm mydata

# Remove all unused volumes
docker volume prune

Inspecting Volume Details

docker volume inspect pgdata

Output:

[
{
"CreatedAt": "2024-01-15T10:30:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
"Name": "pgdata",
"Options": {},
"Scope": "local"
}
]
warning

docker volume prune removes all volumes not attached to a running container. Stopped containers' volumes are also removed. Always double-check before running this command.

Bind Mounts

Bind mounts map a specific host directory into the container.

Basic Usage

# Mount current directory into /app
docker run -d \
--name dev \
-v "$(pwd)":/app \
-w /app \
node:20-alpine npm run dev

Read-Only Bind Mounts

# Mount config as read-only
docker run -d \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx:alpine

The :ro suffix prevents the container from modifying the host file.

Common Bind Mount Patterns

PatternExampleUse Case
Source code mount-v "$(pwd)":/appDevelopment hot-reload
Config file mount-v ./nginx.conf:/etc/nginx/nginx.conf:roCustom configuration
Log directory-v /var/log/myapp:/var/log/appCentralized logging
Socket mount-v /var/run/docker.sock:/var/run/docker.sockDocker-in-Docker tools
danger

Mounting the Docker socket (docker.sock) gives the container full control over the Docker daemon. Only do this for trusted tools like Portainer or Traefik.

tmpfs Mounts

tmpfs mounts store data in the host's memory (RAM). Data is never written to disk and is lost when the container stops.

docker run -d \
--name api \
--tmpfs /tmp:size=100m \
my-api:1.0.0

When to Use tmpfs

ScenarioWhy tmpfs?
Secrets / tokensNever touches disk -- more secure
Scratch space for processingFast reads/writes, auto-cleaned
Temporary session dataNo persistence needed

Volumes in Docker Compose

Named Volumes

services:
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data

api:
image: my-api:1.0.0
volumes:
- uploads:/app/uploads

volumes:
pgdata:
uploads:

The top-level volumes: section declares named volumes. Compose creates them automatically.

Bind Mounts

services:
api:
image: my-api:1.0.0
volumes:
- ./src:/app/src # Relative path bind mount
- ./config:/app/config:ro # Read-only

tmpfs

services:
api:
image: my-api:1.0.0
tmpfs:
- /tmp:size=100m

Sharing Volumes Between Services

services:
writer:
image: my-writer:1.0.0
volumes:
- shared-data:/data

reader:
image: my-reader:1.0.0
volumes:
- shared-data:/data:ro

volumes:
shared-data:

Long-Form --mount Syntax

The --mount flag is more explicit than -v and is easier to read for complex configurations:

# Named volume
docker run -d \
--mount type=volume,source=pgdata,target=/var/lib/postgresql/data \
postgres:16

# Bind mount (read-only)
docker run -d \
--mount type=bind,source="$(pwd)"/config,target=/app/config,readonly \
my-api:1.0.0

# tmpfs
docker run -d \
--mount type=tmpfs,target=/tmp,tmpfs-size=104857600 \
my-api:1.0.0

-v vs --mount Comparison

Feature-v--mount
Auto-creates volume✅ Yes✅ Yes
Auto-creates bind host path✅ Yes (can mask errors)❌ Errors if path missing
ReadabilityShort but ambiguousVerbose but clear
Recommended forQuick usage, scriptsProduction, Compose files
tip

For production use, prefer --mount because it fails explicitly when a host path is missing, rather than silently creating an empty directory.

Checking What's Mounted

# List mounts for a container
docker inspect -f '{{json .Mounts}}' my-container | python3 -m json.tool

# Quick view of all volumes used by running containers
docker ps --format '{{.Names}}' | xargs -I{} docker inspect -f '{{.Name}}: {{range .Mounts}}{{.Type}}:{{.Source}}->{{.Destination}} {{end}}' {}

Key Takeaways

  • Named volumes are the recommended storage for persistent data -- Docker manages them and they are portable.
  • Bind mounts map host directories -- ideal for development and config files, but path-dependent.
  • tmpfs mounts live in RAM -- use for secrets and scratch data that should never touch disk.
  • Use :ro suffix to mount volumes read-only where the container should not modify data.
  • docker volume prune is dangerous -- verify what is unused before running it.

What's Next