
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 30, 2025
When developing with Docker containers, it’s best practice to store important data in Docker volumes. Docker volumes ensure the data persists since they exist independently of a container’s lifecycle. To enhance the development experience, we can back up a Docker container along with its data volumes. However, including the data volumes in the backup isn’t straightforward.
In this tutorial, we’ll use a practical example to show how we can back up and restore a Docker container along with its data volumes.
Before we proceed, we need to ensure the container and volume exist.
To illustrate, let’s create and start a MySQL container:
$ docker run --name mysql_container -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress -v mysql_data:/var/lib/mysql -d mysql:5.7
Here’s what the command does:
Above, the MySQL container stores its data in the named volume mysql_data.
Next, let’s inspect the volume associated with the specific container mysql_container:
$ docker inspect mysql_container | grep Mounts -A 10
"Mounts": [
{
"Type": "volume",
"Name": "mysql_data",
"Source": "/var/lib/docker/volumes/mysql_data/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
The container mysql_container contains the volume mysql_data for the database.
In this section, let’s back up the container and its associated volume.
To begin, let’s use the docker save command to back up the image:
$ docker save -o mysql_image_backup.tar mysql:5.7
Here’s the breakdown:
The command above creates a TAR archive of the image:
$ ls
mysql_image_backup.tar
Our current working directory now contains mysql_image_backup.tar, the TAR archive of the image, enabling us to restore it later.
In this step, let’s back up all MySQL data files using the tar command. To clarify, we back up the entire MySQL volume:
$ docker run --rm -v mysql_data:/data -v $(pwd):/backup busybox tar cvf /backup/mysql_data_backup.tar /data
This command mounts the MySQL data volume inside a temporary container, compresses its content, and saves it as the TAR archive mysql_data_backup.tar. To clarify, the TAR archive is stored in the current directory.
Here, we combine inspecting the container mysql_container and output redirection:
$ docker inspect mysql_container > mysql_metadata.json
This command backs up the container’s metadata in the JSON file mysql_metadata.json. Thus, we can restore the container with its original settings.
To make the backup process easier, we can automate it with the help of a simple Bash script:
#!/bin/bash
# Set variables
CONTAINER_NAME="mysql_container"
IMAGE_NAME="mysql:5.7"
VOLUME_NAME="mysql_data"
# Backup filenames
IMAGE_BACKUP="mysql_image_backup.tar"
VOLUME_BACKUP="mysql_data_backup.tar"
METADATA_BACKUP="mysql_metadata.json"
echo "Starting backup process..."
# Backup the Docker image
echo "Saving Docker image..."
docker save -o "$IMAGE_BACKUP" "$IMAGE_NAME"
# Backup the data volume
echo "Saving volume data..."
docker run --rm -v "$VOLUME_NAME":/data -v "$(pwd)":/backup busybox tar cvf "/backup/$VOLUME_BACKUP" /data
# Backup container metadata
echo "Saving container metadata..."
docker inspect "$CONTAINER_NAME" > "$METADATA_BACKUP"
echo "Backup completed successfully!"
Let’s make the script executable:
$ chmod +x backup_script.sh
Here’s a sample of the output the script displays:
$ ./backup_script.sh
Starting backup process...
Saving Docker image...
Saving volume data...
...
Backup completed successfully!
Now, we’ve automated backing up the container image as well as its volume data.
Before restoring, let’s remove the existing container, volume, and image to ensure we restore from the backup instead of using the already existing data:
$ docker stop mysql_container && docker rm mysql_container
This command stops and removes the container mysql_container. Next, let’s remove the volume:
$ docker volume rm mysql_data
mysql_data
Further, let’s remove the image:
$ docker rmi mysql:5.7
At this point, we can proceed with the restoration steps.
Here, let’s load the container image:
$ docker load -i mysql_image_backup.tar
Above, we restore the container image from the backup:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 5107333e08a8 15 months ago 501MB
In this output, let’s ensure the image mysql:5.7 is now listed.
Here, we extract all the necessary information from the metadata file mysql_metadata.json instead of manually specifying the environment variables, volume mappings, and other settings:
# Extract container name
CONTAINER_NAME=$(jq -r '.[0].Name' mysql_metadata.json)
# Extract image name
IMAGE_NAME=$(jq -r '.[0].Config.Image' mysql_metadata.json)
# Extract volume mappings
VOLUME_MAPPINGS=$(jq -r '.[0].Mounts | map("-v \(.Name):\(.Destination)") | join(" ")' mysql_metadata.json)
# Extract environment variables
ENV_VARS=$(jq -r '.[0].Config.Env | map("-e " + .) | join(" ")' mysql_metadata.json)
To dynamically construct the docker run command, we use the jq command to extract:
With this addition, we can restore the container with it’s original settings without manually specifying them.
Next, we recreate the volume mysql_data:
$ docker volume create mysql_data
mysql_data
After the command completes, we can start a new MySQL container.
Now, let’s restore the MySQL database by extracting it from the backup:
$ docker run --rm -v mysql_data:/data -v $(pwd):/backup busybox tar xvf /backup/mysql_data_backup.tar -C /
This command copies the backup data in the new volume.
To restore the container’s metadata, let’s dynamically reconstruct the docker run command using the JSON file mysql_metadata.json:
$ RESTORE_CMD="docker run --name $CONTAINER_NAME $ENV_VARS $VOLUME_MAPPINGS -d $IMAGE_NAME"
Let’s have a look at the breakdown:
Let’s now execute the dynamically generated docker run command above stored in the RESTORE_CMD variable:
$ eval "$RESTORE_CMD"
We can now recreate our MySQL container with its original settings.
Finally, let’s restart the container:
$ docker restart mysql_container
mysql_container
At this point, we confirm the Docker container backup and its associated data volume backup work correctly.
Similarly, let’s automate the restoration process:
#!/bin/bash
# Backup filenames
IMAGE_BACKUP="mysql_image_backup.tar"
VOLUME_BACKUP="mysql_data_backup.tar"
METADATA_BACKUP="mysql_metadata.json"
echo "Starting restoration process..."
# Extract container name
CONTAINER_NAME=$(jq -r '.[0].Name' "$METADATA_BACKUP")
# Extract image name
IMAGE_NAME=$(jq -r '.[0].Config.Image' "$METADATA_BACKUP")
# Extract volume mappings
VOLUME_MAPPINGS=$(jq -r '.[0].Mounts | map("-v \(.Name):\(.Destination)") | join(" ")' "$METADATA_BACKUP")
# Extract environment variables
ENV_VARS=$(jq -r '.[0].Config.Env | map("-e " + .) | join(" ")' "$METADATA_BACKUP")
# Stop and remove existing container
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "Stopping and removing existing container..."
docker stop "$CONTAINER_NAME"
docker rm "$CONTAINER_NAME"
fi
# Remove existing volume
for VOL in $(docker volume ls --format '{{.Name}}'); do
if echo "$VOLUME_MAPPINGS" | grep -q "$VOL"; then
echo "Removing existing volume: $VOL"
docker volume rm "$VOL"
fi
done
# Remove existing image
if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^$IMAGE_NAME$"; then
echo "Removing existing image..."
docker rmi "$IMAGE_NAME"
fi
# Restore the Docker image
echo "Loading Docker image..."
docker load -i "$IMAGE_BACKUP"
# Recreate the volumes
for VOL in $VOLUME_MAPPINGS; do
VOL_NAME=$(echo "$VOL" | cut -d ':' -f1 | sed 's/-v //')
echo "Recreating volume: $VOL_NAME"
docker volume create "$VOL_NAME"
done
# Restore the volume data
echo "Restoring volume data..."
docker run --rm $VOLUME_MAPPINGS -v "$(pwd)":/backup busybox tar xvf "/backup/$VOLUME_BACKUP" -C /
# Construct the docker run command
RESTORE_CMD="docker run --name $CONTAINER_NAME $ENV_VARS $VOLUME_MAPPINGS -d $IMAGE_NAME"
# Recreate the container with original metadata
echo "Recreating container..."
eval "$RESTORE_CMD"
# Restart the container
echo "Restarting container..."
docker restart "$CONTAINER_NAME"
echo "Restoration completed successfully!"
Then, we can make the script executable:
$ chmod +x restoration_script.sh
Finally, let’s execute the script:
$ ./restoration_script.sh
Starting restoration process...
Stopping and removing existing container...
mysql_container
mysql_container
Starting restoration process...
Removing existing volume...
mysql_data
Removing existing image...
...
Loading Docker image...
...
Loaded image: mysql:5.7
Recreating volume...
mysql_data
Restoring volume data...
...
Extracting container metadata...
Recreating container...
4dc2f1513734905c6fad0f49748caf45f2cbdc02cf83e6de2060b0df2702fe5e
Restarting container...
mysql_container
Restoration completed successfully!
With the help of this automation script, we streamline how the existing resources are removed, as well as the restoration process.
In this article, we demonstrated how to create a backup for a MySQL container and its associated data volume.
For system failures or accidental deletions, backing up and restoring Docker containers, along with their data volumes, ensures smooth recovery and data safety. First, we created a MySQL container with a named volume. Next, we separately backed up both the container image and its volume. Before the restoration, we removed the existing resources. After that, we reloaded the image, recreated the volume and container, restored the database files, and finally restarted the container.
As a result, we enhance the management of our Docker container backups, ensuring a seamless restoration process.