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
| Type | Description | Best for |
|---|---|---|
| Named volume | Docker-managed storage at /var/lib/docker/volumes/ | Production databases, persistent state |
| Bind mount | Map a host directory into the container | Development (live code reload) |
| tmpfs mount | In-memory only, never written to disk | Secrets, 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.