Docker volumes hold the data your containers cannot recreate: databases, uploaded files, application state. Backing them up looks as simple as copying a directory, but doing it while containers are running can produce copies that will not restore. This guide explains how volumes work and how to back them up safely.
What lives where in a Docker host
Before deciding what to back up, separate the four kinds of things on a Docker host:
- Images are read-only templates. They come from a registry or are built from a Dockerfile. You can usually download or rebuild them, so they rarely need a backup.
- Containers are running instances of images with a thin writable layer. Anything written to that layer disappears when the container is removed.
- Configuration is what describes your stack:
docker-compose.ymlfiles,.envfiles, secrets, reverse proxy configuration. It is small but essential. - Volumes and bind mounts are where persistent data lives. This is what this article is about.
Named volumes versus bind mounts
| Type | Where data lives | Example |
|---|---|---|
| Named volume | Managed by Docker, usually under /var/lib/docker/volumes/<name>/_data | -v pgdata:/var/lib/postgresql/data |
| Bind mount | Any path on the host that you choose | -v /srv/app/uploads:/app/uploads |
| tmpfs mount | Memory only, lost on stop | --tmpfs /tmp |
To see which volumes and mounts a container uses:
docker volume ls
docker inspect --format '{{ json .Mounts }}' my-container | python3 -m json.tool
Bind mounts are easy to back up with any file backup tool because they are regular directories. Named volumes can also be read from the host path, but the location is an implementation detail of Docker (and differs with rootless Docker or Docker Desktop), so it is cleaner to access them through a container.
The consistency problem
Copying files while an application writes to them can produce a copy where some files are from before a change and others from after it. For static files such as uploaded images, this is usually harmless. For databases, it is dangerous: PostgreSQL, MySQL, MariaDB, MongoDB and Redis keep data across several files and in memory. A file-level copy of their data directory taken while they run may fail to start or silently contain inconsistent data.
There are three safe approaches, in order of preference for databases:
- Use the database's own dump or backup tool (
pg_dump,mysqldump/mariadb-dump,mongodump, RedisBGSAVE). - Stop the container, copy the volume, start the container again.
- Use a storage-level snapshot (LVM, ZFS, Btrfs) that captures all files at the same instant, then copy from the snapshot. The result is crash-consistent, like a power loss, which most modern databases recover from, but it is not as safe as a proper dump.
Method 1: archive a volume with a temporary container
This is the method described in Docker's documentation. A short-lived container mounts the volume and a host directory, and creates a tar archive:
# Back up the named volume "app_uploads" to ./backups/app_uploads-DATE.tar.gz
mkdir -p backups
docker run --rm \
-v app_uploads:/data:ro \
-v "$(pwd)/backups":/backup \
alpine tar czf /backup/app_uploads-$(date +%F).tar.gz -C /data .
Mounting the volume read-only (:ro) guarantees the backup container cannot modify it. For volumes used by databases, stop the application first, or use Method 2 instead.
To restore into a new or empty volume:
docker volume create app_uploads_restored
docker run --rm \
-v app_uploads_restored:/data \
-v "$(pwd)/backups":/backup \
alpine sh -c "cd /data && tar xzf /backup/app_uploads-2026-10-19.tar.gz"
File ownership is preserved by tar when run as root inside the container, which matters for images that run as a specific user ID.
Method 2: dump databases running in containers
Run the dump tool inside the database container, so you use the same version as the server:
# PostgreSQL: custom-format dump of database "app"
docker exec app-db pg_dump -U app -Fc app > backups/app-$(date +%F).dump
# MySQL / MariaDB: consistent logical dump of InnoDB tables
docker exec app-mysql sh -c 'exec mysqldump --single-transaction --routines --events \
-uroot -p"$MYSQL_ROOT_PASSWORD" appdb' | gzip > backups/appdb-$(date +%F).sql.gz
With MariaDB images, the equivalent client is mariadb-dump. Do not add -t to docker exec when redirecting a dump to a file: allocating a terminal can alter the output stream and corrupt binary formats. Read more about consistency options in our MySQL and PostgreSQL guides.
Method 3: stop, copy, start
For applications without a dump tool, a short stop guarantees consistency:
docker compose stop app
docker run --rm -v myproject_appdata:/data:ro -v "$(pwd)/backups":/backup \
alpine tar czf /backup/appdata-$(date +%F).tar.gz -C /data .
docker compose start app
Keep the stop window short by preparing everything before stopping, and schedule it outside peak hours.
Do not forget the configuration
A volume backup without the matching configuration forces you to reconstruct how the stack was started. Back up together with the data:
- Compose files and override files;
.envfiles and secrets (encrypted, because they contain credentials);- reverse proxy and TLS configuration;
- the exact image tags or digests in use. Avoid relying on
latest: a restore months later may pull an incompatible version.
Storing the backups
An archive sitting next to the volume on the same disk protects only against accidental deletion. Copy backups to another machine and to offsite storage, encrypt them if they leave your network, and apply a retention policy (for example, 7 daily, 4 weekly and 6 monthly copies). The 3-2-1-1-0 rule is a good framework.
Testing restores
Schedule a regular test where you restore a volume into a fresh one, start a copy of the stack against it on an isolated network or another host, and check that the application works. For databases, restore the dump into a new container and run a few queries that you know the expected answers to, such as row counts of key tables.
Common mistakes
- Backing up
/var/lib/dockerwholesale while Docker is running, which captures images and layers you do not need and databases in an inconsistent state. - Forgetting bind mounts that are not under the project directory.
- Backing up only images and assuming data is inside them.
- Keeping the only backup on the same host.
- Restoring a database volume into a newer major version of the database image, which may require an upgrade procedure.
Next steps
- Run
docker volume lsanddocker inspecton every host and list all persistent data. - For each database container, set up a scheduled dump.
- Archive the remaining volumes with a temporary container and copy everything offsite.
- Add the configuration files and pin your image tags.
- Read Docker backup best practices for production servers to automate the process.
Backnix is designed to discover volumes and back up Docker hosts consistently. See the Docker backup page for the current status.