Docker Mount Complete Guide: Volumes, Bind Mounts, and tmpfs Comparison

Introduction

When working with Docker containers, understanding how to properly manage data persistence is crucial. Docker provides three main approaches for mounting data into containers: Volumes, Bind Mounts, and tmpfs mounts. Each has distinct characteristics, use cases, and trade-offs.

This comprehensive guide explores all Docker mount types, their differences, practical examples, and best practices to help you choose the right approach for your specific needs.

What is Mounting in Docker?

Mounting in Docker refers to the process of making host filesystem directories or Docker-managed storage available inside containers. This allows containers to:

  • Access host files and directories
  • Persist data beyond container lifecycle
  • Share data between multiple containers
  • Exchange configuration files, logs, and application code
graph TD
    A[Docker Mount Types] --> B[Volumes]
    A --> C[Bind Mounts]
    A --> D[tmpfs Mounts]

    B --> B1[Docker-managed storage]
    B --> B2[/var/lib/docker/volumes]
    B --> B3[Production data persistence]

    C --> C1[Direct host path mapping]
    C --> C2[Development & code sync]
    C --> C3[Configuration files]

    D --> D1[In-memory storage]
    D --> D2[Temporary data]
    D --> D3[Sensitive information]

    style B fill:#4ecdc4
    style C fill:#feca57
    style D fill:#ff6b6b

Three Types of Docker Mounts

Quick Comparison Table

FeatureDocker VolumeBind Mounttmpfs Mount
ManagementDocker-managedUser-managedDocker-managed
Storage Location/var/lib/docker/volumesAnywhere on hostMemory (RAM)
PortabilityHighLowN/A
PerformanceGoodGoodExcellent
PersistenceYesYesNo (volatile)
Best ForProduction dataDevelopmentTemporary data
Can Share Between ContainersYesYesNo
Backup/MigrationEasyManualN/A
Host Path DependencyNoYesNo

What Are Docker Volumes?

Docker Volumes are the preferred mechanism for persisting data. They are completely managed by Docker and stored in a dedicated location on the host.

graph LR
    subgraph "Host System"
        V[/var/lib/docker/volumes]
        V1[my-vol/]
        V2[db-data/]
        V --> V1
        V --> V2
    end

    subgraph "Container 1"
        C1[/app/data]
    end

    subgraph "Container 2"
        C2[/var/lib/mysql]
    end

    V1 --> C1
    V2 --> C2

    style V fill:#4ecdc4
    style V1 fill:#a8e6cf
    style V2 fill:#a8e6cf

Characteristics

  • Managed by Docker: Docker handles all lifecycle operations
  • Named or Anonymous: Can be explicitly named or auto-generated
  • Shared Access: Multiple containers can mount the same volume
  • Volume Drivers: Support for cloud storage, network storage, etc.
  • Isolated from Host: No direct dependency on host directory structure

Basic Volume Commands

 1# Create a named volume
 2docker volume create my-volume
 3
 4# List all volumes
 5docker volume ls
 6
 7# Inspect volume details
 8docker volume inspect my-volume
 9
10# Remove a volume
11docker volume rm my-volume
12
13# Remove all unused volumes
14docker volume prune

Using Volumes in Containers

1. Short Syntax (-v flag)

 1# Named volume
 2docker run -v my-volume:/app/data nginx:latest
 3
 4# Anonymous volume (auto-generated name)
 5docker run -v /app/data nginx:latest
 6
 7# Multiple volumes
 8docker run \
 9  -v db-data:/var/lib/mysql \
10  -v config-data:/etc/mysql/conf.d \
11  mysql:8.0

2. Long Syntax (–mount flag) - Recommended

1docker run \
2  --mount type=volume,source=my-volume,target=/app/data \
3  nginx:latest
4
5# With additional options
6docker run \
7  --mount type=volume,source=my-volume,target=/app/data,readonly \
8  nginx:latest

Docker Compose Example

 1services:
 2  app:
 3    image: node:18
 4    volumes:
 5      # Named volume
 6      - app-data:/app/data
 7      - app-logs:/app/logs
 8    environment:
 9      - NODE_ENV=production
10
11  database:
12    image: postgres:15
13    volumes:
14      # Named volume for database persistence
15      - postgres-data:/var/lib/postgresql/data
16      # Named volume for initialization scripts
17      - postgres-init:/docker-entrypoint-initdb.d
18    environment:
19      - POSTGRES_PASSWORD=secret
20
21# Volume definitions
22volumes:
23  app-data:
24    driver: local
25  app-logs:
26    driver: local
27  postgres-data:
28    driver: local
29  postgres-init:
30    driver: local

