Docker Storage: Volumes and Bind Mounts
📅 Published: Feb 2026
⏱️ Estimated Reading Time: 16 minutes
🏷️ Tags: Docker, Storage, Volumes, Bind Mounts, Data Persistence, Docker Volumes
Introduction: The Storage Problem
Containers are ephemeral by design. When a container is removed, everything written inside it disappears. This is great for stateless applications. But most real applications need to persist data. Databases need to store records. Web applications need to save user uploads. Logs need to be retained.
Docker solves this problem with two storage mechanisms: volumes and bind mounts. Both allow containers to store data outside their writable layer, ensuring data survives container restarts and removals.
This guide explains when to use each type, how to configure them, and best practices for production deployments.
Part 1: Docker Storage Types
The Three Storage Options
Docker provides three ways to manage data in containers:
| Storage Type | Managed By | Location | Use Case |
|---|---|---|---|
| Volumes | Docker | /var/lib/docker/volumes/ | Production data, databases, shared data |
| Bind Mounts | You | Anywhere on host | Development code, config files |
| tmpfs mounts | Docker | Memory | Temporary data, secrets |
Container Writable Layer
Every container has a writable layer on top of the image layers. When you write data in a container, it goes to this writable layer. But when the container is removed, this layer is lost.
┌─────────────────┐ │ Writable Layer │ ← Data here is lost when container is removed ├─────────────────┤ │ Image Layers │ ← Read-only, shared between containers └─────────────────┘
Volumes and bind mounts bypass the writable layer. Data written to them persists even after the container is removed.
Part 2: Volumes
What Are Volumes?
Volumes are the preferred way to persist data in Docker. They are completely managed by Docker. Volumes are stored in a part of the host filesystem managed by Docker (/var/lib/docker/volumes/ on Linux). You should not modify these files directly.
Why Volumes Are Preferred
Managed by Docker
Docker handles volume creation, backup, and restoration. You don't need to manage filesystem permissions or locations.
Cross-Platform
Volumes work the same on Linux, Windows, and Mac. Bind mounts have different behaviors across platforms.
Backup and Migration
Docker provides commands to backup and restore volumes. You can easily move volumes between hosts.
Performance
On Linux, volumes have better performance than bind mounts because they use the native filesystem.
Volume Drivers
Volumes support drivers for network storage, cloud storage, and encryption.
Creating and Using Volumes
Create a volume
# Create named volume docker volume create mydata # List volumes docker volume ls # Inspect volume docker volume inspect mydata
Use volume in a container
# Mount volume to /data in container docker run -v mydata:/data alpine touch /data/test.txt # Another container can access the same data docker run -v mydata:/data alpine ls /data # Output: test.txt
Shorthand syntax
# Create and mount in one command docker run -v myvolume:/app/data nginx
Mount syntax (recommended)
The --mount syntax is more explicit and preferred for complex mounts.
docker run --mount source=mydata,target=/data alpine
Volume Drivers
Volume drivers allow storing data outside the local host.
# Create volume with driver docker volume create --driver flocker mydata # Use with cloud storage docker volume create --driver cloudstor:aws --opt size=10 mydata
Backup and Restore Volumes
Backup a volume
# Run a temporary container that copies volume contents docker run --rm -v mydata:/source -v $(pwd):/backup alpine \ tar czf /backup/mydata-backup.tar.gz -C /source .
Restore a volume
# Restore from backup docker run --rm -v mydata:/target -v $(pwd):/backup alpine \ tar xzf /backup/mydata-backup.tar.gz -C /target
Removing Volumes
# Remove volume docker volume rm mydata # Remove unused volumes docker volume prune # Remove all unused volumes docker volume prune -a
Part 3: Bind Mounts
What Are Bind Mounts?
Bind mounts map a directory or file from the host into the container. You control the exact location on the host. Unlike volumes, bind mounts are not managed by Docker.
When to Use Bind Mounts
Development
Bind mounts allow you to edit code on your host and see changes immediately in the container. No rebuild needed.
Configuration files
You can mount specific configuration files from the host into containers.
Sharing host data
When containers need to access existing data on the host.
Using Bind Mounts
Basic bind mount
# Mount current directory to /app in container docker run -v $(pwd):/app node:18 npm start # Mount specific directory docker run -v /host/data:/container/data nginx
Mount syntax
# Using --mount (recommended) docker run --mount type=bind,source=$(pwd),target=/app node:18 # Read-only bind mount docker run --mount type=bind,source=$(pwd),target=/app,readonly node:18
Bind Mount Examples
Development with live reload
# Mount code, run with hot reload docker run -v $(pwd):/app -p 3000:3000 node:18 npm run dev
Mount configuration file
# Mount custom nginx config docker run -v /host/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 nginx
Mount multiple locations
docker run \ -v /host/data:/data \ -v /host/config/app.conf:/app/config/app.conf:ro \ myapp
Part 4: Volumes vs Bind Mounts
Comparison Table
| Feature | Volumes | Bind Mounts |
|---|---|---|
| Managed by | Docker | You |
| Location | /var/lib/docker/volumes/ | Anywhere on host |
| Backup | Docker commands | Manual |
| Cross-platform | Yes | Limited |
| Performance (Linux) | Native | Good |
| Performance (Mac/Windows) | Good | Slower (through VM) |
| Permissions | Docker manages | You manage |
| Portability | High | Low |
| Use in Dockerfile | No | No |
| Volume drivers | Yes | No |
When to Use Volumes
Production databases
Volumes provide better performance and are managed by Docker.
Persistent application data
When data must survive container removal.
Sharing data between containers
Multiple containers can mount the same volume.
Backup and restore
Docker provides built-in backup commands.
When you need volume drivers
For network storage or cloud storage.
When to Use Bind Mounts
Development
Edit code on host, see changes in container without rebuild.
Configuration management
Mount specific config files from host.
Accessing host data
When containers need to read or write to specific host directories.
Testing
Quickly test changes without building images.
Part 5: Advanced Storage Patterns
Pattern 1: Sharing Data Between Containers
Using named volume
# Create volume docker volume create shared-data # First container writes data docker run -v shared-data:/data alpine sh -c "echo hello > /data/file.txt" # Second container reads data docker run -v shared-data:/data alpine cat /data/file.txt
Using container volumes (--volumes-from)
# Create data container docker create -v /data --name data-container alpine # Other containers share the volume docker run --volumes-from data-container alpine ls /data
Pattern 2: Database with Persistent Storage
# Create volume for database docker volume create postgres-data # Run PostgreSQL with volume docker run -d \ --name postgres \ -e POSTGRES_PASSWORD=secret \ -v postgres-data:/var/lib/postgresql/data \ postgres:15 # Data survives container removal docker stop postgres docker rm postgres # New container with same volume retains data docker run -d \ --name postgres-new \ -e POSTGRES_PASSWORD=secret \ -v postgres-data:/var/lib/postgresql/data \ postgres:15
Pattern 3: Development Environment
# Bind mount code, volume for dependencies docker run -it \ --name dev \ -v $(pwd):/app \ -v node_modules:/app/node_modules \ -p 3000:3000 \ node:18 /bin/bash
The node_modules volume ensures dependencies persist while code is mounted.
Pattern 4: Log Aggregation
# Create volume for logs docker volume create app-logs # Application container writes logs docker run -d --name app -v app-logs:/var/log/app myapp # Log processor reads logs docker run -d --name log-processor -v app-logs:/logs log-processor
Part 6: Permissions and Ownership
The Permission Problem
When you use bind mounts, files keep their host ownership. The container user may not have permission to read or write these files.
Solution 1: Match User IDs
Find the container user ID:
docker run --rm alpine id # uid=0(root) gid=0(root)
Run container with same UID as host user:
docker run -u $(id -u):$(id -g) -v $(pwd):/app alpine touch /app/test.txt
Solution 2: Change Ownership in Container
docker run -v $(pwd):/app alpine sh -c "chown -R node:node /app && npm start"
Solution 3: Use Dockerfile with User
FROM node:18 WORKDIR /app RUN chown -R node:node /app USER node
Solution 4: Use Volumes (No Permission Issues)
Volumes automatically handle permissions correctly. This is another reason to prefer volumes in production.
Part 7: Performance Considerations
Volume Performance
Volumes on Linux use the native filesystem. Performance is excellent—comparable to writing directly to disk.
Bind Mount Performance on Linux
Bind mounts on Linux have good performance. There is minimal overhead compared to volumes.
Bind Mount Performance on Mac/Windows
On Docker Desktop (Mac/Windows), bind mounts go through a virtual machine. Performance can be significantly slower than volumes. For development, this is usually acceptable. For production on Mac/Windows, use volumes.
Optimizing Bind Mount Performance
Use :cached or :delegated flags (Docker Desktop)
# For read-heavy workloads docker run -v $(pwd):/app:cached node:18 # For write-heavy workloads docker run -v $(pwd):/app:delegated node:18
Use volumes for dependencies
# Bind mount code, volume for node_modules docker run -v $(pwd):/app -v node_modules:/app/node_modules node:18
Part 8: Real-World Scenarios
Scenario 1: Production PostgreSQL Database
A production PostgreSQL database needs reliable, persistent storage.
# Create volume docker volume create postgres-prod # Run PostgreSQL docker run -d \ --name postgres \ -e POSTGRES_PASSWORD=secure_password \ -e POSTGRES_DB=myapp \ -v postgres-prod:/var/lib/postgresql/data \ --restart unless-stopped \ postgres:15 # Backup daily docker run --rm \ -v postgres-prod:/data \ -v $(pwd)/backups:/backup \ alpine tar czf /backup/postgres-$(date +%Y%m%d).tar.gz -C /data .
Scenario 2: Web Application Development
A developer needs to work on a Node.js application with hot reload.
# Create volume for dependencies docker volume create app-node-modules # Run development container docker run -it \ --name dev \ -v $(pwd):/app \ -v app-node-modules:/app/node_modules \ -p 3000:3000 \ -p 9229:9229 \ node:18 \ npm run dev
Scenario 3: Nginx with Custom Configuration
A web server needs a custom configuration file and SSL certificates.
# Mount config file (read-only) # Mount SSL certificates docker run -d \ --name nginx \ -v /host/nginx.conf:/etc/nginx/nginx.conf:ro \ -v /host/certs:/etc/nginx/certs:ro \ -p 80:80 \ -p 443:443 \ nginx
Scenario 4: Multi-Container Application with Shared Uploads
A web application allows user uploads. The web containers need to share uploaded files.
# Create volume for uploads docker volume create uploads # Run web containers with shared volume docker run -d --name web1 -v uploads:/app/uploads -p 8080:80 mywebapp docker run -d --name web2 -v uploads:/app/uploads -p 8081:80 mywebapp # Run background processor that reads uploads docker run -d --name processor -v uploads:/app/uploads myprocessor
Part 9: Troubleshooting
Container Can't Write to Bind Mount
Problem: Permission denied when writing to bind mount.
Check permissions:
# Check host file permissions ls -la $(pwd) # Check container user docker run --rm alpine id
Fix:
# Run with same user docker run -u $(id -u):$(id -g) -v $(pwd):/app alpine touch /app/test.txt # Or change permissions in container docker run -v $(pwd):/app alpine sh -c "chown -R 1000:1000 /app && touch /app/test.txt"
Volume Won't Delete
Problem: Volume cannot be removed because it's in use.
# Find containers using volume docker ps -a --filter volume=mydata # Remove containers docker rm container-id # Remove volume docker volume rm mydata
Bind Mount Performance Slow
Problem: File operations in bind mount are slow (Docker Desktop).
Solutions:
Use volumes instead of bind mounts
Add
:cachedor:delegatedflagsUse Docker Desktop settings to improve performance
Consider moving to Linux for production
Lost Data After Container Removal
Problem: Container data disappeared after removal.
Check if volume was used:
# List volumes docker volume ls # Check if container had volume mount docker inspect container | jq '.[0].Mounts'
Prevention: Always use volumes for persistent data.
Summary
| Storage Type | Best For | Key Characteristics |
|---|---|---|
| Volumes | Production data, databases | Docker-managed, portable, better performance |
| Bind Mounts | Development, config files | Host-managed, direct access, convenient |
Volumes are the recommended choice for production persistent data. They are managed by Docker, portable across platforms, and provide better performance.
Bind mounts are excellent for development and for mounting specific configuration files. They give you direct access to files on the host.
Practice Questions
What is the difference between a volume and a bind mount?
Why are volumes preferred for production databases?
How do you create a named volume and mount it to a container?
A developer needs to edit code on their laptop and see changes immediately in a container. Which storage type should they use?
How do you backup a volume to a tar file?
Learn More
Practice Docker storage with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/
Comments
Post a Comment