Docker Volumes vs Bind Mounts: A Practical Guide for Homelabbers
Confused about Docker volumes vs bind mounts? This guide explains when to use each one in your homelab with real examples and step-by-step instructions.
Author
David Okonkwo
Key Takeaways
- Volumes are managed by Docker, stored in its own directory, and are the preferred choice for production data and databases.
- Bind mounts let you mount any host directory into a container - ideal for development, configuration files, and sharing data with the host.
- Use volumes when Docker manages the data lifecycle; use bind mounts when you need direct host filesystem access or want to edit files from the host.
- Docker Compose makes both types easy to declare with simple YAML syntax.
- Back up your volumes regularly - they're invisible in your normal filesystem browsing.
If you've ever stared at a docker run -v command and wondered whether to type a path or a name, you're not alone. Every homelabber hits this fork in the road. The good news is that the difference between Docker volumes and bind mounts is simpler than most tutorials make it sound - and once you understand it, you'll know exactly which one to pick every time.
I remember my first homelab setup. I had Jellyfin running in a container, and I kept losing my media library configuration every time I updated the container. I'd recreate the stack, paste in my settings, and cross my fingers. Then someone told me about persistent storage, and my entire homelab experience changed overnight. That's the moment I want to help you get to - where your data survives container restarts, updates, and migrations without drama.
This guide walks through what Docker volumes and bind mounts are, how they differ, and - most importantly - when to use each one in your homelab.
Why This Matters
Before we talk about the how, let's talk about the why. Containers are ephemeral by design. When you stop a container, its filesystem disappears with it. If you store a database file inside a container and that container crashes or gets replaced during an update, you lose your data.
That's not acceptable for a homelab. Your Jellyfin watch history, your Home Assistant configuration, your Pi-hole statistics - these need to survive container updates. Docker gives you two ways to persist data: volumes and bind mounts.
Think of it like this: a container is a temporary rental apartment. Everything inside it is the landlord's furniture. When you move out (delete the container), everything inside goes with you. Volumes and bind mounts are your personal storage unit - stuff you keep regardless of which apartment you're in.
What Are Docker Volumes?
A Docker volume is a storage location managed entirely by Docker. When you create a volume, Docker sets aside a directory on your host system (usually at /var/lib/docker/volumes/) and gives it a name. Containers can then mount that volume and read from or write to it.
docker volume create jellyfin-config
docker run -v jellyfin-config:/config jellyfin/jellyfin
Here's what's happening: Docker creates a directory somewhere on your host, names it jellyfin-config, and tells the Jellyfin container to treat the /config directory inside the container as that volume. Any data Jellyfin writes to /config actually goes into the Docker-managed volume directory on your host.
- Docker creates and manages them
- They have friendly names you choose
- They work the same across Linux, macOS, and Windows
- Docker handles permissions for you
- They can be backed up and restored with Docker commands
- You can share a volume across multiple containers
The biggest advantage for beginners is that volumes just work. You don't need to know where they're stored on the host. You don't need to set file permissions. You give them a name, mount them in your container, and Docker takes care of the rest.
What Are Bind Mounts?
A bind mount is a direct link between a directory on your host system and a directory inside a container. You pick any path on your host - like /home/akash/jellyfin-config - and mount it into the container.
docker run -v /home/akash/jellyfin-config:/config jellyfin/jellyfin
The difference from a volume is subtle but important. With a volume, Docker manages the storage location. With a bind mount, you're in full control - you point Docker at an existing directory on your host, and the container sees it as a folder inside itself.
Key characteristics of bind mounts:- You control the host path
- Files are directly accessible from the host
- Great for development (edit code on host, run in container)
- File permissions are the host's permissions
- Host path must exist or Docker will create it as a directory
- Less portable (hardcoded paths)
Bind mounts are the obvious choice when you need to edit files from your host machine. If you're developing a web application, you can edit code in VS Code on your host and see changes instantly inside the running container. That's a powerful workflow.
Side-by-Side Comparison
| Feature | Volumes | Bind Mounts |
|---|---|---|
| Managed by | Docker | You |
| Location on host | /var/lib/docker/volumes/ | Any path you choose |
| Host access | Requires root or docker group | Direct - any user can access |
| Permissions | Docker handles them | Your host permissions apply |
| Portability | High - works on any system | Low - path-dependent |
| Backup | docker run --volumes-from | Standard filesystem tools |
| Empty host path | Volume is created | Container content is exposed |
| Performance | Native (same as host) | Native (same as host) |
| Best for | Databases, app data, config | Dev code, media files, logs |
One important thing to notice: performance is essentially the same for both. Neither volumes nor bind mounts are inherently faster. The difference is in management, not speed.
When to Use Docker Volumes
Use volumes in these situations:
1. Database Storage
Databases are the poster child for volumes. PostgreSQL, MySQL, MongoDB, Redis - they all need persistent storage that Docker can manage cleanly.
# docker-compose.yml
services:
postgres:
image: postgres:16
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
If you ever need to migrate your database to a different host, you can back up the volume with docker run --rm -v postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz -C /data . and restore it on the new machine. The volume name stays the same, and your applications don't care where the data physically lives.
2. Application Configuration That Shouldn't Change
Some applications write configuration files at first startup. If you need that config to survive container updates, a volume keeps it safe without exposing it to accidental modification on the host.
3. Data You Want Docker to Back Up
If you use tools like Duplicati running in a container, volumes are easier to back up because you can mount them together in a backup container.
When to Use Bind Mounts
Bind mounts shine in these scenarios:
1. Development and Testing
This is where bind mounts really earn their keep. You can edit files on your host in VS Code or Vim and see changes reflected immediately in the running container.
# docker-compose.yml for development
services:
webapp:
build: .
volumes:
- ./src:/app/src # bind mount for live code editing
- /app/node_modules # anonymous volume to prevent overwriting
I use this pattern constantly. I write code in VS Code on my desktop, the file changes appear instantly inside the container, and the dev server auto-reloads. No rebuilds needed.
2. Media Libraries
If you run Jellyfin, Plex, or Emby, your media files are probably on a NAS or an external drive. Bind mounts let you point the container at exactly where your media lives.
services:
jellyfin:
image: jellyfin/jellyfin
volumes:
- jellyfin-config:/config # volume for config
- /mnt/nas/media/movies:/media/movies # bind mount for media
- /mnt/nas/media/tv:/media/tv # bind mount for TV shows
- /mnt/nas/media/music:/media/music # bind mount for music
Your media files are too large to live inside Docker's volume directory, and you want to manage them from your NAS directly. Bind mounts are the natural choice here.
3. Configuration Files You Want to Edit
Some applications need configuration files that you'll edit frequently. For example, Caddy's Caddyfile or Nginx's config files. A bind mount makes them accessible from your host with your regular editor.
4. Log Files
If you want Docker containers to write logs to a specific location on your host (like /var/log/myapp/), bind mounts give you that control.
Step-by-Step: Creating and Managing Volumes
Let me show you how to work with volumes in practice.
Creating a Named Volume
docker volume create my-app-data
You can inspect it to see where it lives:
docker volume inspect my-app-data
Output:
[
{
"CreatedAt": "2026-06-06T10:00:00Z",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/my-app-data/_data",
"Name": "my-app-data",
"Scope": "local"
}
]
The Mountpoint field shows where the data actually lives on your host. You can go there and look at the files if you need to - but the whole point of volumes is that you usually don't need to.
Using a Volume with a Container
docker run -d --name myapp -v my-app-data:/app/data myimage
The -v flag takes volume-name:container-path. Docker matches the first part against existing volume names. If a volume with that name exists, it mounts it. If not, Docker creates a new one.
Using Volumes in Docker Compose
For most homelab setups, Docker Compose is how you'll manage your stacks. Here's the pattern:
version: "3.8"
services:
app:
image: myapp:latest
volumes:
- app-data:/app/data
volumes:
app-data: # Docker creates this volume
Docker Compose prefixes volume names with the project directory name. If your compose file is in ~/docker/myapp/, the volume will be named myapp_app-data. You can override this with name::
volumes:
app-data:
name: my-persistent-data
Backing Up a Volume
docker run --rm -v my-app-data:/data -v /tmp/backup:/backup alpine \
tar czf /backup/volume-backup-$(date +%Y%m%d).tar.gz -C /data .
This runs a temporary Alpine container, mounts both the volume and your backup directory, and creates a compressed archive. Simple and repeatable.
Step-by-Step: Using Bind Mounts in Docker Compose
Bind mounts in Docker Compose are just as straightforward:
services:
app:
image: myapp:latest
volumes:
- /absolute/path/on/host:/app/data:ro # ro = read-only
You can use relative paths too:
services:
app:
image: myapp:latest
volumes:
- ./config:/app/config # relative to compose file
Important: The Initialization Behavior
Here's something that trips up almost everyone at least once. When you mount an empty directory from your host into a container, it overwrites whatever was in the container's directory. If the container image had default configuration files at /app/config, they won't show up in your bind mount unless the host directory already has files there.
With volumes, Docker handles this differently. If the volume is empty and the container image has data at the mount point, Docker copies that data into the volume on first use. This is why volumes are often easier for application configuration - you don't need to manually populate them first.
The Docker Compose Extended Syntax
Both volumes and bind mounts can use Docker Compose's extended syntax for more control:
services:
app:
image: myapp:latest
volumes:
- type: volume
source: app-data
target: /app/data
volume:
nocopy: false # copy container data to empty volume
- type: bind
source: ./config
target: /app/config
read_only: true
volumes:
app-data:
This syntax is more verbose but makes the intent explicit. I use it in production stacks where clarity matters more than brevity.
Common Mistakes
Here are the mistakes I see most often in the homelab community:
1. Confusing the Argument Order
The -v (or --volume) flag takes source:destination, not the other way around. I've lost count of how many times I've accidentally typed the container path first.
# Correct
docker run -v my-volume:/container/path image
# Wrong (this creates a bind mount named /container/path)
docker run -v /container/path:my-volume image
2. Forgetting the Colon When Using Bind Mounts
If you write -v /home/akash/data without a colon, Docker treats it as a named volume, not a bind mount. The path becomes a volume name, and Docker creates it under /var/lib/docker/volumes/.
3. Expecting Bind Mounts to Copy Container Data
As I mentioned above, bind mounts don't initialize from the container image. If you mount an empty host directory, the container sees an empty directory - even if the image had important files there. Pre-populate your host directory first, or use volumes.
4. Permission Problems with Bind Mounts
Bind mounts use host permissions. If the container runs as a non-root user (UID 1000) but your host directory is owned by root, the container can't write to it.
# Fix: set the correct ownership
sudo chown -R 1000:1000 ./data
Or check what user the container runs as and match it:
# Find the container's user
docker run --rm myimage id
5. Not Backing Up Your Volumes
Volumes are hidden away in /var/lib/docker/volumes/. You won't stumble across them in your daily filesystem browsing, which means they're easy to forget when you're doing backups. Add volume backup commands to your backup scripts.
6. Using Bind Mounts for Database Data
Databases can be finicky about file locking and permissions. Bind mounts sometimes cause issues that volumes don't. When in doubt, use a named volume for database storage.
How to Choose: A Decision Framework
Here's a simple decision tree I use:
- Is this a database? Use a volume.
- Does the application need to write lots of data you don't want to manage? Use a volume.
- Do you need to edit the files from your host? Use a bind mount.
- Are you mounting media files from a NAS? Use a bind mount.
- Is this a development environment? Use a bind mount.
- Is this a production deployment? Default to volumes.
Recommended Storage Hardware
Running containers with persistence means you need reliable storage. Here are a few products I recommend based on my own testing:
If you're running your Docker host on a mini PC (which is an excellent choice for a homelab), make sure it has enough storage:
For a more budget-friendly option, the Western Digital SN770 is a reliable NVMe drive for less demanding workloads:
FAQ
1. Is there a performance difference between volumes and bind mounts?
No, not in practice. Both access the host filesystem directly. The difference is in management, not speed. The only exception is Docker for Mac, where filesystem operations on bind mounts can be slower due to the macOS filesystem sharing layer - but for volumes, Docker uses a native implementation that's faster.
2. Can I convert a bind mount to a volume after I've started using it?
Not directly, but you can migrate the data. Stop the container, copy the data from your bind mount directory into a new volume with docker run --rm -v /path/on/host:/source -v new-volume:/destination alpine cp -a /source/. /destination/, then update your compose file to use the volume instead.
3. What happens if I delete a container using a volume?
The volume and its data persist. Docker only warns you about this when you use docker rm -v (the -v flag explicitly deletes anonymous volumes). Named volumes are never automatically deleted.
4. Can multiple containers share the same volume?
Yes, and this is a common pattern. Multiple containers can mount the same volume simultaneously. Just make sure they don't write to the same files at the same time unless you have application-level locking.
5. Where should I put my Docker volumes for backup purposes?
You don't need to move them. Use Docker's built-in backup command (shown in the backup section above) to create archives that you can store anywhere. Add this to your homelab backup routine - check out our Homelab Backups and Monitoring guide for a complete backup strategy.
What to Learn Next
Understanding volumes and bind mounts is a foundational Docker skill. Once you're comfortable with these concepts, here's what I'd recommend exploring next:
- Docker Compose Best Practices - If you're ready to take your Docker setups to the next level, read through Docker Compose Best Practices: 12 Rules I Follow After Running 40+ Containers for patterns used in real production stacks.
- Docker Networking - Persistent storage is half the picture. Understanding how containers talk to each other is the other half. Our Docker for Homelabs guide covers the networking basics you need.
- Container Orchestration - Once you have a few containers running with persistent storage, you'll want to think about how they work together. The Docker Swarm vs K3s guide can help you decide which orchestration tool fits your setup.
- Homelab Hardware - If you're building your first homelab, don't miss Homelab Hardware Basics: What You Really Need to Get Started. Choosing the right hardware from the start saves you from rebuilding later.
- Backup Strategies - Your data is only safe if it's backed up. Read Homelab Backups and Monitoring for a complete approach to protecting your homelab data.
The beauty of Docker is that you don't need to learn everything at once. Start with volumes for your databases and bind mounts where you need direct host access. You'll quickly develop a feel for which one fits each situation. And if you make the wrong choice? Docker makes it easy to change your mind - your data is what matters, and as long as it's persisted, you can always migrate it later.

alt="WD Red Plus 8TB NAS Hard Drive"
alt="Samsung 990 Pro 2TB NVMe SSD"
alt="Western Digital SN770 1TB NVMe SSD"