Stop containers and tarball Docker volumes for backup.
0
docker-volume-backup.sh edited
140 lines 4.0 kB view raw
1#!/bin/bash 2 3# Space-separated list of containers to stop before backup. 4# Prefix with context to target a specific Docker context (e.g. "rootless:app-db default:app-web"). 5# Uses the $GLOBAL_CONTEXT specified below if no context is provided. 6CONTAINER_NAMES="${CONTAINER_NAMES}" 7 8# Space-separated list of volumes to backup. 9# Prefix with context to target a specific Docker context (e.g. "rootless:app-db_data default:app-uploads"). 10# Uses the $GLOBAL_CONTEXT specified below if no context is provided. 11VOLUME_NAMES="${VOLUME_NAMES}" 12 13# Default Docker context to use when entries don't specify one. 14GLOBAL_CONTEXT="${DOCKER_CONTEXT}" 15 16# Directory on the host where backup archives are stored. 17BACKUP_DIR="${BACKUP_DIR:-/data/backups}" 18 19# Set to "true" to preview actions without executing them. 20DRY_RUN="${DRY_RUN:-false}" 21 22IFS=' ' read -r -a container_entries <<< "$CONTAINER_NAMES" 23IFS=' ' read -r -a volume_entries <<< "$VOLUME_NAMES" 24 25stopped=() 26 27if [ "$DRY_RUN" = "true" ]; then 28 echo "=== DRY RUN MODE - No changes will be made ===" 29 echo "" 30fi 31 32parse_entry() { 33 local entry="$1" 34 if [[ "$entry" == *":"* ]]; then 35 local context="${entry%%:*}" 36 local name="${entry#*:}" 37 echo "$context $name" 38 else 39 local context="${GLOBAL_CONTEXT:-default}" 40 local name="$entry" 41 echo "$context $name" 42 fi 43} 44 45ensure_context() { 46 local ctx="$1" 47 if [ "$DRY_RUN" = "true" ]; then 48 echo " [dry-run] Would switch to context '$ctx'" 49 return 50 fi 51 if [ "$ctx" = "default" ]; then 52 docker context use default >/dev/null 2>&1 53 return 54 fi 55 if docker context ls --format '{{.Name}}' | grep -q "^${ctx}$"; then 56 docker context use "$ctx" >/dev/null 2>&1 57 else 58 echo "! Creating Docker Context '$ctx' !" 59 docker context create "$ctx" \ 60 --docker host="unix:///var/run/docker_${ctx}.sock" 61 docker context use "$ctx" >/dev/null 2>&1 62 fi 63} 64 65restart_containers() { 66 local -n restarts_ref=$1 67 if [ ${#restarts_ref[@]} -eq 0 ]; then 68 return 69 fi 70 echo "" 71 echo "Re-starting containers..." 72 for entry in "${restarts_ref[@]}"; do 73 read -r ctx name <<< "$(parse_entry "$entry")" 74 ensure_context "$ctx" 75 if [ "$DRY_RUN" = "true" ]; then 76 echo " [dry-run] Would start $name (context: $ctx)" 77 else 78 echo " Starting $name (context: $ctx)" 79 docker start "$name" 80 fi 81 done 82} 83 84current_context="" 85echo "Stopping containers:" 86for entry in "${container_entries[@]}"; do 87 [ -z "$entry" ] && continue 88 read -r ctx name <<< "$(parse_entry "$entry")" 89 if [ "$ctx" != "$current_context" ]; then 90 ensure_context "$ctx" 91 current_context="$ctx" 92 fi 93 if [ "$DRY_RUN" = "true" ]; then 94 echo " [dry-run] Would stop $name" 95 stopped+=("$entry") 96 elif docker ps --format '{{.Names}}' | grep -q "^${name}$"; then 97 echo " Stopping $name..." 98 docker stop -t 30 "$name" 99 exit_code=$? 100 if [[ ${exit_code} -ne 0 ]]; then 101 echo " ⚠ Failed to stop $name" 102 restart_containers stopped 103 exit 1 104 else 105 stopped+=("$entry") 106 echo "$name stopped" 107 fi 108 else 109 echo "$name not running, skipping" 110 fi 111done 112 113echo "" 114echo "Preparing volumes for export..." 115for entry in "${volume_entries[@]}"; do 116 [ -z "$entry" ] && continue 117 read -r ctx name <<< "$(parse_entry "$entry")" 118 if [ "$ctx" != "$current_context" ]; then 119 ensure_context "$ctx" 120 current_context="$ctx" 121 fi 122 123 archive_name="${name}.tar.gz" 124 if [ "$DRY_RUN" = "true" ]; then 125 echo " [dry-run] Would backup $name to ${BACKUP_DIR}/${archive_name}" 126 elif docker volume ls --format "{{.Name}}" | grep -q "^${name}$"; then 127 echo " Backing up $name to ${BACKUP_DIR}/${archive_name}..." 128 docker run --rm \ 129 -v "${name}":/source:ro \ 130 -v "${BACKUP_DIR}":/backup \ 131 alpine:3 tar czf "/backup/${archive_name}" -C /source . 132 echo "$name exported" 133 else 134 echo " ⚠ Volume $name not found, skipping" 135 fi 136done 137 138restart_containers stopped 139echo "" 140echo "Backup complete."