Backup of virtual machines has long been the industry standard. While Docker is not a direct alternative to virtual machines, there are many similarities between them. So, how do you handle backup Docker containers?
In this article, we will explore various approaches to backup Docker containers and their specific features.
What should be backed up in Docker?
Let’s start with the purpose of a backup. A backup is needed to restore the system to its desired state as quickly as possible.
The philosophy of a Docker container assumes that it is easily restartable. Inside the container is the executable application code, while all its settings are either specified in the container’s launch command (via environment variables) or stored in a volume.
Therefore, for successful recovery, you need two things:
- The command or configuration used to launch the container.
- A backup of all the container’s volumes.
Before the backup
In any recovery scenario, you will need to restart the container from scratch: specify the container name, map ports, define environment variables, configure network settings, and set resource limits.
Therefore, it’s essential to save the container’s startup command — whether it’s a docker-compose.yml
file or a simple docker run
command. Ideally, you should store the Docker configuration in a version control system like Git. At the very least, place the instructions for starting the container alongside the backup.
Important: backup a database in a Docker container
If you need to backup a database located in a Docker container, you don’t need to backup the container itself or the volume where the database data is stored. Instead, you should back up the database itself using the method recommended by the database developer, as it may vary depending on the database type.
Let’s take a closer look at backup MySQL and PostgreSQL databases.
Backup and restore MySQL database in Docker container
You can back up a MySQL database located in a Docker container using the mysqldump
utility, which is already included in the Docker container. By the way, SqlBak supports backup databases in containers.
Example:
docker exec my-mysql mysqldump -uroot -pMyPassword my_database > backup.sql
This command creates a database dump file named backup.sql
in the directory where the command was executed.
To restore the backup from scratch, create a MySQL container using the same command as the original one:
docker run --name my-mysql-2 -e MYSQL_ROOT_PASSWORD=MyPassword -d mysql:latest
Create a database:
docker exec -i my-mysql-2 mysql -uroot -pMyPassword -e "CREATE DATABASE my_database;"
docker exec -i my-mysql-2 mysql -uroot -pMyPassword my_database < backup.sql
Backup and restore PostgreSQL database in Docker container
A backup of a PostgreSQL database can be created using the pg_dump
utility, which is available directly in the container with the database. The backup is created as follows:
docker exec -t my_postgres pg_dump -U postgres my_database > backup.sql
This command created a database dump file named backup.sql
in the directory where the command was executed.
To restore the database, deploy the container with the database:
docker run --name my_postgres_2 -e POSTGRES_PASSWORD=MyPassword -d postgres
docker exec -it my_postgres_2 psql -U postgres -c "CREATE DATABASE my_database;"
And perform the restore:
docker exec -i my_postgres_2 psql -U postgres my_database < backup.sql
About backup consistency
If a container is not stopped during a backup, the backup of the container or its volumes will not be consistent. This means files might be copied while they are being written to. For a directory of photos, this might not be a big issue — you might just lose a couple of files. However, a database file copied during its operation could become corrupted and impossible to restore.
For this reason, it’s better to either stop containers that hold databases or use the database’s built-in backup tools, which don’t require stopping the container.
Backup Docker volume
In essence, both backing up and restoring a Docker volume is simply a matter of copying files. You can directly copy the volume itself. However, the best practice recommended in the Docker documentation is to create a temporary container and share the volumes with it using the --volumes-from
parameter. This approach is universal, as it operates at Docker’s abstraction level and does not depend on the actual location of the volumes.
First, identify the list of volumes used by the container. This command will display both anonymous and named volumes:
docker inspect -f '{{range .Mounts}}{{.Destination}} {{end}}' <container_name>
Optionally, stop the container if it is important to avoid file corruption during the copying process:
docker stop <container_name>
For each volume from the list, create a backup using a temporary container with the following command:
docker run --rm --volumes-from <container_name> -v $(pwd):/backup ubuntu bash -c "cd / && tar cf /backup/backup.tar <volume_path>"
This command creates a temporary container (the --rm
parameter ensures the container is removed after execution) that has access to all volumes of the target container (thanks to the --volumes-from <container_name>
parameter). It then uses tar cf
to archive the <volume_path>
with the directory hierarchy preserved from the root and saves it as /backup/backup.tar
, which is mapped to the current directory on the host machine.
docker start <container_name>
A simple script to backup all volumes of a container
On Linux, create a backup script:
nano backup-container.sh
#!/bin/bash CONTAINER_NAME="" DATE=$(date +"%Y-%m-%d") VOLUMES=$(docker inspect -f '{{range .Mounts}}{{.Destination}} {{end}}' $CONTAINER_NAME) if [ -z "$VOLUMES" ]; then echo "No volumes found for container $CONTAINER_NAME." exit 1 fi echo "Stopping container $CONTAINER_NAME..." docker stop $CONTAINER_NAME BACKUP_DIR="./backup_$DATE" mkdir -p $BACKUP_DIR for VOLUME in $VOLUMES; do VOLUME_NAME=$(basename $VOLUME) echo "Backing up $VOLUME to $BACKUP_DIR/${VOLUME_NAME}_backup_$DATE.tar" docker run --rm --volumes-from $CONTAINER_NAME -v $(pwd):/backup ubuntu bash -c "cd / && tar cf /backup/$BACKUP_DIR/${VOLUME_NAME}_backup_$DATE.tar .$VOLUME" done echo "Starting container $CONTAINER_NAME..." docker start $CONTAINER_NAME echo "Backup complete. Files are in $BACKUP_DIR."
In this script, the volumes of the container specified in the CONTAINER_NAME
variable will be backed up into a directory named with the current date. This simplifies file organization during restore.
Allow the script to run by executing:
chmod +x backup-container.sh
And run it by executing:
bash backup-container.sh
Restore Docker volume
docker run -v /<my_user_defined_volume> --name <new_container_name> <image_name>
After that, stop the container immediately:
docker stop <new_container_name>
Then, using a temporary container, restore each volume with the following command:
docker run --rm --volumes-from <new_container_name> -v $(pwd):/backup ubuntu bash -c "cd / && tar xvf /backup/backup.tar"
docker start <new_container_name>
Example of backup and restore a volume
As an example, let’s set up Jenkins in Docker, perform a backup, and then restore it.
Run the following command to start Jenkins:
docker run -d --name jenkins_server -p 8080:8080 -p 50000:50000 -v /builds:/custom_builds jenkins/jenkins:lts
Now you can configure Jenkins, set up a login and password, install plugins, save data to the /builds
directory, and so on.
To perform a backup, first identify the volumes associated with the jenkins_server
container.
docker inspect -f '{{ json .Mounts }}' jenkins_server | jq '.[].Destination'
Here you can see that the Jenkins container has two volumes: the one we explicitly specified, /custom_builds
, and the one defined by Jenkins itself, /var/jenkins_home
.
Backup each of these volumes:
docker run --rm --volumes-from jenkins_server -v $(pwd):/backup ubuntu bash -c "cd / && tar cf /backup/jenkins_home.tar /var/jenkins_home"
docker run --rm --volumes-from jenkins_server -v $(pwd):/backup ubuntu bash -c "cd / && tar cf /backup/custom_builds.tar /custom_builds"
To restore, start another Jenkins server using the same command but on a different port:
docker run -d --name jenkins_server -p 8082:8080 -p 50002:50000 -v /builds2:/custom_builds jenkins/jenkins:lts
sudo docker stop jenkins_server_2
And restore the volumes:
docker run --rm --volumes-from jenkins_server_2 -v $(pwd):/backup ubuntu bash -c "rm -rf /var/jenkins_home/* && cd / && tar xvf /backup/jenkins_home.tar"
docker run --rm --volumes-from jenkins_server_2 -v $(pwd):/backup ubuntu bash -c "rm -rf /custom_builds/* && cd / && tar xvf /backup/custom_builds.tar"
Start the container:
sudo docker start jenkins_server_2
Backup a Docker container
In addition to backup volumes and environment variables, you can also backup the container itself. This ensures a quick and reliable restore of the entire setup as it was.
Should you backup the container itself?
If you are 100% certain that all data and settings are stored on the host system, then backing up the container itself is unnecessary.
However, if you use the Docker container like a virtual machine and/or make changes to the container itself — such as updating a library — then it makes sense to backup the container.
Backup containers is only possible by creating a new image. During restore, a new image is created, which can lead to confusion and might prevent you from updating the version of your original image. Therefore, restore the container itself should be considered a quick solution, with the option to tidy up the images afterward once everything is operational.
How to backup a Docker container
If you need a consistent backup, stop the container first:
sudo docker stop <container_name>
Then create a new image based on the container using the commit
command:
docker commit <container_id> <container_id>_backup
After that, you can export this image to a file using the docker save
command:
docker save -o <container_id>_backup_image.tar <container_id>_backup
Now you can remove the temporary image and start the container:
docker image rm <container_id>_backup sudo docker start <container_name>
Restore a container:
When restore a container, a new image will be created. This may not be ideal, so this solution should only be used to quickly restore the system. Afterward, you can consider how to transition back to the original image.
First, load the backup using the docker load
command:
docker load -i <container_id>_backup_image.tar
Start the container with the same parameters as the original:
docker run -v /<my_user_defined_volume> -p <host-port>:<container-port> --name <container_name> <container_id>_backup
And stop it immediately:
docker stop <container_name>
Restore the volume as described above.
Now you can start the container:
docker start <container_name>
General Advice
Do not store backups on the same server where you are creating them. The best option currently is to store backups in cloud storage solutions designed specifically for backups, such as AWS S3 or Azure. These services offer specialized plans and features for backup storage. You can transfer backups to these storage solutions using command-line utilities.
Most importantly, simply setting up backups is not enough. Be sure to regularly test the restore process, at least in a test environment. During testing, create a simple instruction manual for restore your backups and store it alongside the backups. When something goes wrong, you may be in a state of panic, and this instruction manual will help you avoid mistakes and restore everything as quickly as possible.