Docker volumes cheatsheet.
Three types
- Named volume: managed by Docker, persists between containers.
- Bind mount: maps a host directory into container.
- tmpfs: in-memory, not persistent.
Named volume
docker volume create data
docker run -v data:/var/lib/data app
docker run --mount type=volume,src=data,dst=/var/lib/data app
Bind mount
docker run -v $(pwd)/local:/app app
docker run -v $(pwd):/app:ro app # read-only
docker run --mount type=bind,src=/path,dst=/app app
tmpfs
docker run --tmpfs /tmp app
docker run --mount type=tmpfs,dst=/tmp,tmpfs-size=64m app
Useful for sensitive temp data.
Volume management
docker volume ls
docker volume inspect data
docker volume rm data
docker volume prune # remove unused
Anonymous volumes
docker run -v /app/node_modules app
Preserves /app/node_modules from the image even when bind-mounting . over /app.
volumes:
- .:/app
- /app/node_modules # anonymous, hides bind mount
Volume drivers
docker volume create --driver local --opt type=nfs \
--opt o=addr=192.168.1.10,rw \
--opt device=:/path nfs_data
Plugins for NFS, S3, Ceph, etc.
Compose volumes
services:
db:
volumes:
- pg_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
volumes:
pg_data:
cache:
external: true # already exists
Backup
docker run --rm -v pg_data:/data -v $(pwd):/backup alpine \
tar czf /backup/pg_data.tar.gz -C /data .
Restore
docker run --rm -v pg_data:/data -v $(pwd):/backup alpine \
sh -c "cd /data && tar xzf /backup/pg_data.tar.gz"
DB volume backup (Postgres)
docker exec db pg_dump -U postgres myapp > backup.sql
docker exec -i db psql -U postgres myapp < backup.sql
Ownership / permissions
Bind mounts inherit host user/group:
docker run -u $(id -u):$(id -g) -v $(pwd):/app app
Otherwise files created by container may be owned by root (annoying).
RUN useradd -u 1000 -m app
USER app
Match UID with host user.
consistency (Mac/Windows)
volumes:
- .:/app:cached # host -> container can lag
- .:/app:delegated # container -> host can lag
- .:/app:consistent # default; can be slow
Performance on Mac/Windows is bad due to virtualization. Use cached for read-heavy.
SELinux (Linux)
docker run -v $(pwd):/app:z app # shared
docker run -v $(pwd):/app:Z app # private
Or set --security-opt label=disable.
Read-only root filesystem
docker run --read-only --tmpfs /tmp --tmpfs /var/run app
Forces immutable container. Tighten security; ensure app doesn’t need to write outside specified mounts.
Sharing data between containers
docker volume create shared
docker run -d --name producer -v shared:/data producer
docker run -d --name consumer -v shared:/data consumer
Both see /data.
Initialize from image
FROM postgres:16
COPY ./init.sql /docker-entrypoint-initdb.d/
Or via compose:
db:
image: postgres:16
volumes:
- ./init:/docker-entrypoint-initdb.d:ro
- pg_data:/var/lib/postgresql/data
Don’t bind-mount over a database
# BAD
volumes:
- ./pg_data:/var/lib/postgresql/data
Bind mounts have host filesystem semantics — Postgres on Mac/Windows hits perf issues. Use named volume.
Migration / size monitoring
docker system df -v
docker volume inspect data | grep Mountpoint
du -sh /var/lib/docker/volumes/data/_data
Common mistakes
- Bind-mounting
.over/appwiping the image’snode_modules. - Forgetting
:rofor config files. - Volume contains stale data from old image build.
- UID mismatch → permission errors.
- Anonymous volumes piling up after
docker rm.
Read this next
If you want my backup + restore scripts, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .