Docker volumes cheatsheet.

Three types

  • Named volume: managed by Docker, persists between containers.
  • Bind mount: maps a host directory into container.
  • tmpfs: in-memory, not persistent.

Named volume

docker volume create data
docker run -v data:/var/lib/data app
docker run --mount type=volume,src=data,dst=/var/lib/data app

Bind mount

docker run -v $(pwd)/local:/app app
docker run -v $(pwd):/app:ro app                    # read-only
docker run --mount type=bind,src=/path,dst=/app app

tmpfs

docker run --tmpfs /tmp app
docker run --mount type=tmpfs,dst=/tmp,tmpfs-size=64m app

Useful for sensitive temp data.

Volume management

docker volume ls
docker volume inspect data
docker volume rm data
docker volume prune                                  # remove unused

Anonymous volumes

docker run -v /app/node_modules app

Preserves /app/node_modules from the image even when bind-mounting . over /app.

volumes:
  - .:/app
  - /app/node_modules        # anonymous, hides bind mount

Volume drivers

docker volume create --driver local --opt type=nfs \
  --opt o=addr=192.168.1.10,rw \
  --opt device=:/path nfs_data

Plugins for NFS, S3, Ceph, etc.

Compose volumes

services:
  db:
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro

volumes:
  pg_data:
  cache:
    external: true               # already exists

Backup

docker run --rm -v pg_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/pg_data.tar.gz -C /data .

Restore

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

DB volume backup (Postgres)

docker exec db pg_dump -U postgres myapp > backup.sql
docker exec -i db psql -U postgres myapp < backup.sql

Ownership / permissions

Bind mounts inherit host user/group:

docker run -u $(id -u):$(id -g) -v $(pwd):/app app

Otherwise files created by container may be owned by root (annoying).

RUN useradd -u 1000 -m app
USER app

Match UID with host user.

consistency (Mac/Windows)

volumes:
  - .:/app:cached              # host -> container can lag
  - .:/app:delegated           # container -> host can lag
  - .:/app:consistent          # default; can be slow

Performance on Mac/Windows is bad due to virtualization. Use cached for read-heavy.

SELinux (Linux)

docker run -v $(pwd):/app:z app    # shared
docker run -v $(pwd):/app:Z app    # private

Or set --security-opt label=disable.

Read-only root filesystem

docker run --read-only --tmpfs /tmp --tmpfs /var/run app

Forces immutable container. Tighten security; ensure app doesn’t need to write outside specified mounts.

Sharing data between containers

docker volume create shared
docker run -d --name producer -v shared:/data producer
docker run -d --name consumer -v shared:/data consumer

Both see /data.

Initialize from image

FROM postgres:16
COPY ./init.sql /docker-entrypoint-initdb.d/

Or via compose:

db:
  image: postgres:16
  volumes:
    - ./init:/docker-entrypoint-initdb.d:ro
    - pg_data:/var/lib/postgresql/data

Don’t bind-mount over a database

# BAD
volumes:
  - ./pg_data:/var/lib/postgresql/data

Bind mounts have host filesystem semantics — Postgres on Mac/Windows hits perf issues. Use named volume.

Migration / size monitoring

docker system df -v
docker volume inspect data | grep Mountpoint
du -sh /var/lib/docker/volumes/data/_data

Common mistakes

  • Bind-mounting . over /app wiping the image’s node_modules.
  • Forgetting :ro for config files.
  • Volume contains stale data from old image build.
  • UID mismatch → permission errors.
  • Anonymous volumes piling up after docker rm.

Read this next

If you want my backup + restore scripts, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .