Skip to content

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.


01

Inspect the Container Mounts

Identify Data

Confirm that app_data is the intended persistent data volume, not a different environment or an anonymous volume.

Terminal window
docker inspect --format '{{json .Mounts}}' app
docker volume ls
docker volume inspect app_data || exit 1
# Prepare a fresh private destination before interrupting the application.
umask 077
BACKUP_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"}]

02

Quiesce the Service Before Copying Files

Consistency

Use 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.

Terminal window
docker compose ps
docker compose stop app || exit 1
docker 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 1
test -z "$running_writers" || { echo "Volume is still in use; stop." >&2; exit 1; }
❯ Illustrative Output (Results Vary)
app Stopped
No running container is using app_data

03

Archive the Read-Only Volume Contents

Backup

Create 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.

Terminal window
# 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 1
fi
❯ Illustrative Output (Results Vary)
backups/app_data.tar.gz created

04

Verify the Archive and Bring the Service Back

Verify Backup

Read 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.

Terminal window
set -euo pipefail
sudo 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 app
docker compose ps app
❯ Illustrative Output (Results Vary)
./
./config/
./uploads/
<sha256 checksum varies> backups/app_data.tar.gz
Container project-app-1 Started
project-app-1 app Up
Actual Compose names and state vary; use the application's health check too.

05

Restore Without Overwriting the Original

Recovery Test

Create 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.

Terminal window
# 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 1
fi
docker volume create app_data_restore || exit 1
docker 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 /restore
docker 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_restore
Files listed: match the expected application data
Application-level recovery test: run with the app's documented procedure

Comments