Advanced Volume Features

1. Volume Drivers

 1# Use NFS volume driver
 2docker volume create \
 3  --driver local \
 4  --opt type=nfs \
 5  --opt o=addr=192.168.1.100,rw \
 6  --opt device=:/path/to/dir \
 7  nfs-volume
 8
 9# Use AWS EFS driver (requires plugin)
10docker volume create \
11  --driver rexray/efs \
12  --name efs-volume
13
14# Azure File Storage
15docker volume create \
16  --driver azure_file \
17  --name azure-volume \
18  -o share=myshare

2. Volume with Specific Permissions

1# Create volume with specific user/group
2docker volume create \
3  --opt type=tmpfs \
4  --opt device=tmpfs \
5  --opt o=uid=1000,gid=1000 \
6  my-volume

3. Read-Only Volumes

1# Mount volume as read-only
2docker run \
3  -v my-volume:/app/data:ro \
4  nginx:latest
5
6# Or with --mount
7docker run \
8  --mount type=volume,source=my-volume,target=/app/data,readonly \
9  nginx:latest

Pros and Cons

Pros

  • Easy to backup and migrate: Can be backed up using docker cp or volume plugins
  • Cross-platform: Works on Linux, Windows, and macOS
  • Managed lifecycle: Docker handles creation and cleanup
  • Better performance on Docker Desktop: Optimized for macOS and Windows
  • Volume drivers: Can store volumes on remote hosts or cloud providers
  • Safe sharing: Multiple containers can safely share volumes
  • Pre-populated content: New volumes can be pre-filled from container content

Cons

  • Less direct access: Cannot directly edit files from host without container
  • Abstracted location: Files stored in Docker-managed directory
  • Requires Docker commands: Need Docker CLI to manage volumes

Use Cases

  1. Database Storage

    • PostgreSQL, MySQL, MongoDB data directories
    • Ensures data survives container recreation
  2. Application State

    • User uploads
    • Application-generated files
    • Cache directories
  3. Shared Configuration

    • Shared config between microservices
    • Centralized logging
  4. Backup and Restore Scenarios

     1# Backup volume
     2docker run --rm \
     3  -v my-volume:/data \
     4  -v $(pwd):/backup \
     5  busybox tar czf /backup/backup.tar.gz /data
     6
     7# Restore volume
     8docker run --rm \
     9  -v my-volume:/data \
    10  -v $(pwd):/backup \
    11  busybox tar xzf /backup/backup.tar.gz -C /
    

2. Bind Mounts (Best for Development)

What Are Bind Mounts?

Bind Mounts directly map a host directory or file to a container path. The host path must exist before creating the container.

graph LR
    subgraph "Host System"
        H1[/home/user/project]
        H2[/home/user/logs]
        H3[/etc/config]
    end

    subgraph "Container"
        C1[/app]
        C2[/app/logs]
        C3[/etc/app/config]
    end

    H1 -->|Bind Mount| C1
    H2 -->|Bind Mount| C2
    H3 -->|Bind Mount| C3

    style H1 fill:#feca57
    style H2 fill:#feca57
    style H3 fill:#feca57

Characteristics

  • Direct Host Mapping: Container accesses files directly from host
  • Path Dependency: Requires specific host directory structure
  • Two-Way Sync: Changes reflect immediately in both directions
  • No Docker Management: Docker doesn’t manage the host directory
  • Full Host Access: Container has same permissions as mounted directory

Using Bind Mounts

1. Short Syntax (-v flag)

 1# Basic bind mount
 2docker run -v /host/path:/container/path nginx:latest
 3
 4# With read-only flag
 5docker run -v /host/path:/container/path:ro nginx:latest
 6
 7# Current directory
 8docker run -v $(pwd):/app node:18
 9
10# Multiple bind mounts
11docker run \
12  -v $(pwd)/src:/app/src \
13  -v $(pwd)/logs:/app/logs \
14  -v $(pwd)/config.json:/app/config.json \
15  node:18

2. Long Syntax (–mount flag) - More Explicit

 1# Basic bind mount
 2docker run \
 3  --mount type=bind,source=/host/path,target=/container/path \
 4  nginx:latest
 5
 6# Read-only bind mount
 7docker run \
 8  --mount type=bind,source=/host/path,target=/container/path,readonly \
 9  nginx:latest
