One of the fundamental challenges when working with Docker containers is data persistence. By default, containers are ephemeral - when they stop or are removed, all data inside them disappears. This creates a problem for applications that need to store data permanently, such as databases, user uploads, or configuration files.
Docker volumes solve this problem by providing a way to persist data outside the container's filesystem. In this comprehensive guide, we'll explore everything you need to know about Docker volumes, from basic concepts to advanced use cases.
Before diving into volumes, it's important to understand how Docker handles data storage by default:
# Start a container and create a file docker run -it ubuntu:latest bash echo "Important data" > /tmp/data.txt exit # Start the same container again - the file is gone! docker run -it ubuntu:latest bash cat /tmp/data.txt # File not found
Docker volumes are the preferred mechanism for persisting data generated and used by Docker containers. Volumes are completely managed by Docker and offer several advantages:
Docker provides three main ways to mount data into containers:
# Create a named volume docker volume create my-volume # List all volumes docker volume ls # Inspect volume details docker volume inspect my-volume
# Mount a volume to a container docker run -d -v my-volume:/data ubuntu:latest # Alternative syntax using --mount docker run -d --mount source=my-volume,target=/data ubuntu:latest # Create volume and container in one command docker run -d -v my-data:/app/data nginx:latest
# Create an anonymous volume docker run -d -v /data ubuntu:latest # Docker automatically creates a volume with a random name docker volume ls
# Create a volume for MySQL data docker volume create mysql-data # Run MySQL with persistent storage docker run -d \ --name mysql-db \ -e MYSQL_ROOT_PASSWORD=secret \ -v mysql-data:/var/lib/mysql \ mysql:8.0 # Data persists even if container is removed docker rm -f mysql-db docker run -d \ --name mysql-db-new \ -e MYSQL_ROOT_PASSWORD=secret \ -v mysql-data:/var/lib/mysql \ mysql:8.0
# Create volume for user uploads docker volume create app-uploads # Run web application docker run -d \ --name webapp \ -p 8080:80 \ -v app-uploads:/var/www/html/uploads \ nginx:latest # Upload files persist across container restarts
# Mount current directory for development docker run -d \ --name dev-container \ -p 3000:3000 \ -v $(pwd):/app \ -w /app \ node:16-alpine \ npm start # Changes in local files reflect immediately in container
# Volume (managed by Docker) docker run -d -v my-volume:/data nginx:latest # Bind mount (host directory) docker run -d -v /host/path:/data nginx:latest # Bind mount current directory docker run -d -v $(pwd):/data nginx:latest
Docker supports different volume drivers for various storage backends:
# Local driver (default) docker volume create --driver local my-volume # Create volume with specific options docker volume create \ --driver local \ --opt type=nfs \ --opt o=addr=192.168.1.1,rw \ --opt device=:/path/to/share \ nfs-volume
# Mount volume as read-only docker run -d -v my-volume:/data:ro nginx:latest # Using --mount syntax docker run -d --mount source=my-volume,target=/data,readonly nginx:latest
# Create volume with labels docker volume create \ --label environment=production \ --label backup=daily \ prod-data # Filter volumes by labels docker volume ls --filter label=environment=production
# Create shared volume docker volume create shared-data # Container 1: Writer docker run -d \ --name writer \ -v shared-data:/data \ ubuntu:latest \ bash -c "while true; do echo $(date) >> /data/log.txt; sleep 5; done" # Container 2: Reader docker run -d \ --name reader \ -v shared-data:/data \ ubuntu:latest \ bash -c "while true; do tail -f /data/log.txt; sleep 1; done"
# Create data container docker create -v /data --name data-container ubuntu:latest # Use data container in other containers docker run -d --volumes-from data-container nginx:latest docker run -d --volumes-from data-container mysql:8.0
# Create backup of volume data docker run --rm \ -v my-volume:/data \ -v $(pwd):/backup \ ubuntu:latest \ tar czf /backup/volume-backup.tar.gz -C /data . # Alternative using rsync docker run --rm \ -v my-volume:/data \ -v $(pwd):/backup \ instrumentisto/rsync \ rsync -av /data/ /backup/
# Create new volume docker volume create restored-volume # Restore data from backup docker run --rm \ -v restored-volume:/data \ -v $(pwd):/backup \ ubuntu:latest \ tar xzf /backup/volume-backup.tar.gz -C /data
# Copy data between volumes docker run --rm \ -v source-volume:/source \ -v target-volume:/target \ ubuntu:latest \ cp -av /source/. /target/
version: '3.8'
services:
web:
image: nginx:latest
volumes:
- web-content:/usr/share/nginx/html
- ./config:/etc/nginx/conf.d:ro
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
volumes:
- db-data:/var/lib/mysql
volumes:
web-content:
db-data:
version: '3.8'
services:
app:
image: myapp:latest
volumes:
- existing-volume:/data
volumes:
existing-volume:
external: true
version: '3.8'
services:
app:
image: myapp:latest
volumes:
- type: volume
source: app-data
target: /data
volume:
nocopy: true
- type: bind
source: ./config
target: /etc/app
read_only: true
volumes:
app-data:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.1,rw
device: ":/path/to/share"
# Mount tmpfs for temporary files docker run -d \ --tmpfs /tmp:rw,noexec,nosuid,size=1g \ nginx:latest # Using --mount syntax docker run -d \ --mount type=tmpfs,destination=/tmp,tmpfs-size=1g \ nginx:latest
# Read-only bind mount docker run -d -v /host/config:/app/config:ro nginx:latest # Mount with specific user docker run -d \ --user 1000:1000 \ -v app-data:/data \ myapp:latest # Limit volume size (if supported by driver) docker volume create \ --opt size=10G \ limited-volume
# Check volume permissions docker run --rm -v my-volume:/data ubuntu:latest ls -la /data # Fix ownership issues docker run --rm -v my-volume:/data ubuntu:latest chown -R 1000:1000 /data # Set proper permissions docker run --rm -v my-volume:/data ubuntu:latest chmod -R 755 /data
# List all volumes to verify existence docker volume ls # Create missing volume docker volume create missing-volume # Inspect volume details docker volume inspect my-volume
# Check container mount points
docker inspect container-name --format='{{json .Mounts}}'
# Verify volume contents
docker run --rm -v my-volume:/data ubuntu:latest find /data -type f
# Check volume usage
docker system df -v
# Remove specific volume docker volume rm my-volume # Remove all unused volumes docker volume prune # Remove volumes with confirmation docker volume prune --force # Remove volumes matching filter docker volume prune --filter label=environment=test
# Check Docker system usage docker system df # Detailed volume usage docker system df -v # Clean up everything unused docker system prune --volumes
# Complete development stack with volumes
version: '3.8'
services:
app:
build: .
volumes:
- .:/app
- node_modules:/app/node_modules
ports:
- "3000:3000"
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
node_modules:
postgres_data:
# Production-ready database with backup docker run -d \ --name production-db \ --restart unless-stopped \ -e MYSQL_ROOT_PASSWORD=secure_password \ -v mysql_data:/var/lib/mysql \ -v mysql_config:/etc/mysql/conf.d:ro \ -v mysql_backups:/backups \ mysql:8.0
# Centralized logging setup docker run -d \ --name log-collector \ -v app_logs:/var/log/app \ -v system_logs:/var/log/system \ logstash:latest
Docker volumes are essential for building robust, production-ready containerized applications. They provide a reliable way to persist data, share information between containers, and maintain data integrity across container lifecycles.
Understanding the different types of volume mounts - volumes, bind mounts, and tmpfs - allows you to choose the right approach for each use case. Whether you're building development environments, production databases, or complex multi-container applications, proper volume management is crucial for success.
Start with simple named volumes for basic persistence needs, then explore advanced features like volume drivers, backup strategies, and security configurations as your requirements grow. Remember to regularly maintain your volumes, monitor usage, and follow security best practices to ensure your containerized applications run smoothly and securely.
With the knowledge from this guide, you're equipped to handle data persistence challenges in your Docker projects effectively and efficiently.