Day 10: Managing Data with Volumes and Bind Mounts
Learning Objectives: By the end of this lesson, you will be able to:
- Understand different types of Docker data storage: volumes, bind mounts, and tmpfs
- Create and manage Docker volumes for persistent data storage
- Use bind mounts for development workflows and file sharing
- Apply best practices for data management in containerized applications
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Imagine you’ve built an amazing web application in a Docker container, and users start creating accounts and uploading files. Then you restart the container and… everything is gone! This data disappearance nightmare is exactly what we’ll solve today by learning Docker’s data persistence mechanisms.
Lesson Overview
- Duration: 50-60 minutes
- Prerequisites: Completion of Days 8-9, understanding of Docker containers and images
- Materials Needed: Docker installed, text editor, sample database or application
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Container Data Challenges - Why containers lose data and how to solve it
- Docker Storage Types - Volumes, bind mounts, and tmpfs mounts
- Volume Management - Creating, using, and managing Docker volumes
- Bind Mount Workflows - Development patterns and file sharing
- Best Practices - Security, performance, and backup strategies
Success Criteria:
By the end of this session, you should be able to set up persistent data storage for any containerized application and choose the right storage approach for different use cases.
3. Foundation Knowledge (15-20 minutes)
The Container Data Problem
Containers are designed to be ephemeral - they can be stopped, started, and destroyed at any time. By default, any data created inside a container disappears when the container is removed.
Real-World Analogy: Think of containers like hotel rooms. When you check out, the room is cleaned and reset for the next guest. If you want to keep your belongings, you need to store them somewhere permanent (like a storage unit) rather than leaving them in the room.
Why This Matters:
- Data Persistence: Applications need to survive container restarts
- Data Sharing: Multiple containers might need access to the same data
- Development Workflow: Developers need to edit code that affects running containers
- Backup and Recovery: Data must be preservable and transferable
Docker Storage Types
Docker provides three main ways to persist data:
1. Volumes (Recommended for Production)
- Managed entirely by Docker
- Stored in Docker’s area of the host filesystem
- Can be shared between containers
- Persist even when containers are removed
- Can be backed up and restored easily
2. Bind Mounts
- Mount a host filesystem path into the container
- Give full access to the host filesystem
- Useful for development and specific host integration needs
- Performance characteristics depend on host filesystem
3. tmpfs Mounts (Memory Storage)
- Store data in the host’s memory
- Never written to disk
- Fast but volatile
- Useful for temporary data and security-sensitive information
Key Terminology
- Volume: A Docker-managed storage location independent of container lifecycle
- Bind Mount: A direct mapping between host filesystem path and container path
- Mount Point: The directory inside the container where external storage is accessed
- Named Volume: A volume with a specific name that can be referenced by multiple containers
- Anonymous Volume: A volume without a name, managed by Docker
4. Detailed Explanation and Examples (25-30 minutes)
Working with Docker Volumes
Creating and Managing Volumes
# Create a named volume
docker volume create my-app-data
# List all volumes
docker volume ls
# Inspect volume details
docker volume inspect my-app-data
# Remove a volume
docker volume rm my-app-data
# Remove all unused volumes
docker volume prune
Using Volumes in Containers
# Run container with named volume
docker run -d \
--name web-app \
-v my-app-data:/app/data \
nginx
# Run container with anonymous volume
docker run -d \
--name temp-app \
-v /app/temp \
nginx
# Modern syntax with --mount
docker run -d \
--name web-app \
--mount source=my-app-data,target=/app/data \
nginx
Working with Bind Mounts
Development Workflow Example
# Bind mount current directory for live code editing
docker run -d \
--name dev-server \
-p 3000:3000 \
-v $(pwd):/app \
-w /app \
node:18-alpine \
npm start
# Bind mount specific directories
docker run -d \
--name config-app \
-v /host/config:/app/config:ro \
-v /host/logs:/app/logs \
myapp
Modern Mount Syntax
# Bind mount with --mount (more explicit)
docker run -d \
--mount type=bind,source=/host/data,target=/app/data,readonly \
--name readonly-app \
nginx
# Multiple mounts
docker run -d \
--mount type=volume,source=app-data,target=/data \
--mount type=bind,source=/host/config,target=/config \
--mount type=tmpfs,target=/tmp \
myapp
Volume Drivers and Advanced Features
Volume Drivers
# Use different volume drivers
docker volume create \
--driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/path/to/share \
nfs-volume
Volume Labels and Metadata
# Create volume with labels
docker volume create \
--label project=webapp \
--label environment=production \
webapp-prod-data
Common Use Cases and Patterns
Database Data Persistence
# Dockerfile for persistent database
FROM postgres:13
ENV POSTGRES_DB=myapp
ENV POSTGRES_USER=appuser
ENV POSTGRES_PASSWORD=secret
VOLUME ["/var/lib/postgresql/data"]
# Run with persistent data
docker run -d \
--name postgres-db \
-e POSTGRES_PASSWORD=secret \
-v postgres-data:/var/lib/postgresql/data \
postgres:13
Configuration Management
# Mount configuration files
docker run -d \
--name web-server \
-v /host/nginx.conf:/etc/nginx/nginx.conf:ro \
-v web-content:/usr/share/nginx/html \
nginx
Log Management
# Mount logs directory for external processing
docker run -d \
--name app-server \
-v app-logs:/var/log/app \
-v /host/log-processor:/log-processor \
myapp
Common Misconceptions
-
Misconception: “Volumes make containers heavier and slower” Reality: Volumes can actually improve performance for data-intensive operations
-
Misconception: “Bind mounts are always faster than volumes” Reality: Performance depends on the specific use case and host filesystem
-
Misconception: “Data in volumes is automatically backed up” Reality: You must implement your own backup strategy for volume data
5. Live Coding Demonstration (20-25 minutes)
Example 1: Web Application with Persistent Data
# Create a volume for application data
docker volume create webapp-data
# Run a web application with persistent storage
docker run -d \
--name persistent-webapp \
-p 8080:80 \
-v webapp-data:/usr/share/nginx/html \
nginx
# Add some content to demonstrate persistence
docker exec persistent-webapp \
sh -c 'echo "<h1>Persistent Data!</h1>" > /usr/share/nginx/html/index.html'
# Visit http://localhost:8080 to see content
# Stop and remove container
docker stop persistent-webapp
docker rm persistent-webapp
# Start a new container with the same volume
docker run -d \
--name new-webapp \
-p 8080:80 \
-v webapp-data:/usr/share/nginx/html \
nginx
# Content is still there!
Explanation:
- We created a named volume
webapp-data - The content persists even when the container is removed
- New containers can access the same data by mounting the same volume
Example 2: Development Environment with Live Reload
Create app.js:
const express = require('express');
const fs = require('fs');
const app = express();
app.get('/', (req, res) => {
const config = JSON.parse(fs.readFileSync('/app/config/config.json'));
res.json({
message: 'Hello from Docker!',
version: config.version,
environment: config.environment
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Create config/config.json:
{
"version": "1.0.0",
"environment": "development"
}
Create package.json:
{
"name": "volume-demo",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
# Run with bind mount for live development
docker run -d \
--name dev-app \
-p 3000:3000 \
-v $(pwd):/app \
-v /app/node_modules \
-w /app \
node:18-alpine \
sh -c "npm install && npm start"
# Now edit config.json and see changes reflected immediately
Key Points:
- Source code changes are immediately available in the container
- We use a volume for node_modules to prevent host conflicts
- This setup enables hot-reload development workflows
Example 3: Multi-Container Data Sharing
# Create shared volume
docker volume create shared-data
# Producer container - writes data
docker run -d \
--name data-producer \
-v shared-data:/data \
alpine \
sh -c 'while true; do echo "$(date): Hello from producer" >> /data/log.txt; sleep 5; done'
# Consumer container - reads data
docker run -d \
--name data-consumer \
-v shared-data:/shared:ro \
alpine \
sh -c 'while true; do echo "Latest entries:"; tail -n 3 /shared/log.txt; sleep 10; done'
# Monitor both containers
docker logs -f data-consumer
Debugging and Inspection
# Inspect what's in a volume
docker run --rm \
-v webapp-data:/data \
alpine \
ls -la /data
# Copy data from volume to host
docker run --rm \
-v webapp-data:/data \
-v $(pwd):/backup \
alpine \
cp -r /data/* /backup/
# Backup a volume
docker run --rm \
-v webapp-data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/webapp-backup.tar.gz -C /data .
6. Hands-On Practice (30-40 minutes)
Exercise 1: Database Persistence (15 minutes)
Objective: Set up a PostgreSQL database with persistent data storage
Instructions:
- Create a named volume for PostgreSQL data
- Run a PostgreSQL container with the volume mounted
- Connect to the database and create a table with some data
- Stop and remove the container
- Start a new container with the same volume and verify data persistence
Starter Commands:
# Create volume
docker volume create postgres-data
# Run PostgreSQL (fill in the volume mount)
docker run -d \
--name postgres-db \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=testdb \
# Add volume mount here
postgres:13
# Connect to database
docker exec -it postgres-db psql -U postgres -d testdb
SQL to test persistence:
CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(100));
INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Charlie');
SELECT * FROM users;
Exercise 2: Development Environment Setup (15 minutes)
Objective: Create a live-reload development environment for a Python Flask application
Requirements:
- Use bind mounts to enable live code editing
- Mount a separate volume for Python packages
- Ensure changes to Python files are reflected immediately
Create app.py:
from flask import Flask, jsonify
import json
import os
app = Flask(__name__)
@app.route('/')
def hello():
config_path = '/app/config/settings.json'
if os.path.exists(config_path):
with open(config_path) as f:
config = json.load(f)
else:
config = {'message': 'No config found'}
return jsonify({
'app': 'Flask Docker Demo',
'config': config
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Create requirements.txt:
Flask==2.3.0
Create config/settings.json:
{
"message": "Hello from mounted config!",
"debug": true
}
Task: Write the docker run command to enable live development with proper volume mounts.
Exercise 3: Log Aggregation System (10 minutes)
Objective: Create a system where multiple containers write logs to a shared volume
Challenge:
- Create three containers that write different types of logs to a shared volume
- Create a fourth container that monitors and aggregates these logs
- Use appropriate read-only and read-write permissions
Container Ideas:
- Web server writing access logs
- Database writing query logs
- Application writing error logs
- Log aggregator reading all logs
Hints:
- Use Alpine containers with simple shell scripts for log generation
- Consider using different subdirectories for different log types
- Make some mounts read-only for security
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
- Problem: Data disappears when container is removed Cause: Not using volumes or bind mounts for persistent data Solution: Always mount volumes for data that should persist
# ❌ Data will be lost
docker run -d postgres:13
# ✅ Data persists
docker run -d -v postgres-data:/var/lib/postgresql/data postgres:13
- Problem: Permission denied errors with bind mounts Cause: User ID mismatches between host and container Solution: Use proper user mapping or fix permissions
# Fix permissions before mounting
sudo chown -R 1000:1000 /host/data
# Or run container with matching user ID
docker run -d \
--user $(id -u):$(id -g) \
-v /host/data:/app/data \
myapp
- Problem: Performance issues with bind mounts on macOS/Windows Cause: File system translation overhead Solution: Use volumes for performance-critical data, bind mounts only for development
# ❌ Slow on macOS/Windows
docker run -v /host/large-dataset:/data myapp
# ✅ Better performance
docker volume create dataset-volume
docker run -v dataset-volume:/data myapp
- Problem: Accidentally mounting over important container directories Cause: Mounting to system directories without understanding consequences Solution: Be careful with mount points, use read-only when appropriate
# ❌ Dangerous - might break container
docker run -v /host:/etc nginx
# ✅ Safe - specific config file
docker run -v /host/nginx.conf:/etc/nginx/nginx.conf:ro nginx
Debugging Tips
# Check what volumes are mounted
docker inspect <container-name> | grep -A 10 "Mounts"
# See volume usage
docker system df -v
# Find containers using a volume
docker ps -a --filter volume=<volume-name>
# Check volume driver and options
docker volume inspect <volume-name>
Backup and Recovery
# Backup volume to tar file
docker run --rm \
-v volume-name:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/volume-backup.tar.gz -C /data .
# Restore volume from tar file
docker run --rm \
-v volume-name:/data \
-v $(pwd):/backup \
alpine \
tar xzf /backup/volume-backup.tar.gz -C /data
8. Best Practices and Professional Tips (10 minutes)
Industry Standards
Volume Management Strategy
# Use descriptive volume names
docker volume create myapp-prod-database
docker volume create myapp-prod-uploads
docker volume create myapp-prod-logs
# Label volumes for better organization
docker volume create \
--label environment=production \
--label component=database \
--label project=myapp \
myapp-prod-db
Security Best Practices
# Use read-only mounts when possible
docker run \
-v config-data:/app/config:ro \
-v logs-data:/app/logs \
myapp
# Avoid mounting sensitive host directories
# ❌ Never do this
docker run -v /:/host-root myapp
# ✅ Mount only what's needed
docker run -v /host/app-config:/app/config:ro myapp
Development vs Production Patterns
Development:
# Development: Use bind mounts for live editing
docker run \
-v $(pwd)/src:/app/src \
-v $(pwd)/config:/app/config \
myapp
Production:
# Production: Use volumes for better performance and security
docker run \
-v prod-app-data:/app/data \
-v prod-app-logs:/app/logs \
myapp
Dockerfile Volume Declarations
# Declare volumes in Dockerfile for documentation
FROM nginx:alpine
# Create volume mount points
VOLUME ["/var/log/nginx"]
VOLUME ["/etc/nginx/conf.d"]
# This documents where external storage should be mounted
Monitoring and Maintenance
# Regular cleanup of unused volumes
docker volume prune
# Monitor volume disk usage
docker system df -v
# Backup critical volumes regularly
# (Implement in CI/CD or cron jobs)
Next Steps in Your Learning Journey
- Learn about volume drivers for network storage
- Explore container orchestration storage (Kubernetes persistent volumes)
- Study backup and disaster recovery strategies
- Practice with different database persistence patterns
9. Summary and Review (5 minutes)
Key Takeaways
- Use volumes for production data - They’re managed by Docker and more portable than bind mounts
- Bind mounts are great for development - Enable live code editing and configuration management
- Always plan for data persistence - Don’t lose important data when containers restart
Quick Review Questions
- What’s the difference between a volume and a bind mount?
- When would you use a tmpfs mount?
- How do you backup data from a Docker volume?
Answers:
- Volumes are managed by Docker in its own storage area; bind mounts directly map host filesystem paths
- For temporary data that should be fast (memory-stored) and secure (never written to disk)
- Use a temporary container to tar the volume contents to a backup location
For Instructors
Teaching Tips
- Use concrete examples with data that students can see persist
- Demonstrate both the problem (data loss) and the solution (volumes)
- Show performance differences between different storage types
- Emphasize security implications of different mounting strategies
Assessment Ideas
- Data persistence verification exercises
- Performance comparison assignments
- Security audit of volume mounting strategies
- Real-world backup and recovery scenarios
Adaptation Notes
- For beginners: Focus on basic volume usage and common patterns
- For advanced students: Explore volume drivers and network storage
- Online delivery: Use screen sharing to show file system changes
- Time management: Hands-on exercises are crucial - allocate adequate time for practice