Skip to main content
Docker intermediate Lesson 5 of 9

Docker Volumes and Storage

Persist data outside containers using volumes and bind mounts. Understand the container filesystem lifecycle.

Why Containers Lose Data

Every container has a thin writable layer on top of the image. When the container is removed, that layer is deleted. Images are read-only.

Image layer (read-only)     ← FROM, RUN, COPY instructions
    +
Writable container layer    ← runtime changes — DELETED on docker rm

To persist data, write it outside the container using a volume or bind mount.

Storage Types

TypeDescriptionBest for
Named volumeDocker-managed storage at /var/lib/docker/volumes/Production databases, persistent state
Bind mountMap a host directory into the containerDevelopment (live code reload)
tmpfs mountIn-memory only, never written to diskSecrets, temporary scratch space

Named Volumes

# Create a volume explicitly
docker volume create pgdata

# Mount it when running a container (-v name:containerPath)
docker run -d \
  --name postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:15

# List volumes
docker volume ls

# Inspect a volume (see mount point on host)
docker volume inspect pgdata

# Remove a specific volume
docker volume rm pgdata

# Remove all unused volumes
docker volume prune

Even after docker rm postgres, the pgdata volume remains on disk. The next container mounting it picks up the existing data.

Bind Mounts

# Mount current directory into container (development)
docker run -d \
  --name dev-server \
  -v $(pwd):/app \           # host path : container path
  -p 3000:3000 \
  node:18-alpine \
  npm run dev

# Mount a specific config file (read-only)
docker run -d \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx

Windows note: Use absolute paths: -v C:/Users/you/project:/app

tmpfs Mounts

Stored in host memory only — never on disk. Good for sensitive data:

docker run -d \
  --tmpfs /run \
  --tmpfs /tmp:size=64m \
  myapp:1.0

Practical: Development Setup with Bind Mount

# Mount source code so changes reflect without rebuilding
docker run -d \
  --name dev \
  -v $(pwd)/src:/app/src \    # bind mount source
  -v node_modules:/app/node_modules \  # named volume for deps
  -p 3000:3000 \
  node:18-alpine \
  npm run dev

# Separate volume for node_modules prevents host modules from
# overwriting the container's modules

Backing Up and Restoring Volumes

# Backup: run a temporary container to tar the volume contents
docker run --rm \
  -v pgdata:/data \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/pgdata-backup.tar.gz -C /data .

# Restore: extract into a fresh volume
docker volume create pgdata-new
docker run --rm \
  -v pgdata-new:/data \
  -v $(pwd):/backup \
  alpine \
  tar xzf /backup/pgdata-backup.tar.gz -C /data

Volume Permissions

Containers often run as non-root users. If the volume mount is owned by root, the app can’t write to it.

# In your Dockerfile — create dir and set ownership before USER
RUN mkdir -p /app/data && chown node:node /app/data
USER node

Or fix ownership at runtime:

docker run --rm -v myvolume:/data alpine chown -R 1000:1000 /data

Frequently Asked Questions

Should I use volumes or bind mounts?
Volumes for everything in production — Docker manages them, they work on all platforms, and they're portable. Bind mounts for development — mount your source code into the container so edits reflect immediately without rebuilding.
What happens to my database data when I remove the container?
It's gone unless you mounted a volume. Always use a named volume for database containers: -v pgdata:/var/lib/postgresql/data
Can two containers share the same volume?
Yes. Mount the same named volume in multiple containers. Be careful about concurrent writes — use this pattern for read-sharing (e.g. static assets) or with apps that handle concurrent access correctly.