Every Docker container running in your homelab generates data — database files, uploaded assets, configuration, logs, user content. Lose that data when a container restarts or moves to another host, and you’ve lost everything your service was built on.
Containers are ephemeral by design. Their filesystems vanish on docker rm. Persistent data lives in volumes and bind mounts — but choosing the
wrong approach can leave you with slow storage, broken permissions,
backup gaps, or containers that can’t migrate between hosts.
This guide covers the four Docker storage options available in every homelab — named volumes, bind mounts, tmpfs, and NFS volumes — with practical patterns for databases, configs, media, and logs. You’ll learn which to use, when, and how to back it all up without thinking about it.
Docker Named Volumes — The Default Choice
Named volumes are Docker-managed storage. Docker creates them, tracks
them in its own metadata, and stores them under
/var/lib/docker/volumes/ on the host. You reference them by name, and
Docker handles the path resolution.
Create and use a named volume in Docker Compose:
|
|
Docker creates the volume automatically on docker compose up -d. The
volume persists after docker compose down. Remove it explicitly with:
|
|
When named volumes shine:
- Databases (PostgreSQL, MySQL, MariaDB, MongoDB) — these need fast, Docker-managed storage without host path dependencies
- Secrets and app data generated by the container, not edited from the host
- Multi-node setups — volumes are easier to reference in Swarm or Nomad stacks
When they don’t:
- You need to inspect or edit files directly from the host (bind mounts are better)
- You want explicit control over the filesystem path
- You’re sharing data between multiple containers that need specific directory structures
Named volumes are the default for a reason — they’re portable, Docker-managed, and work across environments without path assumptions.
Bind Mounts — Direct Host Access
Bind mounts map an arbitrary host directory into the container. You control exactly where data lives on the host filesystem.
Bind mount in Docker Compose:
|
|
The :ro flag mounts read-only for security — the container can’t
modify the configuration file.
When bind mounts win:
- Configuration files — edit
nginx.conforprometheus.ymlfrom the host with your text editor - Development — live-reload containers mount your working directory
- Media libraries — point Plex, Jellyfin, or Navidrome at your existing media folders without moving anything
- Log directories — collect container logs into a known host path your log collector watches
Bind mount checklist for the homelab:
- Always use absolute paths to avoid surprises
- Add
:rofor files the container shouldn’t write (config, certs) - Check host permissions — the user ID inside the container must have access to the host directory
- Pre-create directories with correct ownership before starting the container
Named Volumes vs Bind Mounts — When to Use Each
The most common Docker storage question has a straightforward answer:
| Aspect | Named Volume | Bind Mount |
|---|---|---|
| Management | Docker creates, names, destroys | You manage paths manually |
| Backup | Back up /var/lib/docker/volumes/ |
Back up the host directory |
| Permission safety | Docker sets initial ownership to the container user | Host permissions apply directly |
| Editing files | Requires docker exec or root on host |
Direct file access from host |
| Performance | Native Docker (btrfs, overlay2) | Direct host filesystem |
| Portability | Copy volume name to another Docker host | Must replicate path structure |
| Secrets / configs | Awkward | Ideal |
Rule of thumb for the homelab:
Use named volumes for anything the container writes (databases, app state, generated data). Use bind mounts for anything you write (config files, media, development code, log output).
This maps cleanly to real services:
|
|
Tmpfs Mounts — In-Memory Storage
Tmpfs mounts store data in RAM. Data disappears when the container stops. Useful for ephemeral, performance-sensitive data that doesn’t need to survive a reboot.
Example — Redis cache with tmpfs:
|
|
Appropriate uses:
- Redis or Memcached caches (data can be rebuilt)
- Session stores
- Temporary processing directories
- /tmp inside containers (reduces disk writes)
Inappropriate uses:
- Database data files
- Uploaded media
- Configuration state
- Any data that must survive a restart
Tmpfs is a specialized tool — use it when data loss is acceptable and speed matters.
NFS Volumes — Shared Storage Across Hosts
In a multi-node homelab, volumes on one host aren’t available on another. NFS volumes let containers on any host share the same backing storage.
Set up an NFS share on your Proxmox host or NAS:
|
|
Use NFS volumes in Docker Compose:
|
|
Or create NFS volumes from the CLI:
|
|
NFS volume considerations:
- NFSv4 is the minimum for production — older versions lack locking and security
- Network latency adds 1-5 ms per I/O operation — don’t put latency- sensitive databases on NFS
- Use
softmounts to avoid a container hang when the NFS server is unreachable no_root_squashon the server lets container root write files (needed for most containers)- Back up the NFS server, not the individual Docker hosts
Docker Volume Backup — Practical Automation
A volume is only useful if you can restore it. Here’s a backup strategy that covers databases, configs, and app data with minimal overhead.
Backup script for named volumes with restic (or rsync):
|
|
For PostgreSQL databases specifically — use pg_dump for consistent snapshots:
|
|
Restore a volume:
|
|
Automate with a systemd timer:
|
|
|
|
Enable it:
|
|
Volume Cleanup and Lifecycle Management
Unused volumes accumulate. A year into your homelab, you’ll have orphaned volumes from renamed services, abandoned experiments, and failed updates.
Find and prune old volumes:
|
|
Label-based volume management — tag your volumes so you know what’s safe to remove:
|
|
Include volumes in your docker-compose down strategy:
|
|
Only use -v when you’re certain you want to destroy data — it’s
irreversible.
Practical Homelab Storage Layout
A battle-tested directory structure for Docker services on a single Proxmox VM or bare-metal host:
|
|
Corresponding Docker Compose for a service:
|
|
The pattern: bind mounts for configs and media, named volumes for database-backed writes. This gives you easy config editing from the host plus Docker-managed storage for the data that needs it.
Summary: Choose Your Storage by Data Type
| Data type | Storage method | Example |
|---|---|---|
| Databases, app state | Named volume | PostgreSQL data, Prometheus TSDB |
| Configuration files | Bind mount (ro) | nginx.conf, prometheus.yml |
| Media / user uploads | Bind mount | Movies, photos, music |
| Ephemeral cache | Tmpfs | Redis cache, processing temp dirs |
| Multi-node shared data | NFS volume | Jellyfin config, media libraries |
| Logs | Bind mount | Container stdout logs for collection |
Key takeaways:
- Named volumes are the default — use them for anything the container writes. Docker manages permissions, locations, and lifecycle.
- Bind mounts for host-edited files — configs, media, log dirs. Keep absolute paths consistent across environments.
- Back up early, back up often — one
docker compose down -vmistake and your database is gone. Automate volume backups with restic, borg, or rsync on a systemd timer. - Use NFS volumes for multi-node setups — but don’t put latency-sensitive databases on NFS. Reserve it for media and config.
- Label and prune — tag production volumes with
backup=dailyand prune the rest weekly.
The difference between a well-managed homelab and a brittle one comes down to how you handle data persistence. Containers come and go. Volumes are what survive. Get the storage right, and your services will survive Docker upgrades, host migrations, and the occasional “whoops I deleted the stack” moment.