# TIL: Docker Volume Debugging: Finding Where Your Data Actually Lives
Spent 2 hours debugging why data wasn't persisting. Turns out, understanding Docker volumes is crucial.
The Problem
I had a container with a volume mount, but couldn't figure out where the data was actually stored on the host:
docker run -v mydata:/data myappWhere is mydata? What's inside it?
The Solution
Find Volume Location
# List all volumes
docker volume ls
# Inspect a specific volume
docker volume inspect mydataOutput:
[
{
"CreatedAt": "2024-12-14T10:30:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mydata/_data",
"Name": "mydata",
"Options": {},
"Scope": "local"
}
]The Mountpoint tells you exactly where the data lives!
View Volume Contents (The Trick)
You can't just cd to that path (permission denied). Instead, use a temporary container:
docker run --rm -v mydata:/data alpine ls -la /dataOr for interactive browsing:
docker run --rm -it -v mydata:/data alpine sh
cd /data
ls -laBetter: Named Volume Inspection
Create a simple alias:
# Add to ~/.bashrc or ~/.zshrc
alias dvol='docker run --rm -it -v'Usage:
# Browse any volume interactively
dvol mydata:/data alpine sh
# Quick listing
dvol mydata:/data alpine ls -la /data
# Check file contents
dvol mydata:/data alpine cat /data/config.json
# Copy file out of volume
docker run --rm -v mydata:/data -v $(pwd):/backup alpine cp /data/important.txt /backup/Debugging Bind Mounts
For bind mounts (host path to container):
docker run -v /host/path:/container/path myappTo see what the container actually sees:
docker exec -it container_name ls -la /container/pathCommon Volume Issues
Issue 1: Volume Not Mounting
# Check if volume exists
docker volume ls | grep mydata
# Create it if missing
docker volume create mydataIssue 2: Wrong Permissions
# Check ownership in volume
docker run --rm -v mydata:/data alpine ls -ln /data
# Fix permissions (if needed)
docker run --rm -v mydata:/data alpine chown -R 1000:1000 /dataIssue 3: Dangling Volumes
# List dangling volumes (not used by any container)
docker volume ls -f dangling=true
# Remove them
docker volume prune
# Be careful! This deletes data!Issue 4: Volume vs Bind Mount Confusion
# Named volume (managed by Docker)
-v mydata:/data
# Bind mount (you manage the host path)
-v /host/path:/data
-v $(pwd):/data
# Anonymous volume (Docker creates and manages)
-v /dataPro Tips
1. Backup a Volume
# Backup volume to tar file
docker run --rm \
-v mydata:/data \
-v $(pwd):/backup \
alpine tar czf /backup/mydata-backup.tar.gz -C /data .2. Restore a Volume
# Restore from backup
docker run --rm \
-v mydata:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/mydata-backup.tar.gz -C /data3. Copy Volume to Another
# Copy all data from vol1 to vol2
docker run --rm \
-v vol1:/source:ro \
-v vol2:/dest \
alpine sh -c "cp -av /source/. /dest/"4. Clone a Volume
# Create new volume as copy of existing
docker volume create vol2
docker run --rm \
-v vol1:/source:ro \
-v vol2:/dest \
alpine cp -av /source/. /dest/Real-World Example
I was debugging a database that wasn't persisting data:
# Check if volume exists
docker volume inspect postgres_data
# Error: No such volume
# Ah! The docker-compose.yml had a typo
# It said: postgress_data (3 s's)
# Should be: postgres_data (2 s's)
# Found all volumes
docker volume ls
# Found: postgress_data (the typo!)
# Renamed it
docker volume create postgres_data
docker run --rm \
-v postgress_data:/source \
-v postgres_data:/dest \
alpine cp -av /source/. /dest/
# Removed the typo volume
docker volume rm postgress_dataUseful One-Liners
# Find which containers use a volume
docker ps -a --filter volume=mydata
# Remove all stopped containers' volumes
docker container prune -f && docker volume prune -f
# List volumes with size (requires Docker 20.10+)
docker system df -v
# Find volumes larger than 1GB
docker system df -v | awk '$4 > 1000'The Gotcha I Learned
Docker Compose creates volumes with prefixes:
# docker-compose.yml
volumes:
mydata:Creates volume named: projectname_mydata
To use a specific name:
volumes:
mydata:
name: mydataThis one trick would have saved me those 2 hours. Now you know!