10
11# With consistency options (macOS/Windows)
12docker run \
13  --mount type=bind,source=$(pwd),target=/app,consistency=cached \
14  node:18

Docker Compose Example

 1services:
 2  web:
 3    image: nginx:latest
 4    ports:
 5      - "8080:80"
 6    volumes:
 7      # Bind mount for development - hot reload
 8      - ./src:/usr/share/nginx/html
 9      - ./nginx.conf:/etc/nginx/nginx.conf:ro
10      - ./logs:/var/log/nginx
11    environment:
12      - ENVIRONMENT=development
13
14  app:
15    image: node:18
16    working_dir: /app
17    volumes:
18      # Sync entire project directory
19      - ./app:/app
20      # But exclude node_modules (use anonymous volume)
21      - /app/node_modules
22      # Mount specific config file
23      - ./app/config/development.json:/app/config/config.json
24    command: npm run dev
25    ports:
26      - "3000:3000"

Development Workflow Example

 1# Complete development setup with hot reload
 2
 3# 1. Project structure
 4# project/
 5# ├── src/
 6# │   ├── index.js
 7# │   └── utils/
 8# ├── tests/
 9# ├── config/
10# └── docker-compose.yml
11
12# 2. Docker Compose configuration
13services:
14  dev:
15    image: node:18
16    working_dir: /app
17    volumes:
18      # Mount source code for hot reload
19      - ./src:/app/src
20      - ./tests:/app/tests
21      - ./package.json:/app/package.json
22      # Use anonymous volume for node_modules
23      - /app/node_modules
24    command: npm run dev
25    ports:
26      - "3000:3000"
27    environment:
28      - NODE_ENV=development
29      - CHOKIDAR_USEPOLLING=true  # For file watching
30
31# 3. Start development
32docker compose up
33
34# Now changes to ./src/* are immediately reflected in container!

Advanced Bind Mount Features

1. Consistency Modes (macOS/Windows)

Note (2026): Current Docker Desktop uses VirtioFS file sharing by default and ignores the cached / delegated / consistent flags; they are accepted only for backward compatibility. For faster bind mounts on macOS/Windows, use VirtioFS (default), Docker Desktop’s synchronized file shares, or keep heavy directories such as node_modules in a named volume.

 1# Cached: prioritize container performance
 2docker run \
 3  --mount type=bind,source=$(pwd),target=/app,consistency=cached \
 4  node:18
 5
 6# Delegated: prioritize host performance
 7docker run \
 8  --mount type=bind,source=$(pwd),target=/app,consistency=delegated \
 9  node:18
10
11# Consistent: perfect sync (default, slowest)
12docker run \
13  --mount type=bind,source=$(pwd),target=/app,consistency=consistent \
14  node:18

2. Bind Propagation

 1# Shared propagation (default)
 2docker run \
 3  --mount type=bind,source=/host/path,target=/container/path,bind-propagation=shared \
 4  ubuntu
 5
 6# Private propagation
 7docker run \
 8  --mount type=bind,source=/host/path,target=/container/path,bind-propagation=private \
 9  ubuntu
10
11# Slave propagation
12docker run \
13  --mount type=bind,source=/host/path,target=/container/path,bind-propagation=slave \
14  ubuntu

3. SELinux Labels (Linux)

1# With SELinux label
2docker run -v /host/path:/container/path:z nginx:latest  # Private label
3docker run -v /host/path:/container/path:Z nginx:latest  # Shared label

Pros and Cons

Pros

  • Direct file editing: Edit files on host with any editor
  • Real-time sync: Changes immediately reflected in container
  • No Docker commands needed: Use normal file operations
  • Perfect for development: Hot reload, live debugging
  • Share configuration: Easy to share config files
  • Specific file mounting: Can mount individual files

Cons

  • Host path dependency: Requires specific directory structure
  • Less portable: Paths differ across environments
  • Security concerns: Container can modify host files
  • Permission issues: User ID mismatches can cause problems
  • Performance on macOS/Windows: Slower than volumes
  • Backup complexity: Must backup host directory separately

