KL, MY
7:30:00 AM

A quick guide to backing up Docker named volumes manually and automatically using best practices.
January 16, 2024(Last update at April 16, 2025)
5 min read
So, you’ve got your containerized service up and running—everything works flawlessly for months, maybe even years. Then one day, your server becomes inaccessible, and your service goes down. After troubleshooting, you discover the cause: a failed storage drive. Now, you’re either spending time and money to recover it—or accepting that your data is gone forever.
One of the main reasons to run a homelab is to take control of your own data. That also means taking responsibility for protecting it. In this article, we’ll cover how to back up Docker container data stored in named volumes—and why that’s a little different than working with bind mounts.
If your container stores data using bind mounts, you can easily back it up by tarring the folder and transferring it via scp or sftp.
However, named volumes are managed by Docker itself and not directly tied to a visible host path. Simply tarring their directories is not best practice. Instead, there’s a safer and more structured way to back them up.
To back up a named volume, we’ll spin up a temporary container that:
It is recommended that you stop the container before you backup your named volume.
On Linux:
docker run --rm -v "VOLUME_NAME:/tmp" \ -v "HOST_BACKUP_DIR:/backup" \ ubuntu tar -czvf /backup/BACKUP_FILE.tar /tmpOn Windows (WSL2):
docker run --rm -v "VOLUME_NAME:/tmp" ` -v "HOST_BACKUP_DIR:/backup" ` ubuntu tar -czvf /backup/BACKUP_FILE.tar /tmpVOLUME_NAME – your Docker named volume (check with docker volume ls)HOST_BACKUP_DIR – where to save the backup file, e.g., $HOME/dockerBACKUP_FILE.tar – name your backup clearly and include the dateI will be using Firefly III as an example as I use this everyday. The example will be in Linux (Ubuntu):
docker run --rm -v "firefly_iii_db:/tmp" \ -v "$HOME/docker/firefly/backup:/backup" \ ubuntu tar -czvf /backup/firefly_db_2024_01_04.tar /tmpThis command:
firefly_iii_db to /tmp in the container$HOME/docker/firefly/backup to /backup/tmp and saves it to the backup directoryTo restore from the backup, simply reverse the process:
docker run --rm -v "VOLUME_NAME:/recover" \ -v "HOST_BACKUP_DIR:/backup" \ ubuntu tar -xvf /backup/BACKUP_FILE.tar -C /recover --strip 1Just like with backups, stop any running containers that are using the volume before restoring it.
Manual backups are great—but automated backups are better. You shouldn’t rely on your memory to protect your data.
For this, we’ll use offen/docker-volume-backup. This Docker image can:
backup: # In production, it is advised to lock your image tag to a proper # release version instead of using `latest`. # Check https://github.com/offen/docker-volume-backup/releases # for a list of available releases. container_name: {SERVICE_NAME}_backup image: offen/docker-volume-backup:v2 environment: BACKUP_FILENAME: {SERVICE_NAME}-backup-%Y-%m-%dT%H-%M-%S.tar.gz BACKUP_PRUNING_PREFIX: {SERVICE_NAME}-backup- BACKUP_RETENTION_DAYS: 21 AWS_S3_BUCKET_NAME: {AWS_BUCKET_NAME} AWS_S3_PATH: {SERVICE_NAME} AWS_ACCESS_KEY_ID: {AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY: {AWS_SECRET_ACCESS_KEY} GPG_PASSPHRASE: {PASSPHRASE_TO_ENCRYPT_YOUR_FILE} BACKUP_STOP_CONTAINER_LABEL: {SERVICE_NAME} restart: always volumes: - {VOLUME_NAME_TO_BACKUP}:/backup/{CONTAINER_NAME}:ro - {VOLUME_NAME_TO_BACKUP}:/backup/{CONTAINER_NAME}:ro # Mounting the Docker socket allows the script to stop and restart # the container during backup. You can omit this if you don't want # to stop the container. In case you need to proxy the socket, you can # also provide a location by setting `DOCKER_HOST` in the container - /var/run/docker.sock:/var/run/docker.sock:ro # If you mount a local directory or volume to `/archive` a local # copy of the backup will be stored there. You can override the # location inside the container by setting `BACKUP_ARCHIVE`. # You can omit this if you do not want to keep local backups. - ./backups:/archive{SERVICE_NAME}: container_name: {MY_SERVICE_APP_NAME} image: {MY_SERVICE_IMAGE} restart: always labels: // the {SERVICE_NAME} should be the same as the one you specify // in the backup container environment - BACKUP_STOP_CONTAINER_LABEL - docker-volume-backup.stop-during-backup={SERVICE_NAME}📆 By default, backups run daily at 12:00 AM UTC. You can customize this with the TIMEZONE variable.
To manually trigger a backup:
docker exec <container_ref> backupdocker-volume-backupRestoration is still a manual step. The process is identical to the manual restore described above.
If your backup is encrypted (ends in .gpg), decrypt it first:
gpg -o {TAR_FILE_NAME}.gz -d {TAR_FILE_NAME}.gz.gpgThen restore it using the tar command.
To include multiple volumes in a single backup file, just map them into subdirectories:
backup: container_name: firefly_iii_backup image: offen/docker-volume-backup:v2 environment: ..... restart: always volumes: - db:/backup/db:ro - upload:/backup/upload:ro - /var/run/docker.sock:/var/run/docker.sock:ro - ./backups:/archiveTo restore the db volume:
docker run --rm -v "firefly_iii_db:/recover" \ -v "$HOME/docker/firefly:/backup" \ ubuntu tar -xvf /backup/{TAR_VOLUME_NAME}.gz \ --strip-components=2 -C /recover /backup/dbTo restore upload volume:
docker run --rm -v "firefly_iii_upload:/recover" \ -v "$HOME/docker/firefly:/backup" \ ubuntu tar -xvf /backup/{TAR_VOLUME_NAME}.gz \ --strip-components=2 -C /recover /backup/uploadYou now know how to:
Following the 3-2-1 backup rule may seem like overkill—but at the very least, aim to store your backups in two different locations.
Thanks for reading, and happy self-hosting!