CloudOpsGuide
docker

Docker Volumes: Persistent Storage Solutions

Beginner
12 minutes
October 2026
CloudOpsGuide Team

Docker Volumes: Persistent Storage Solutions

Manage persistent data in Docker containers — named volumes, bind mounts, tmpfs, and best practices for production workloads.

Table of Contents

Volume Types

TypeData LocationPersistenceUse Case
Named VolumeDocker-managed (/var/lib/docker/volumes)Survives container deletionDatabases, app data
Bind MountSpecific host pathDepends on hostConfig files, source code
tmpfsHost RAM onlyGone on stopTemporary sensitive data

Named Volumes

Create and Use

# Create a named volume
docker volume create my-data

# Use it
docker run -d --name db -v my-data:/var/lib/postgresql/data postgres:16

# Inspect
docker volume inspect my-data

Docker Compose

services:
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:    # Docker creates and manages this

Drivers

# Local driver (default)
docker volume create my-data

# NFS driver
docker volume create --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/exports/data \
  nfs-data

# Azure Files (via plugin)
docker volume create --driver azure_file_storage azure-data

Bind Mounts

# Bind mount — host path to container path
docker run -d --name nginx \
  -v /home/user/html:/usr/share/nginx/html:ro \
  nginx:alpine

Read-Only vs Read-Write

# Read-only — container can't modify files
-v /host/path:/container/path:ro

# Read-write (default)
-v /host/path:/container/path:rw

Common Use Cases

# Source code for development
docker run -v $(pwd)/src:/app/src node:20 npm run dev

# Config files
docker run -v ./nginx.conf:/etc/nginx/nginx.conf:ro nginx

# Logs to host
docker run -v ./logs:/var/log/app myapp

tmpfs Mounts

# Store sensitive data in RAM only — never touches disk
docker run -d --name app \
  --tmpfs /tmp/secrets \
  myapp

Use for: temporary credentials, session data, cache that must not persist.

Docker Compose Volumes

Named + Bind + tmpfs Together

services:
  app:
    image: myapp
    volumes:
      - app-data:/app/data          # named volume (persistent)
      - ./config:/app/config:ro     # bind mount (config)
      - type: tmpfs
        target: /tmp/cache           # tmpfs (temporary)

  db:
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data

  shared:
    image: alpine
    volumes:
      - shared-data:/shared

volumes:
  app-data:
  db-data:
  shared-data:

Volume From Another Container

services:
  backup:
    image: alpine
    volumes:
      - db-data:/backup:ro    # read-only access to db's volume
    command: tar czf /backup/backup.tar.gz -C /backup .

Backup and Restore

Backup a Named Volume

# Create a backup of a named volume
docker run --rm \
  -v db-data:/source:ro \
  -v $(pwd):/backup \
  alpine tar czf /backup/db-backup-$(date +%Y%m%d).tar.gz -C /source .

Restore to a New Volume

docker volume create db-data-restored

docker run --rm \
  -v db-data-restored:/target \
  -v $(pwd):/backup \
  alpine sh -c "cd /target && tar xzf /backup/db-backup-20261007.tar.gz"

Backup Running Container's Data

# Stop the container first for consistency
docker stop db
docker run --rm -v db-data:/source:ro -v $(pwd):/backup alpine tar czf /backup/db.tar.gz -C /source .
docker start db

Best Practices

1. Named Volumes Over Bind Mounts for Production

# Bad — host-dependent, fragile
volumes:
  - /home/user/data:/var/lib/postgresql/data

# Good — portable, Docker-managed
volumes:
  - pgdata:/var/lib/postgresql/data

2. Read-Only for Config Files

volumes:
  - ./nginx.conf:/etc/nginx/nginx.conf:ro    # can't be modified at runtime
  - ./ssl:/etc/ssl/certs:ro

3. Volume Naming Convention

volumes:
  - "${APP_NAME}-data:/app/data"
  - "${APP_NAME}-logs:/app/logs"
  - "${APP_NAME}-config:/app/config:ro"

4. Clean Up Orphaned Volumes

# See unused volumes
docker volume ls -f dangling=true

# Remove unused volumes
docker volume prune

5. Don't Store Logs in Named Volumes

# Bad — logs fill up disk silently
docker run -v app-logs:/var/log/app myapp

# Better — use Docker's logging drivers
docker run --log-driver=json-file --log-opt max-size=10m --log-opt max-file=3 myapp

Common Issues

"Permission Denied" on Volume

The container's user can't write to the host directory:

# Fix ownership on host
docker run -v $(pwd)/data:/app/data --user $(id -u):$(id -g) myapp

# Or use named volumes (no permission issues)
docker run -v app-data:/app/data myapp

Volume Size Growing Unbounded

# Check what's using space
docker system df -v | grep volume

# See inside a volume
docker run --rm -v my-volume:/data alpine du -sh /data/* | sort -h

Data Lost After Container Removal

Volumes persist containers — but only if you use -v (named/bind). If you didn't:

# Data is inside the container's writable layer — gone on removal
docker rm my-container    # data lost!

# Always use volumes for data
docker run -v my-data:/data myapp

Quick Reference

# List volumes
docker volume ls

# Inspect a volume
docker volume inspect my-data

# Create volume
docker volume create my-data

# Remove specific volume
docker volume rm my-data

# Remove all unused volumes
docker volume prune

# Run with volume
docker run -v name:/path image

# Run with bind mount
docker run -v /host/path:/container/path:ro image

# Run with tmpfs
docker run --tmpfs /tmp image

Related Articles


Last Updated: October 2026
Author: CloudOpsGuide Team
Difficulty: Beginner
Estimated Reading Time: 12 minutes