Use Cases

  1. Development Environment

    1# Hot reload for web development
    2docker run \
    3  -v $(pwd)/src:/app/src \
    4  -v $(pwd)/public:/app/public \
    5  -p 3000:3000 \
    6  node:18 npm run dev
    
  2. Configuration Files

    1# Mount specific config files
    2docker run \
    3  -v /etc/myapp/config.yml:/app/config.yml:ro \
    4  myapp:latest
    
  3. Log Collection

    1# Collect logs to host directory
    2docker run \
    3  -v $(pwd)/logs:/var/log/app \
    4  myapp:latest
    
  4. Testing and CI/CD

    1# Run tests on current codebase
    2docker run \
    3  -v $(pwd):/app \
    4  -w /app \
    5  node:18 npm test
    
  5. Database Configuration

    1# Custom PostgreSQL config
    2docker run \
    3  -v $(pwd)/postgresql.conf:/etc/postgresql/postgresql.conf:ro \
    4  -v postgres-data:/var/lib/postgresql/data \
    5  postgres:15
    

3. tmpfs Mounts (For Temporary Data)

What Are tmpfs Mounts?

tmpfs mounts store data in the host’s memory (RAM). Data exists only while the container runs and is never written to disk.

graph TD
    subgraph "Host System"
        RAM[System RAM]
        RAM --> TM1[tmpfs Mount 1]
        RAM --> TM2[tmpfs Mount 2]
    end

    subgraph "Container"
        C1[/tmp]
        C2[/run]
    end

    TM1 -->|In-Memory| C1
    TM2 -->|In-Memory| C2

    N[Container Stops] -->|Data Lost| X[❌ Data Deleted]

    style RAM fill:#ff6b6b
    style TM1 fill:#ffb3ba
    style TM2 fill:#ffb3ba
    style X fill:#ff6b6b

Characteristics

  • Memory Storage: Data stored in RAM, not disk
  • Volatile: Data lost when container stops
  • Fast Performance: No disk I/O overhead
  • Secure: No data leakage to disk
  • Linux Only: Not available on Windows containers
  • Size Limited: Limited by available memory

Using tmpfs Mounts

1. Short Syntax (–tmpfs flag)

 1# Basic tmpfs mount
 2docker run --tmpfs /app/tmp nginx:latest
 3
 4# With size limit (100MB)
 5docker run --tmpfs /app/tmp:size=100m nginx:latest
 6
 7# Multiple tmpfs mounts
 8docker run \
 9  --tmpfs /tmp \
10  --tmpfs /run:size=64m \
11  ubuntu:latest

2. Long Syntax (–mount flag)

1# Basic tmpfs
2docker run \
3  --mount type=tmpfs,target=/app/tmp \
4  nginx:latest
5
6# With options
7docker run \
8  --mount type=tmpfs,target=/app/tmp,tmpfs-size=100m,tmpfs-mode=1770 \
9  nginx:latest

Docker Compose Example

 1services:
 2  app:
 3    image: myapp:latest
 4    tmpfs:
 5      # Simple tmpfs mount
 6      - /tmp
 7      - /run
 8
 9  web:
10    image: nginx:latest
11    volumes:
12      # Using long syntax for more control
13      - type: tmpfs
14        target: /app/cache
15        tmpfs:
16          size: 100m
17          mode: 1770
18      - type: tmpfs
19        target: /tmp
20        tmpfs:
21          size: 50m
22
23  database:
24    image: postgres:15
25    volumes:
26      # Persistent data on volume
27      - db-data:/var/lib/postgresql/data
28    tmpfs:
29      # Temporary files in memory
30      - /tmp
31      - /run/postgresql:size=100m
32
33volumes:
34  db-data:

Advanced tmpfs Options

 1# With all options
 2docker run \
 3  --mount type=tmpfs,target=/app/tmp,\
 4tmpfs-size=100m,\
 5tmpfs-mode=1777,\
 6tmpfs-uid=1000,\
 7tmpfs-gid=1000 \
 8  myapp:latest
 9
10# Options explained:
11# - tmpfs-size: Maximum size (100MB)
12# - tmpfs-mode: Unix permissions (1777 = sticky bit + rwx)
13# - tmpfs-uid: Owner user ID
14# - tmpfs-gid: Owner group ID

Pros and Cons

Pros

  • Excellent performance: In-memory operations are extremely fast
  • Secure: Sensitive data never written to disk
  • No disk wear: Reduces SSD/HDD wear for temporary files
  • Automatic cleanup: Data automatically removed when container stops
  • No persistence overhead: No need to manage cleanup

Cons

  • Volatile storage: All data lost when container stops
  • Linux only: Not supported on Windows containers
  • Memory limited: Uses system RAM (limited resource)
  • Cannot share: Cannot share tmpfs between containers
  • No backup possible: Data cannot be backed up
  • Memory pressure: Can affect system performance if overused

Use Cases

  1. Temporary Processing

    1# Image processing with temporary files
    2docker run \
    3  --tmpfs /tmp:size=1g \
    4  -v $(pwd)/input:/input:ro \
    5  -v $(pwd)/output:/output \
    6  image-processor
    
  2. Sensitive Data

    1# Handle sensitive credentials in memory
    2docker run \
    3  --tmpfs /secrets:size=10m,mode=0700 \
    4  -e SECRET_FILE=/secrets/token \
    5  myapp:latest
    
  3. Build Cache

    1# Fast build with in-memory cache
    2docker run \
    3  --tmpfs /tmp:size=2g \
    4  -v $(pwd):/app \
    5  -w /app \
    6  node:18 npm run build
    
  4. Session Storage

    1# Web server with in-memory sessions
    2docker run \
    3  --tmpfs /var/lib/nginx/sessions:size=200m \
    4  nginx:latest
    
  5. Testing Environment

    1# Fast test execution with tmpfs
    2docker run \
    3  --tmpfs /tmp:size=500m \
    4  --tmpfs /var/tmp:size=500m \
    5  -v $(pwd):/app \
    6  test-runner npm test
    

Complete Comparison Matrix

Feature Comparison

FeatureVolumeBind Mounttmpfs
Storage Location/var/lib/docker/volumesCustom host pathMemory (RAM)
Managed ByDockerHost/UserDocker
PersistenceYes (survives container)Yes (survives container)No (volatile)
PortabilityHighLowMedium
Performance (Linux)GoodGoodExcellent
Performance (macOS/Win)ExcellentModerateExcellent
Host AccessIndirectDirectNone
Sharing Between ContainersYesYesNo
Backup/RestoreEasyManualNot applicable
Size LimitHost diskHost diskRAM
Platform SupportAllAllLinux only
SELinux/AppArmorHandled by DockerManual configurationHandled by Docker
Initial ContentCan inheritUses existingEmpty

Performance Comparison

graph LR
    A[Performance by Platform] --> B[Linux Host]
    A --> C[macOS/Windows]

    B --> B1[Volume: ★★★★☆]
    B --> B2[Bind Mount: ★★★★☆]
    B --> B3[tmpfs: ★★★★★]

    C --> C1[Volume: ★★★★★]
    C --> C2[Bind Mount: ★★☆☆☆]
    C --> C3[tmpfs: ★★★★★]

    style B3 fill:#4ecdc4
    style C1 fill:#4ecdc4
    style C3 fill:#4ecdc4

Use Case Decision Tree

graph TD
    A[Choose Mount Type] --> B{Need Persistence?}
    B -->|No| C[tmpfs Mount]
    B -->|Yes| D{Production or Development?}

    D -->|Production| E[Docker Volume]
    D -->|Development| F{Need Direct File Access?}

    F -->|Yes| G[Bind Mount]
    F -->|No| H[Docker Volume]

    C --> I[Fast, Secure, Volatile]
    E --> J[Managed, Portable, Backupable]
    G --> K[Direct Access, Hot Reload]
    H --> L[Isolated, Performance]

    style E fill:#4ecdc4
    style G fill:#feca57
    style C fill:#ff6b6b

Best Practices

1. Volume Best Practices

 1# Good: Named volumes with clear purpose
 2services:
 3  db:
 4    image: postgres:15
 5    volumes:
 6      - postgres-data:/var/lib/postgresql/data
 7      - postgres-backup:/backup
 8
 9volumes:
10  postgres-data:
11    name: myapp_postgres_data
12  postgres-backup:
13    name: myapp_postgres_backup

Best Practices:

  • Use named volumes instead of anonymous volumes
  • Add meaningful volume names
  • Regular backup strategy
  • Use volume drivers for production
  • Document volume contents and purpose

2. Bind Mount Best Practices

 1# Good: Clear separation of concerns
 2services:
 3  dev:
 4    image: node:18
 5    volumes:
 6      # Source code - read/write
 7      - ./src:/app/src
 8      # Configuration - read-only
 9      - ./config/development.json:/app/config/config.json:ro
10      # Logs - write only
11      - ./logs:/app/logs
12      # Exclude node_modules
13      - /app/node_modules

Best Practices:

  • Use read-only (:ro) when possible
  • Exclude unnecessary directories
  • Use absolute paths or $(pwd)
  • Document required host directory structure
  • Be careful with permissions
  • Avoid bind mounts in production

3. tmpfs Best Practices

 1# Good: Size limits and appropriate use
 2services:
 3  app:
 4    image: myapp:latest
 5    tmpfs:
 6      # Limit size to prevent memory exhaustion
 7      - /tmp:size=100m,mode=1777
 8      - /run:size=50m
 9    deploy:
10      resources:
11        limits:
12          memory: 512M  # Account for tmpfs in memory limit

Best Practices:

  • Always set size limits
  • Include tmpfs size in container memory limits
  • Use for truly temporary data only
  • Monitor memory usage
  • Not suitable for large datasets

4. Security Best Practices

 1# Read-only root filesystem with writable tmpfs
 2docker run \
 3  --read-only \
 4  --tmpfs /tmp:size=100m \
 5  --tmpfs /run:size=50m \
 6  myapp:latest
 7
 8# User namespaces to avoid root access
 9docker run \
10  --userns-remap=default \
11  -v my-volume:/app/data \
12  myapp:latest
13
14# Specific user/group
15docker run \
16  --user 1000:1000 \
17  -v my-volume:/app/data \
18  myapp:latest

5. Production Recommendations

 1# Production setup example
 2services:
 3  app:
 4    image: myapp:1.2.3
 5    volumes:
 6      # Named volumes for persistent data
 7      - app-data:/var/lib/app
 8      - app-logs:/var/log/app
 9      # Read-only bind mounts for config
10      - ./config/production.yml:/etc/app/config.yml:ro
11    tmpfs:
12      # In-memory temporary files
13      - /tmp:size=100m
14    deploy:
15      replicas: 3
16      resources:
17        limits:
18          memory: 512M
19
20  db:
21    image: postgres:15
22    volumes:
23      # Volume with backup strategy
24      - postgres-data:/var/lib/postgresql/data
25    environment:
26      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
27    secrets:
28      - db_password
29    tmpfs:
30      - /tmp:size=50m
31
32volumes:
33  app-data:
34    driver: local
35    driver_opts:
36      type: none
37      o: bind
38      device: /mnt/app-data
39  app-logs:
40    driver: local
41  postgres-data:
42    driver: local
43    driver_opts:
44      type: none
45      o: bind
46      device: /mnt/postgres-data
47
48secrets:
49  db_password:
50    external: true

Real-World Examples

Example 1: Full-Stack Application

 1services:
 2  # Frontend (Development)
 3  frontend:
 4    image: node:18
 5    working_dir: /app
 6    command: npm run dev
 7    ports:
 8      - "3000:3000"
 9    volumes:
10      # Bind mounts for hot reload
11      - ./frontend/src:/app/src
12      - ./frontend/public:/app/public
13      - ./frontend/package.json:/app/package.json
14      # Anonymous volume for node_modules
15      - /app/node_modules
16    tmpfs:
17      # Fast build cache
18      - /app/.cache:size=500m
19    environment:
20      - NODE_ENV=development
21      - CHOKIDAR_USEPOLLING=true
22
23  # Backend (Production-like)
24  backend:
25    image: mybackend:latest
26    ports:
27      - "8080:8080"
28    volumes:
29      # Volume for uploaded files
30      - backend-uploads:/app/uploads
31      # Volume for generated reports
32      - backend-reports:/app/reports
33      # Read-only config
34      - ./backend/config.yml:/app/config.yml:ro
35    tmpfs:
36      # Session storage in memory
37      - /tmp/sessions:size=200m
38    environment:
39      - NODE_ENV=production
40      - LOG_LEVEL=info
41
42  # Database (Production)
43  database:
44    image: postgres:15
45    volumes:
46      # Persistent data storage
47      - postgres-data:/var/lib/postgresql/data
48      # Initialization scripts
49      - ./database/init:/docker-entrypoint-initdb.d:ro
50    tmpfs:
51      # PostgreSQL runtime files
52      - /run/postgresql:size=100m
53    environment:
54      - POSTGRES_PASSWORD=secret
55      - POSTGRES_DB=myapp
56
57  # Cache (Production)
58  redis:
59    image: redis:7-alpine
60    volumes:
61      # Persistent cache data
62      - redis-data:/data
63      # Custom configuration
64      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
65    command: redis-server /usr/local/etc/redis/redis.conf
66
67  # Nginx (Production)
68  nginx:
69    image: nginx:latest
70    ports:
71      - "80:80"
72      - "443:443"
73    volumes:
74      # Configuration
75      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
76      - ./nginx/conf.d:/etc/nginx/conf.d:ro
77      # SSL certificates
78      - ./nginx/ssl:/etc/nginx/ssl:ro
79      # Static files
80      - frontend-build:/usr/share/nginx/html:ro
81      # Logs
82      - nginx-logs:/var/log/nginx
83    tmpfs:
84      # Client body temp files
85      - /var/cache/nginx:size=500m
86    depends_on:
87      - frontend
88      - backend
89
90volumes:
91  backend-uploads:
92  backend-reports:
93  postgres-data:
94  redis-data:
95  frontend-build:
96  nginx-logs:

