Managing Data and Volumes
Managing Data and Volumes
Containers are ephemeral—when they're deleted, their data disappears. This chapter covers how to persist data using volumes, bind mounts, and tmpfs mounts, along with strategies for managing application data effectively.
The Storage Problem
By default, all files created inside a container are stored in a writable container layer:
# Create a file in a container
docker run --name mycontainer alpine sh -c "echo 'Hello' > /data.txt"
# File exists
docker exec mycontainer cat /data.txt # Hello
# Remove container
docker rm mycontainer
# Data is gone foreverWarning
Container filesystems are temporary. Any data not stored in volumes is lost when the container is removed. This is by design—containers should be stateless and reproducible.
Storage Options
Docker provides three ways to persist data:
┌─────────────────────────────────────────────────────────┐
│ Container │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Writable Layer │ │
│ │ (Ephemeral - lost on remove) │ │
│ └─────────────────────────────────────────────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ ┌──────┴──────┐ ┌─────┴─────┐ ┌─────┴─────┐ │
│ │ Volume │ │Bind Mount │ │ tmpfs │ │
│ │ │ │ │ │ │ │
│ │ Docker- │ │ Host │ │ Memory │ │
│ │ managed │ │ directory │ │ only │ │
│ └─────────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────┘Storage Types Comparison
| Type | Location | Managed By | Persistence | Use Case |
|---|---|---|---|---|
| Volume | Docker area | Docker | Permanent | Production data |
| Bind mount | Host filesystem | You | Permanent | Development, config |
| tmpfs | Memory | Kernel | Until restart | Sensitive temp data |
Docker Volumes
Volumes are the preferred way to persist data. Docker manages them completely.
Creating Volumes
# Create a named volume
docker volume create mydata
# Create with options
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.1,rw \
--opt device=:/path/to/dir \
nfs-volume
# Volumes are created automatically when used
docker run -v newvolume:/data alpineListing and Inspecting Volumes
# List all volumes
docker volume ls
# Inspect a volume
docker volume inspect mydata
# Find where volume is stored
docker volume inspect mydata --format '{{.Mountpoint}}'
# /var/lib/docker/volumes/mydata/_dataUsing Volumes
# Mount volume at container path
docker run -d \
--name postgres \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# Multiple volumes
docker run -d \
-v app-data:/app/data \
-v app-logs:/app/logs \
myapp
# Read-only volume
docker run -d \
-v config:/app/config:ro \
myappNote
The -v or --volume flag uses the format
volume-name:container-path[:options]. The :ro option mounts the volume as
read-only.
Volume with --mount Syntax
The --mount syntax is more explicit and recommended:
# Using --mount
docker run -d \
--mount type=volume,source=pgdata,target=/var/lib/postgresql/data \
postgres:16
# With options
docker run -d \
--mount type=volume,source=config,target=/app/config,readonly \
myapp-v vs --mount
# -v syntax (shorter but less clear)
docker run -v myvolume:/data:ro myapp
# --mount syntax (more explicit)
docker run --mount type=volume,source=myvolume,target=/data,readonly myapp
# --mount fails if volume doesn't exist (safer)
# -v creates the volume automaticallyRemoving Volumes
# Remove a volume
docker volume rm mydata
# Remove all unused volumes
docker volume prune
# Force remove (no confirmation)
docker volume prune -f
# Remove container and its volumes
docker rm -v mycontainerWarning
Be careful with docker volume prune—it removes ALL volumes not attached to
containers. This can cause data loss.
Bind Mounts
Bind mounts map a host directory to a container path. Essential for development workflows.
Using Bind Mounts
# Mount current directory
docker run -v $(pwd):/app myapp
# Mount specific directory
docker run -v /home/user/project:/app myapp
# Using --mount syntax
docker run --mount type=bind,source=/home/user/project,target=/app myapp
# Read-only bind mount
docker run -v /etc/config:/app/config:ro myappDevelopment Workflow
Bind mounts enable live code changes:
# Mount source code for development
docker run -d \
--name dev-server \
-v $(pwd)/src:/app/src \
-v $(pwd)/package.json:/app/package.json \
-p 3000:3000 \
node:20-alpine npm run dev
# Changes to local ./src are immediately reflected in containerNote
On macOS and Windows, bind mounts can be slower than on Linux due to filesystem translation. Docker Desktop provides optimization options for improved performance.
Bind Mount Permissions
# Specify user/group mapping
docker run -v $(pwd):/app -u $(id -u):$(id -g) myapp
# Match host user ID
docker run -v $(pwd):/app --user 1000:1000 myappFile vs Directory Mounts
# Mount a single file
docker run -v $(pwd)/config.json:/app/config.json myapp
# Mount a directory
docker run -v $(pwd)/config:/app/config myappWarning
When mounting a single file, the file must exist before the container starts. For directories, Docker creates them if they don't exist.
tmpfs Mounts
tmpfs mounts store data in memory—never written to disk.
Using tmpfs
# Create tmpfs mount
docker run -d \
--tmpfs /app/temp \
myapp
# With options
docker run -d \
--mount type=tmpfs,target=/app/temp,tmpfs-size=100m,tmpfs-mode=1777 \
myapp
# Multiple tmpfs mounts
docker run -d \
--tmpfs /app/temp \
--tmpfs /app/cache \
myapptmpfs Use Cases
- Sensitive data that shouldn't persist (tokens, keys during build)
- High-speed temporary storage
- Preventing writes to container filesystem
tmpfs for Secrets
# Sensitive data in tmpfs (never hits disk)
docker run -d \
--tmpfs /run/secrets:size=1m,mode=0700 \
-e SECRET_FILE=/run/secrets/api-key \
myappVolume Backup and Restore
Backup a Volume
# Backup volume to tar file
docker run --rm \
-v mydata:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/mydata-backup.tar.gz -C /source .
# Alternative: copy to stdout
docker run --rm \
-v mydata:/source:ro \
alpine tar czf - -C /source . > mydata-backup.tar.gzRestore a Volume
# Restore from backup
docker run --rm \
-v mydata:/target \
-v $(pwd):/backup \
alpine tar xzf /backup/mydata-backup.tar.gz -C /target
# Or from stdin
docker run --rm -i \
-v mydata:/target \
alpine tar xzf - -C /target < mydata-backup.tar.gzCopy Between Volumes
# Copy data between volumes
docker run --rm \
-v source-vol:/source:ro \
-v dest-vol:/dest \
alpine cp -a /source/. /dest/Database Volume Patterns
PostgreSQL
# PostgreSQL with named volume
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=secret \
-v postgres-data:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16
# Check data directory
docker exec postgres ls -la /var/lib/postgresql/dataMySQL
# MySQL with named volume
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8MongoDB
# MongoDB with named volume
docker run -d \
--name mongo \
-v mongo-data:/data/db \
-p 27017:27017 \
mongo:7Note
Always check the official image documentation for the correct data directory path. Using the wrong path means your data won't persist.
Volume Drivers
Docker supports volume plugins for different storage backends.
Local Driver Options
# Create volume with specific options
docker volume create \
--driver local \
--opt type=none \
--opt o=bind \
--opt device=/path/on/host \
my-local-vol
# NFS volume
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=nfs-server.example.com,rw \
--opt device=:/exported/path \
nfs-volThird-Party Volume Drivers
Popular volume drivers for production:
- REX-Ray: Multi-platform storage orchestration
- NetApp: NetApp storage integration
- Portworx: Cloud-native storage
- AWS EBS/EFS: Amazon storage services
# Install and use a volume driver
docker plugin install rexray/ebs
docker volume create --driver rexray/ebs --opt size=10 ebs-volVolume Best Practices
Use Named Volumes in Production
# GOOD: Named volume (easy to manage)
docker run -v postgres-data:/var/lib/postgresql/data postgres:16
# AVOID: Anonymous volume (hard to track)
docker run -v /var/lib/postgresql/data postgres:16Separate Data and Application
# Separate volumes for different data types
docker run -d \
-v app-data:/app/data \ # Application data
-v app-logs:/app/logs \ # Logs (may rotate differently)
-v app-uploads:/app/uploads \ # User uploads
myappUse Read-Only Where Possible
# Application code should be read-only in production
docker run -d \
-v app-code:/app:ro \
-v app-data:/app/data \
myappDon't Store Secrets in Volumes
# AVOID: Secrets in volumes
docker run -v secrets:/secrets myapp
# BETTER: Use Docker secrets or environment variables
docker run -e DATABASE_URL=postgres://... myappTroubleshooting Volumes
Check Volume Contents
# Start a debug container to inspect volume
docker run -it --rm \
-v mydata:/data \
alpine sh
# Inside the container:
ls -la /data
du -sh /dataFind Volume Usage
# Find which containers use a volume
docker ps -a --filter volume=mydata
# Check volume size
docker system df -v | grep mydataPermission Issues
# Check permissions inside container
docker exec mycontainer ls -la /data
# Fix permissions (careful!)
docker run --rm \
-v mydata:/data \
alpine chown -R 1000:1000 /dataWarning
Permission issues are common when the container user doesn't match the volume's file ownership. Use matching UID/GID or adjust ownership during container startup.
Volume Not Mounting
# Verify volume exists
docker volume ls | grep myvolume
# Check mount in running container
docker inspect mycontainer --format '{{json .Mounts}}'
# View mount from inside container
docker exec mycontainer df -h
docker exec mycontainer mount | grep /dataQuick Reference
| Command | Purpose |
|---|---|
docker volume create | Create a volume |
docker volume ls | List volumes |
docker volume inspect | View volume details |
docker volume rm | Remove a volume |
docker volume prune | Remove unused volumes |
Mount Options Summary
| Option | Purpose |
|---|---|
type | volume, bind, or tmpfs |
source | Volume name or host path |
target | Path inside container |
readonly | Mount as read-only |
volume-driver | Volume driver name |
volume-opt | Driver-specific options |
In the next chapter, we'll explore Docker Compose for orchestrating multi-container applications.