Back Up and Restore a Docker Named Volume
A Docker named volume survives container replacement, but that does not make it a backup. This guide archives a volume while its application is stopped, checks the archive, and restores it into a second volume so the original data stays intact.
Use a Linux shell and replace the example container and volume names after checking your Compose project. For databases, prefer the database’s native backup tool when possible. Never copy a live database’s files unless its documented backup procedure supports that operation.
Inspect the Container Mounts
Identify DataConfirm that app_data is the intended persistent data volume, not a different environment or an anonymous volume.
docker inspect --format '{{json .Mounts}}' appdocker volume lsdocker volume inspect app_data || exit 1# Prepare a fresh private destination before interrupting the application.umask 077BACKUP_DIR="$PWD/backups"mkdir -m 700 "$BACKUP_DIR" || { echo "Choose a new backup directory." >&2; exit 1; }❯ Illustrative Output (Results Vary)
[{"Type":"volume","Name":"app_data","Destination":"/var/lib/app"}]Quiesce the Service Before Copying Files
ConsistencyUse the normal shutdown procedure so the application flushes pending writes. Confirm the Compose service name and stop only that service. Check that no other container is writing to the volume.
docker compose psdocker compose stop app || exit 1docker ps --filter volume=app_data# Stop if any running container still uses this volume; identify its owner.running_writers=$(docker ps -q --filter volume=app_data) || exit 1test -z "$running_writers" || { echo "Volume is still in use; stop." >&2; exit 1; }❯ Illustrative Output (Results Vary)
app StoppedNo running container is using app_dataArchive the Read-Only Volume Contents
BackupCreate a host backup directory and use a temporary helper container. Mount the volume read-only and make only the backup directory writable. Alpine includes BusyBox tar; pin the helper image by digest in a managed environment.
# Use the private BACKUP_DIR reserved in Step 1, in this same shell.docker run --rm --mount type=volume,src=app_data,dst=/source,readonly --mount "type=bind,src=$BACKUP_DIR,dst=/backup" alpine:3.23 sh -c 'umask 077; exec tar -czf /backup/app_data.tar.gz -C /source .'if test "$?" -ne 0; then echo "Backup failed; retain the source and follow the service restart procedure." >&2 exit 1fi❯ Illustrative Output (Results Vary)
backups/app_data.tar.gz createdVerify the Archive and Bring the Service Back
Verify BackupRead the entire compressed archive, inspect a few filenames, and record a checksum. Then start the original Compose service with its original volume and confirm its state. If archive verification fails, restart the original service and investigate before attempting recovery.
set -euo pipefailsudo test -s "$BACKUP_DIR/app_data.tar.gz"sudo tar -tzf "$BACKUP_DIR/app_data.tar.gz" > "$BACKUP_DIR/app_data.contents.txt"head -n 5 "$BACKUP_DIR/app_data.contents.txt"sudo sha256sum "$BACKUP_DIR/app_data.tar.gz" | tee "$BACKUP_DIR/app_data.sha256"docker compose start appdocker compose ps app❯ Illustrative Output (Results Vary)
././config/./uploads/<sha256 checksum varies> backups/app_data.tar.gzContainer project-app-1 Startedproject-app-1 app UpActual Compose names and state vary; use the application's health check too.Restore Without Overwriting the Original
Recovery TestCreate a separate volume and extract the archive into it. Inspect the restored files and test them using the application’s documented recovery procedure. This basic tar example does not preserve every ACL, extended attribute, or SELinux label; use an application-approved metadata-aware backup method when those are required. Do not point the production service at the restore volume until the owner approves a cutover.
# Refuse an existing restore volume: extraction must not merge with older data.if docker volume inspect app_data_restore >/dev/null 2>&1; then echo "Restore volume already exists; choose a new name." >&2 exit 1fidocker volume create app_data_restore || exit 1docker run --rm --mount type=volume,src=app_data_restore,dst=/restore --mount "type=bind,src=$BACKUP_DIR,dst=/backup,readonly" alpine:3.23 tar -xzf /backup/app_data.tar.gz -C /restoredocker run --rm --mount type=volume,src=app_data_restore,dst=/restore,readonly alpine:3.23 find /restore -maxdepth 2 -type f -print❯ Illustrative Output (Results Vary)
Restore volume: app_data_restoreFiles listed: match the expected application dataApplication-level recovery test: run with the app's documented procedure