Volumes & Storage
Persist data beyond container lifecycles with named volumes and bind mounts. Understand when to use each and how to manage them.
Why Container Storage Is Temporary
Each container has its own isolated filesystem layer. It's writable while the container runs, but it's tied to that container's lifecycle. Remove the container, lose the data. For anything that needs to outlive a container like database data, user uploads, logs; you need to store it outside the container using one of Docker's three storage options:
| Type | Managed By | Best For |
|---|---|---|
| Named Volume | Docker | Databases, persistent app data |
| Bind Mount | You | Local development, config files |
| tmpfs Mount | Memory (not disk) | Sensitive temp data |
Named Volumes
Named volumes are stored in Docker's own managed area on your host (/var/lib/docker/volumes/ on Linux). You don't need to know the exact path, Docker manages it for you.
# Create a volume explicitly
docker volume create mydata
# Or let Docker create it automatically on first use
docker run -d \
--name postgres \
-v pgdata:/var/lib/postgresql/data \
postgres:16
The -v syntax is volume-name:path-inside-container. Docker creates the volume if it doesn't exist yet.
The key benefit is that data survives container removal:
# Start a database container
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16
# ... add data to the database ...
# Remove the container
docker rm -f db
# Recreate it with the same volume and you'll see that all data is still there
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16
The container is gone but the volume isn't. Named volumes only get deleted when you explicitly remove them.
Bind Mounts
A bind mount maps a specific folder or file from your host machine directly into the container. Changes on either side are reflected instantly which means no rebuild, no restart. Edit a file in your editor, and the container sees it immediately. This is the standard way to run a development server in Docker without rebuilding the image on every change.
# Mount your current project directory into the container
docker run -d \
--name my-app \
-v $(pwd):/app \
-p 3000:3000 \
node:20-alpine \
sh -c "cd /app && npm run dev"
# Mount a directory to capture logs on the host
docker run -d \
-v $(pwd)/logs:/app/logs \
my-app
Named Volume vs Bind Mount
| Scenario | Use |
|---|---|
| Database data | Named volume |
| Local development with live reload | Bind mount |
| Config files you want to edit easily | Bind mount |
| Production persistent data | Named volume |
| CI/CD artifacts you need on the host | Bind mount |
| Portability across different machines | Named volume |
The short version: named volumes for data that needs to last, bind mounts for files you're actively working with.
tmpfs Mounts
It is a In-Memory Storage and can be used for sensitive data (tokens, secrets, session data) that should never be written to disk. A tmpfs mount exists only in memory. It's fast, it's never written to disk, and it disappears completely when the container stops. It is perfect for temporary files or sensitive in-memory data.
docker run -d \
--tmpfs /app/tmp:rw,size=64m \
my-app
Managing Volumes
# List all volumes
docker volume ls
# Inspect a volume (find its actual location on the host)
docker volume inspect pgdata
# Remove a specific volume
docker volume rm mydata
# Remove all volumes not attached to any container
docker volume prune
# Remove without being prompted to confirm
docker volume prune -f
Sharing a Volume Between Containers
Multiple containers can mount the same volume at the same time:
# Create a shared volume
docker volume create shared-logs
# App container writes logs to it
docker run -d --name app -v shared-logs:/app/logs myapp
# Log shipper reads from it (read-only)
docker run -d --name logshipper -v shared-logs:/logs:ro fluentd
One writer, multiple readers is safe. Multiple containers writing to the same files simultaneously can corrupt data, so design it carefully.
Backing Up and Restoring Volumes
Since named volumes are managed by Docker, backing them up means spinning up a temporary container to read and archive the data:
# Back up a volume to a tar file on your host
docker run --rm \
-v pgdata:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/pgdata-backup.tar.gz -C /source .
# Restore from that backup into a volume
docker run --rm \
-v pgdata:/target \
-v $(pwd):/backup \
alpine tar xzf /backup/pgdata-backup.tar.gz -C /target
This pattern is using a lightweight Alpine container to read or write volume contents and is the standard Docker approach for volume backups.
Quick Reference
| Command | What It Does |
|---|---|
docker volume create <n> | Create a named volume |
docker volume ls | List all volumes |
docker volume inspect <n> | See volume details and host path |
docker volume rm <n> | Delete a specific volume |
docker volume prune | Delete all unused volumes |
-v name:/path | Mount a named volume |
-v $(pwd):/path | Mount current directory (bind mount) |
-v /host/file:/container/file:ro | Read-only bind mount |
Key Takeaways
- Container filesystems are temporary which means data written inside a container is lost when the container is removed unless you use a volume or bind mount
- Named volumes are Docker-managed and survive container removal. They're the right choice for databases and any data that needs to last
- Bind mounts map host paths directly into the container ideal for local development where you want live code reload without rebuilding
- Deleting a container does not delete its volumes. You have to explicitly run
docker volume rmordocker volume pruneto free up the space tmpfsmounts store data in memory only, never on disk, use them for secrets or sensitive temp files- Run
docker volume pruneperiodically to clean up volumes left behind by removed containers
Frequently Asked Questions
If I delete a container, does its volume get deleted too?
No. Named volumes persist independently of containers. The volume and its data stick around until you explicitly remove it with docker volume rm or docker volume prune. This is a common source of confusion for beginners, and also why docker volume prune is a useful cleanup command.
What's the difference between a named volume and a bind mount?
A named volume is managed by Docker and you can use it by name and Docker handles where it lives on your host. A bind mount is a specific path on your host machine that you map into the container. Named volumes are more portable and better for persistent data; bind mounts are more convenient for development because you can see and edit the files directly.
Why is my bind mount showing an empty directory inside the container?
If the container image already has files at the mounted path, the bind mount will hide them. Either pre-populate your host directory with the needed files, or use a named volume which merges with the image's existing content at that path.
Can two containers write to the same volume at the same time?
They can, but it's risky. Concurrent writes to the same files from multiple containers can corrupt data as there's no built-in locking. The safe pattern is one writer and multiple read-only readers (using :ro).
How do I find where a named volume is stored on my host?
Run docker volume inspect <name> and look at the Mountpoint field. On Linux it's usually under /var/lib/docker/volumes/. On Mac and Windows, volumes live inside the Docker Desktop VM, so you can't browse them directly from Finder/Explorer, that's why named volumes are better than bind mounts for those platforms.
What happens to my data if I run docker volume prune?
It permanently deletes all volumes that aren't currently attached to at least one container (running or stopped). Before running it, check docker volume ls and make sure you're not about to lose anything important. There's no undo.