Databases and uploads must outlive containers, which are meant to be disposable.
A container's writable layer disappears when the container is removed, and writing heavily to it is slow. For data that must persist, such as a database's files, use a volume: storage managed by Docker, living outside any container, mounted at a path inside it.
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:17Remove and recreate the db container (to upgrade the image, say) with the same -v pgdata:... and the data is still there.
The more explicit --mount syntax says the same thing and is easier to read in scripts: --mount type=volume,src=pgdata,dst=/var/lib/postgresql/data.
Things to know:
- If you mount a volume that doesn't exist, Docker creates it.
- When an empty volume is mounted over a directory that has content in the image, Docker copies that content into the volume first. Handy for seeding config.
-v /datawith no name creates an anonymous volume with a random ID. They're removed along with the container if you used--rm(ordocker rm -v); otherwise they linger.- Volumes can be shared by several containers at once; append
:roto mount read-only. docker volume ls,inspect,rmandprunemanage them. Removing containers never removes named volumes, which is good for data and bad for disk space.
To back up a volume, mount it into a throwaway container together with a host folder and tar it up. That works the same everywhere, including Docker Desktop, where volume files live inside the VM rather than on your Mac or PC.
Example
docker volume create pgdata
docker run -d --name db -e POSTGRES_PASSWORD=dev \
--mount type=volume,src=pgdata,dst=/var/lib/postgresql/data postgres:17
docker rm -f db # container gone...
docker run -d --name db -e POSTGRES_PASSWORD=dev \
-v pgdata:/var/lib/postgresql/data postgres:17 # ...data still there
# Back up the volume to ./pgdata.tgz
docker run --rm -v pgdata:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/pgdata.tgz -C /data .
docker volume ls
docker volume inspect pgdata --format '{{.Mountpoint}}'Remember
- Volumes are Docker-managed storage that outlives containers
- -v name:/path or --mount type=volume,src=...,dst=...
- Empty volumes are pre-filled from the image's content at that path
- Back up with a throwaway container that tars the volume
Watch out for
- Running a database without a volume, then losing all data on docker rm.