Example 2: Microservices with Shared Volumes

 1services:
 2  # Service 1: File Processor
 3  processor:
 4    image: file-processor:latest
 5    volumes:
 6      # Shared volume for file exchange
 7      - shared-files:/app/files
 8      # Processed files output
 9      - processed-files:/app/output
10    tmpfs:
11      # Fast processing workspace
12      - /tmp/workspace:size=1g
13
14  # Service 2: File Analyzer
15  analyzer:
16    image: file-analyzer:latest
17    volumes:
18      # Read from processed files
19      - processed-files:/app/input:ro
20      # Write analysis results
21      - analysis-results:/app/results
22    tmpfs:
23      # Analysis cache
24      - /tmp/cache:size=500m
25
26  # Service 3: API Server
27  api:
28    image: api-server:latest
29    ports:
30      - "8080:8080"
31    volumes:
32      # Read analysis results
33      - analysis-results:/app/data:ro
34      # API logs
35      - api-logs:/var/log/api
36
37volumes:
38  shared-files:
39  processed-files:
40  analysis-results:
41  api-logs:

Example 3: Development Environment

 1services:
 2  app:
 3    build:
 4      context: .
 5      dockerfile: Dockerfile.dev
 6    command: npm run dev
 7    ports:
 8      - "3000:3000"
 9    volumes:
10      # Full source code mount
11      - .:/app
12      # Exclude specific directories
13      - /app/node_modules
14      - /app/.git
15      - /app/dist
16    tmpfs:
17      # Fast test cache
18      - /app/.cache:size=500m
19      # Jest cache
20      - /tmp/jest:size=200m
21    environment:
22      - NODE_ENV=development
23      - DEBUG=app:*
24
25  db:
26    image: postgres:15
27    ports:
28      - "5432:5432"
29    volumes:
30      # Development database
31      - dev-db-data:/var/lib/postgresql/data
32      # Seed data
33      - ./database/seed.sql:/docker-entrypoint-initdb.d/seed.sql:ro
34    environment:
35      - POSTGRES_PASSWORD=devpassword
36      - POSTGRES_DB=devdb
37
38volumes:
39  dev-db-data:

Troubleshooting

Common Issues and Solutions

1. Permission Denied Errors

 1# Problem: Permission denied when writing to volume
 2# Solution 1: Match user IDs
 3docker run \
 4  --user $(id -u):$(id -g) \
 5  -v $(pwd):/app \
 6  myapp:latest
 7
 8# Solution 2: Change ownership in Dockerfile
 9FROM node:18
10RUN useradd -u 1000 -m appuser
11USER appuser
12
13# Solution 3: Use init container to fix permissions
14docker run \
15  --rm \
16  -v my-volume:/data \
17  alpine:latest \
18  chown -R 1000:1000 /data

2. Data Not Persisting

1# Problem: Data disappears after container restart
2# Wrong: Using container path only
3docker run myapp:latest  # No volume specified!
4
5# Correct: Use named volume
6docker run -v my-data:/app/data myapp:latest

3. Bind Mount Not Syncing (macOS/Windows)

1# Problem: File changes not reflected
2# Solution: make sure Docker Desktop uses VirtioFS file sharing (the default);
3# consistency=cached/delegated are ignored on current versions.
4# Or add polling for file watchers
5docker run \
6  -v $(pwd):/app \
7  -e CHOKIDAR_USEPOLLING=true \
8  node:18 npm run dev

