Skip to content

Instantly share code, notes, and snippets.

@Guiorgy
Last active October 19, 2025 11:21
Show Gist options
  • Select an option

  • Save Guiorgy/699361aea50d1126c636f6e880123af8 to your computer and use it in GitHub Desktop.

Select an option

Save Guiorgy/699361aea50d1126c636f6e880123af8 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <https://unlicense.org>
# NOTE: Deprecated in favor of https://github.com/Guiorgy/docker-vackup
# This is a wrapper for vackup: https://github.com/BretFisher/docker-vackup
# Installation:
# sudo curl -sSL https://raw.githubusercontent.com/BretFisher/docker-vackup/main/vackup -o /usr/local/bin/vackup && sudo chmod +x /usr/local/bin/vackup
# Based on PR #6: https://github.com/BretFisher/docker-vackup/pull/6
# This script stops all running containers, backs up the volumes of all
# containers (including those that were not running), and restarts the
# containers that were stopped
vackup="/usr/local/bin/vackup"
date="/usr/bin/date"
docker="/usr/bin/docker"
mkdir="/usr/bin/mkdir"
ls="/usr/bin/ls"
grep="/usr/bin/grep"
tail="/usr/bin/tail"
xargs="/usr/bin/xargs"
# The root directory where backup directories with the current date will be created
backup_root_directory="$1"
if [ -z "$backup_root_directory" ]; then
echo 1>&2 'Must provide path to the backup destination'
exit 1
fi
# The number of backups to keep. When exceeded, delete old backups
number_of_backups_to_keep="$2"
if [ -z "$number_of_backups_to_keep" ]; then
echo 1>&2 'Must provide the number of backups to keep'
exit 1
fi
if [[ "$number_of_backups_to_keep" =~ ^[0-9]+$ ]]; then
if [ $number_of_backups_to_keep -lt 1 ]; then
echo 1>&2 "The number of backups to keep can not be 0"
exit 1
fi
else
echo 1>&2 "Invalid number format for the number of backups to keep"
exit 1
fi
# Volumes to exclude from being backed up
exclude_volumes="$3"
# Volumes to exclude from having the containers using them being stopped before the backup
exclude_stop_volumes="$4"
datetime=$($date +"%F_%H-%M-%S")
backup_directory="${backup_root_directory}/${datetime}"
running_containers=$($docker ps -q)
all_containers=$($docker ps -a -q)
all_volumes=$($docker volume ls -q)
# Get the list of volumes to backup
if [ -n "exclude_volumes" ]; then
volumes_to_backup=''
for volume in $all_volumes; do
if [ -n "exclude_volumes" ]; then
skip=0
for exclude in $exclude_volumes; do
if [ "$exclude" = "$volume" ]; then
skip=1
break
fi
done
if [ $skip -eq 1 ]; then
continue
fi
fi
volumes_to_backup="$volumes_to_backup $volume"
done
else
volumes_to_backup="$all_volumes"
fi
if [ -z "$volumes_to_backup" ]; then
exit 0
fi
# Get the list of containers that mount at least one volume
containers_with_volumes=''
for volume in $volumes_to_backup; do
if [ -n "exclude_stop_volumes" ]; then
skip=0
for exclude in $exclude_stop_volumes; do
if [ "$exclude" = "$volume" ]; then
skip=1
break
fi
done
if [ $skip -eq 1 ]; then
continue
fi
fi
containers=$(docker ps -q --filter "volume=$volume" | tr '\n' ' ' | awk '{$1=$1;print}')
containers_with_volumes="$containers_with_volumes $containers"
done
containers_with_volumes=$(echo "$containers_with_volumes" | tr ' ' '\n' | sort -u | tr '\n' ' ')
# Stop running containers with volumes
if [ -n "$running_containers_with_volumes" ]; then
$docker stop $running_containers_with_volumes
fi
# Prepare the backup directory
$mkdir "${backup_directory}"
cd "${backup_directory}" # vackup dumps backups into the working directory and doesn't support full paths
# Backup all volumes
for volume in $volumes_to_backup; do $vackup export $volume ${volume}.tar.gz ; done
# Backup containers as docker-compose.yaml
$docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose $all_containers > docker-compose.yaml
# Restart previously stopped containers
if [ -n "$running_containers_with_volumes" ]; then
$docker restart $running_containers_with_volumes
fi
# If the number of backups exceeds the number to keep, remove the oldest backups
cd "${backup_root_directory}"
$ls -tp | $grep '/$' | $tail -n +$((number_of_backups_to_keep + 1)) | $xargs -I {} rm -rf -- {}
@Guiorgy
Copy link
Author

Guiorgy commented Oct 19, 2025

To make it into a periodic backup, create the following Systemd service and timer:

  • /etc/systemd/system/vackup_all.service

    [Unit]
    Description=Backup Docker volumes using vackup
    Wants=vackup_all.timer
    
    [Service]
    Type=exec
    ExecStart=/usr/local/bin/vackup_all {PATH_TO_BACKUP_DESTINATION} {NUMBER_OF_BACKUPS} '{VOLUMES_TO_EXCLUDE}' 'VOLUMES_TO_NOT_STOP'
    # Example:
    #   - Destination: /mnt/external/docker_backups
    #   - Number of backups: 30
    #   - Volumes to exclude: qbittorrent_downloads
    #   - Volumes whos containers to not stop: diun_data example_data
    # ExecStart=/usr/local/bin/vackup_all /mnt/external/docker_backups 30 'qbittorrent_downloads' 'diun_data example_data'
    
    [Install]
    WantedBy=default.target
    • /etc/systemd/system/vackup_all.timer
    [Unit]
    Description=vackup_all service timer
    
    [Timer]
    # Daily backups
    OnCalendar=*-*-* 06:00:00
    Unit=vackup_all.service
    
    [Install]
    WantedBy=timers.target

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment