Container Volumes: Persistent Storage for Docker and Podman
Containers are ephemeral — when they're removed, data is lost. Volumes solve this by persisting data outside the container lifecycle.
The Problem
Containers are designed to be disposable. Stop a container, remove it, start a new one. But what about the data inside?
Without volumes, database data, user uploads, and configuration changes are lost when a container is replaced.
Volume Types
Named Volumes
Managed by Docker/Podman. Data stored in a location managed by the container runtime.
docker volume create mydata
docker run -v mydata:/app/data myimage
Bind Mounts
Mount a host directory into the container.
docker run -v /host/path:/container/path myimage
tmpfs Mounts
In-memory storage. Fast but lost on container stop.
docker run --tmpfs /app/cache myimage
Common Patterns
Database Data
Always use a named volume for database storage:
-v postgres_data:/var/lib/postgresql/data
Never use a bind mount for databases — filesystem semantics can cause data corruption.
Application Uploads
Bind mount for user uploads (easy to back up and serve):
-v /data/uploads:/app/uploads
Configuration Files
Bind mount individual config files:
-v ./nginx.conf:/etc/nginx/nginx.conf:ro
The :ro flag makes it read-only inside the container.
Permission Issues
The #1 volume problem. Container runs as UID 1000, host directory owned by root.
Solutions:
1. Match UIDs: Run container with --user matching host UID
2. chown: Change host directory ownership to match container user
3. Podman rootless: Podman maps UIDs automatically in rootless mode
Backup Strategy
Named Volumes
docker run --rm -v mydata:/data -v /backups:/backup alpine tar czf /backup/mydata.tar.gz /data
Bind Mounts
Back up the host directory directly with rsync, Restic, or Borg.
On TinyPod
TinyPod manages volumes for each application automatically. Data persists across container updates and restarts. Backups are handled by the platform.