4. Volume Taking Too Much Space

 1# Check volume sizes
 2docker system df -v
 3
 4# Remove unused volumes
 5docker volume prune
 6
 7# Remove specific volume
 8docker volume rm volume-name
 9
10# Clean everything (careful!)
11docker system prune -a --volumes

5. Cannot Remove Volume

 1# Problem: "volume is in use"
 2# Solution 1: Find and stop containers using it
 3docker ps -a --filter volume=my-volume
 4
 5# Solution 2: Force remove container and volume
 6docker rm -f container-name
 7docker volume rm my-volume
 8
 9# Solution 3: Remove all stopped containers first
10docker container prune
11docker volume rm my-volume

Migration Strategies

Migrating from Bind Mounts to Volumes

 1# Step 1: Create volume
 2docker volume create my-app-data
 3
 4# Step 2: Copy data from bind mount to volume
 5docker run --rm \
 6  -v /host/path:/source:ro \
 7  -v my-app-data:/dest \
 8  alpine:latest \
 9  sh -c "cp -av /source/. /dest/"
10
11# Step 3: Update docker-compose.yml
12# Before:
13volumes:
14  - /host/path:/app/data
15
16# After:
17volumes:
18  - my-app-data:/app/data

Backing Up and Restoring Volumes

 1# Backup volume to tar file
 2docker run --rm \
 3  -v my-volume:/source:ro \
 4  -v $(pwd):/backup \
 5  alpine:latest \
 6  tar czf /backup/my-volume-backup-$(date +%Y%m%d).tar.gz -C /source .
 7
 8# Restore volume from tar file
 9docker run --rm \
10  -v my-volume:/dest \
11  -v $(pwd):/backup:ro \
12  alpine:latest \
13  tar xzf /backup/my-volume-backup-20250101.tar.gz -C /dest
14
15# Or use dedicated backup tool
16docker run --rm \
17  -v my-volume:/volume \
18  -v $(pwd):/backup \
19  loomchild/volume-backup backup my-volume

Performance Optimization

1. Volume Performance Tips

 1# Use local driver with optimal options
 2volumes:
 3  fast-volume:
 4    driver: local
 5    driver_opts:
 6      type: none
 7      o: bind
 8      device: /mnt/fast-ssd/data  # Use SSD storage
 9
10# For databases, use direct mount
11volumes:
12  postgres-data:
13    driver: local
14    driver_opts:
15      type: none
16      o: bind
17      device: /mnt/database-ssd

2. Bind Mount Performance (macOS/Windows)

 1# :delegated / :cached are ignored by current Docker Desktop (VirtioFS).
 2# The real win is keeping node_modules out of the bind mount:
 3services:
 4  app:
 5    volumes:
 6      - ./src:/app/src
 7      - node_modules:/app/node_modules   # named volume, not a bind mount
 8
 9volumes:
10  node_modules:

3. tmpfs for Performance-Critical Operations

1# Use tmpfs for build artifacts
2services:
3  builder:
4    volumes:
5      - ./src:/app/src:ro
6    tmpfs:
7      - /app/dist:size=2g
8      - /tmp:size=1g

Conclusion

Choosing the right Docker mount type is crucial for application performance, development workflow, and data management:

Quick Selection Guide

  • Use Docker Volumes when:

    • Building production applications
    • Need data persistence across container lifecycles
    • Want Docker-managed storage
    • Require easy backup and migration
    • Need to share data between multiple containers
  • Use Bind Mounts when:

    • Developing applications locally
    • Need real-time file synchronization
    • Want to edit files directly on host
    • Mounting configuration files
    • CI/CD pipelines needing access to build artifacts
  • Use tmpfs Mounts when:

    • Handling sensitive temporary data
    • Need maximum performance
    • Working with temporary build artifacts
    • Storing session data
    • Processing files that don’t need persistence

Key Takeaways

  1. Volumes are preferred for production - Docker-managed, portable, and easy to backup
  2. Bind mounts excel in development - Direct access, hot reload, familiar workflow
  3. tmpfs provides security and speed - Perfect for temporary, sensitive data
  4. Mix approaches when appropriate - Different needs require different solutions
  5. Consider platform differences - Performance varies between Linux, macOS, and Windows

Understanding these mount types and their trade-offs enables you to build more efficient, maintainable, and production-ready containerized applications.

Additional Resources

Yen

Yen

Yen