Docker Volumes Guide
By default, data inside a container is ephemeral—it disappears when the container stops. Docker volumes provide persistent storage that survives container lifecycle. This guide covers named volumes, bind mounts, tmpfs mounts, and volume management best practices.
Containers are designed to be ephemeral—you can stop, delete, and recreate them at any time. When a container is deleted, all data stored in its writable layer is lost. This is problematic for databases, user uploads, logs, configuration files, and any other data that must persist beyond a container's lifetime.
Docker volumes solve this problem by providing storage that exists independently of containers. Volumes can be shared between multiple containers, backed up, migrated, and managed separately from the containers that use them. Docker supports three main types of mounts: named volumes (managed by Docker), bind mounts (host directories), and tmpfs mounts (in-memory storage).
| Feature | Named Volumes | Bind Mounts | tmpfs |
|---|---|---|---|
| Managed by | Docker | User (host filesystem)Memory (RAM)||
| Persistence | Yes | Yes | No (in-memory only) |
| Performance | Good | Best (native) | Fastest |
| Backup/Restore | Easy | Manual | N/A |
| Use Case | Databases, production data | Development, config files | Temporary, sensitive data |
| Cross-platform | Yes | Paths differ | Linux only |
Named volumes are created and managed by Docker. They are stored in Docker's storage directory (usually /var/lib/docker/volumes/ on Linux). Named volumes are the recommended way to persist data in production because they are portable, can be backed up, and are managed by Docker commands.
Benefits of named volumes: Docker handles permissions automatically, they work the same on all platforms, they can be backed up and restored using Docker commands, and they can be shared between multiple containers.
# Create a named volume
docker volume create postgres_data
# List volumes
docker volume ls
# Inspect a volume
docker volume inspect postgres_data
# Run container with named volume
docker run -d \
--name postgres \
-v postgres_data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:15
# Using --mount syntax (more explicit)
docker run -d \
--name postgres \
--mount type=volume,source=postgres_data,target=/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:15
# Data persists even after container deletion
docker stop postgres
docker rm postgres
docker run -d --name new-postgres -v postgres_data:/var/lib/postgresql/data postgres:15
# Data is still there!
Bind mounts map a directory from the host filesystem directly into the container. They are the preferred choice for development because they allow you to edit code on the host and see changes instantly in the container (hot reload). Bind mounts are also useful for providing configuration files or sharing the Docker socket.
However, bind mounts are less portable than named volumes because they depend on host directory paths. They also expose host filesystem permissions issues—the container must have appropriate permissions to read/write the host directory.
# Bind mount current directory to /app in container
docker run -d \
--name dev \
-v "$PWD":/app \
-w /app \
node:18 npm run dev
# Using --mount syntax (more explicit)
docker run -d \
--name dev \
--mount type=bind,source="$(pwd)",target=/app \
-w /app \
node:18 npm run dev
# Read-only bind mount (prevent container from writing)
docker run -d \
--name nginx \
-v "$PWD/nginx.conf":/etc/nginx/nginx.conf:ro \
nginx
# Mount single file
docker run -it --rm \
-v "$PWD/config.json":/app/config.json:ro \
alpine cat /app/config.json
tmpfs mounts store data in the host's memory (RAM), not on disk. This is extremely fast but data is lost when the container stops. tmpfs is perfect for temporary, sensitive data that shouldn't be persisted or written to disk—like session tokens, cache, or temporary files.
Important: tmpfs only works on Linux hosts. On Docker Desktop, it falls back to disk-based storage. tmpfs mounts cannot be shared between containers.
# Run container with tmpfs mount
docker run -d \
--name cache \
--tmpfs /app/cache:rw,noexec,nosuid,size=100m \
redis
# Using --mount syntax
docker run -d \
--name cache \
--mount type=tmpfs,destination=/app/cache,tmpfs-size=100M,tmpfs-mode=1777 \
redis
# Multiple tmpfs mounts
docker run -d \
--name app \
--tmpfs /tmp:rw,size=50m \
--tmpfs /dev/shm:rw,size=1g \
myapp
# Verify mount
docker exec cache df -h | grep /app/cache
# Create a volume
docker volume create mydata
# List volumes
docker volume ls
# Inspect volume (shows mountpoint on host)
docker volume inspect mydata
# Remove a volume (only if not in use)
docker volume rm mydata
# Remove all unused volumes
docker volume prune
# Remove all volumes (including used - dangerous!)
docker volume prune -a
# Copy data into a volume using a temporary container
docker run --rm -v mydata:/data alpine cp /tmp/data.txt /data/
# Backup a volume
docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar czf /backup/mydata-backup.tar.gz /data
# Restore a volume from backup
docker run --rm -v mydata:/data -v $(pwd):/backup alpine tar xzf /backup/mydata-backup.tar.gz -C /
# docker-compose.yml
version: '3.8'
volumes:
postgres_data:
driver: local
redis_data:
services:
postgres:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
environment:
POSTGRES_PASSWORD: secret
redis:
image: redis:alpine
volumes:
- redis_data:/data
command: redis-server --appendonly yes
app:
image: myapp
volumes:
- ./src:/app/src:ro
- /app/node_modules # Anonymous volume (Docker managed)
- type: tmpfs
target: /app/tmp
tmpfs:
size: 100M
dev:
image: node:18
working_dir: /app
volumes:
- .:/app
- /app/node_modules # Prevents overwriting container's node_modules
command: npm run dev
Volumes can be shared between multiple containers. This is useful for patterns like web server + file uploader, or log aggregator + log writer. The same volume can be mounted into many containers simultaneously.
# Create shared volume
docker volume create shared_data
# Container A writes to volume
docker run -d --name writer -v shared_data:/data alpine \
sh -c "while true; do echo $(date) >> /data/log.txt; sleep 5; done"
# Container B reads from same volume
docker run -it --rm --name reader -v shared_data:/data alpine cat /data/log.txt
# Container C also writes to same volume
docker run -d --name logger -v shared_data:/logs alpine \
sh -c "while true; do echo 'Log entry' >> /logs/app.log; sleep 10; done"
# All three containers see the same files
Docker supports volume drivers that allow storing data on remote systems: NFS, AWS EBS, Azure File Storage, Google Cloud Storage, and more. This enables stateful containers in clustered environments where containers can move between hosts while preserving data.
# Create NFS volume
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/exported/path \
nfs-volume
# Run container with NFS volume
docker run -d --name app -v nfs-volume:/data myapp
# AWS EBS volume (using rexray driver)
docker volume create \
--driver rexray/ebs \
--opt size=10 \
--opt volumetype=gp2 \
ebs-volume
# Run container with cloud volume
docker run -d --name db -v ebs-volume:/var/lib/mysql mysql
- Use named volumes for production data - They are portable, backup-friendly, and managed by Docker.
- Use bind mounts for development - Code changes on host reflect instantly in containers.
- Never store important data only in containers - Always use volumes for databases, uploads, logs.
- Use .dockerignore for bind mounts - Prevent accidentally copying node_modules or secrets into containers.
- Use tmpfs for sensitive temporary data - Session tokens, cache, temporary files that shouldn't hit disk.
- Back up volumes regularly - Use the backup/restore patterns shown above.
- Remove unused volumes - Use
docker volume pruneto free disk space. - Use volume drivers for clustered environments - NFS or cloud storage for multi-host persistence.
Docker volumes are essential for persistent data in containerized applications. Choose named volumes for production, bind mounts for development, and tmpfs for temporary in-memory storage.