Docker makes it easy to recreate containers and very easy to forget the data they write. This Docker backup volume data guide focuses on one task: how to back up the data in Docker volumes automatically on every host, with a script you can read, a schedule, retention and an offsite copy, and how to restore it into a new volume when something goes wrong.
The problem: containers are disposable, volume data is not
Images can be pulled again and containers can be recreated from a Compose file in seconds. The data inside volumes cannot: uploaded files, application state, search indexes, message queues and databases exist only there. A docker compose down -v, a docker volume prune run in the wrong place, a failed disk or a compromised host deletes that data for good.
Manual backups do not solve this, because they stop happening after the first busy week. The consequence is discovered on the worst possible day: the last copy is months old, or there is none. The solution is an automated job that finds every volume, archives it safely and sends it to another location.
Step 1: find all volume data on the host
# Named volumes managed by Docker
docker volume ls
# Which container uses which volume or bind mount
docker ps -a --format '{{.Names}}' | while read -r c; do
echo "== $c"
docker inspect -f '{{range .Mounts}}{{.Type}} {{.Name}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' "$c"
done
Write down three groups:
- Named volumes with files (uploads, application data): archive them with the script below.
- Bind mounts (host directories such as
/srv/app/data): back them up as normal directories with your server backup tool. - Database volumes (PostgreSQL, MySQL, MariaDB, MongoDB, Redis): do not archive the files of a running database. Use a dump instead, as explained in Docker Compose Postgres backup.
Step 2: a script that backs up every volume
The script below archives each named volume with a temporary read-only container. Volumes listed in STOP_FOR have their containers stopped for the few seconds the archive takes, which guarantees a consistent copy for applications that keep files open.
#!/usr/bin/env bash
# /usr/local/sbin/docker-volume-backup.sh
set -euo pipefail
DEST=/var/backups/docker-volumes
KEEP_DAYS=14
STAMP=$(date +%F_%H%M)
SKIP_REGEX='^(pgdata|mysql_data)$' # database volumes: use dumps instead
STOP_FOR='app_uploads' # volumes whose containers are stopped first
mkdir -p "$DEST/$STAMP"
for vol in $(docker volume ls -q); do
if [[ "$vol" =~ $SKIP_REGEX ]]; then
echo "skip $vol (database volume)"
continue
fi
stopped=""
if [[ " $STOP_FOR " == *" $vol "* ]]; then
stopped=$(docker ps -q --filter "volume=$vol")
if [ -n "$stopped" ]; then docker stop $stopped > /dev/null; fi
fi
docker run --rm \
-v "$vol":/data:ro \
-v "$DEST/$STAMP":/backup \
alpine tar czf "/backup/$vol.tar.gz" -C /data .
if [ -n "$stopped" ]; then docker start $stopped > /dev/null; fi
echo "ok $vol $(du -h "$DEST/$STAMP/$vol.tar.gz" | cut -f1)"
done
# Retention: remove backup folders older than KEEP_DAYS
find "$DEST" -mindepth 1 -maxdepth 1 -type d -mtime +"$KEEP_DAYS" -exec rm -rf {} +
Key points:
- The volume is mounted with
:ro, so the backup container can never change the data. tarruns as root inside the container and keeps file ownership and permissions, which matters for images that run as a specific user ID.set -euo pipefailstops the script on the first error instead of producing empty archives silently. If a step fails after a container was stopped, start it manually and check the log.
Step 3: schedule it
A systemd timer is easier to monitor than cron, and runs missed jobs after a reboot:
# /etc/systemd/system/docker-volume-backup.service
[Unit]
Description=Back up Docker volume data
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/docker-volume-backup.sh
# /etc/systemd/system/docker-volume-backup.timer
[Unit]
Description=Daily Docker volume backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
chmod 700 /usr/local/sbin/docker-volume-backup.sh
systemctl daemon-reload
systemctl enable --now docker-volume-backup.timer
systemctl list-timers docker-volume-backup.timer
journalctl -u docker-volume-backup.service -n 50
If you prefer cron, the equivalent line is 0 3 * * * /usr/local/sbin/docker-volume-backup.sh >> /var/log/docker-volume-backup.log 2>&1.
Step 4: copy the volume data offsite
Archives in /var/backups live on the same disk as the volumes. Send them to another location with a tool that encrypts and deduplicates, for example restic with an S3-compatible bucket:
export RESTIC_REPOSITORY="s3:https://s3.example.com/my-bucket/docker-host-01"
export RESTIC_PASSWORD_FILE=/root/.restic-password
export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
restic backup /var/backups/docker-volumes /opt/stacks # data + compose files
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Include the Compose files, .env files and reverse proxy configuration: volume data without the stack definition makes the restore much slower. Keep the restic password outside the server too, or the backup cannot be opened after a total loss.
Step 5: restore volume data into a new volume
docker volume create app_uploads_restored
docker run --rm \
-v app_uploads_restored:/data \
-v /var/backups/docker-volumes/2026-10-25_0300:/backup:ro \
alpine sh -c "cd /data && tar xzf /backup/app_uploads.tar.gz"
Point a test copy of the stack at the restored volume, check the application, then switch production over or copy the data back. Restoring into a new volume first means a bad archive never overwrites the data you still have.
Prefer a ready-made tool?
If you would rather configure than script, the open source offen/docker-volume-backup image runs as a container in your Compose stack, handles schedules, retention, stopping containers and uploads to S3 or other storage. The concepts in this guide apply to it as well.
Checklist
- List every volume and bind mount on each host.
- Dump databases; archive other volumes read-only.
- Schedule the job and check its logs or alerts.
- Copy archives and configuration offsite, encrypted.
- Restore one volume into a new volume every month.
For the methods behind this script, read how to back up Docker volumes safely and Docker backup best practices. Backnix is being built to discover Docker volumes and back them up offsite automatically: see the Docker backup page for the current status and the plans.