01Introduction
Containers are ephemeral—when they're removed, their data is gone. For databases, config files, and user uploads, you need persistent storage. Docker offers two main options: bind mounts and named volumes. Each serves different use cases.
02Bind Mounts: Direct Host Access
Bind mounts map a specific path on your host to a path in the container. You control exactly where data lives on the filesystem. Great for config files and development, but comes with permission complexities.
1services: 2 nginx: 3 image: nginx:alpine4 volumes: 5 # Bind mount: host path -> container path6 - ./nginx.conf:/etc/nginx/nginx.conf:ro7 - ./html:/usr/share/nginx/html8 - /data/logs/nginx:/var/log/nginx910 app: 11 image: node:2012 volumes: 13 # Development: mount source code14 - ./src:/app/src15 - ./package.json:/app/package.jsonUse :ro (read-only) for config files that containers should never modify.
03Named Volumes: Docker-Managed Storage
Named volumes are managed by Docker. They're stored in Docker's data directory (usually /var/lib/docker/volumes/). Docker handles permissions automatically, making them ideal for databases and application data.
1services: 2 postgres: 3 image: postgres:154 volumes: 5 # Named volume: Docker manages the location6 - postgres_data:/var/lib/postgresql/data78 redis: 9 image: redis:alpine10 volumes: 11 - redis_data:/data1213volumes: 14 postgres_data: # Docker creates and manages this15 redis_data: 1617# Data lives in /var/lib/docker/volumes/projectname_postgres_data/_dataNamed volumes survive docker compose down. Only docker volume rm or docker compose down -v removes them.
04When to Use Each
**Use Bind Mounts for:**
- Configuration files you edit on the host
- Development source code
- Log files you want to access easily
- Sharing files between host and container
**Use Named Volumes for:**
- Database data (PostgreSQL, MySQL, MongoDB)
- Application data that containers manage
- Data that should survive container recreation
- When you don't care about the exact host path
05The Permissions Problem
Bind mounts inherit host filesystem permissions. If the container runs as a different user than your host user, you'll hit permission errors. Named volumes avoid this because Docker sets permissions correctly.
1# Common permission fix for bind mounts2# Find the UID/GID the container uses3docker exec container_name id45# Option 1: Change host directory ownership6sudo chown -R 1000:1000 ./data78# Option 2: Set PUID/PGID (linuxserver.io images)9services:10 app:11 image: linuxserver/sonarr12 environment:13 - PUID=100014 - PGID=100015 volumes:16 - ./config:/configDon't use chmod 777 as a fix. It's a security risk. Properly set ownership instead.
06Backup Considerations
Your volume choice affects backup strategies. Bind mounts are easy—just backup the host directory. Named volumes require either Docker commands or knowing the Docker storage path.
1# Backup a bind mount2tar -czf backup.tar.gz /path/to/bind/mount34# Backup a named volume5docker run --rm \6 -v postgres_data:/source:ro \7 -v $(pwd):/backup \8 alpine tar -czf /backup/postgres_data.tar.gz -C /source .910# Or find the volume location11docker volume inspect postgres_data --format '{{ .Mountpoint }}'12# Then backup that path (requires root)07Best Practices
1. Use named volumes for database data—they're designed for it
2. Use bind mounts for configs you need to edit
3. Document which volumes contain critical data
4. Use volume labels to add metadata
5. Regularly backup volumes (covered in our backup guide)
1volumes: 2 # Add labels for documentation3 postgres_data: 4 labels: 5 com.example.description: "PostgreSQL database files"6 com.example.backup: "daily"78 # Use external volumes for truly persistent data9 critical_data: 10 external: true11 name: my_critical_data