Skip to content

Instantly share code, notes, and snippets.

@reinder42
Created December 7, 2024 14:16
Show Gist options
  • Save reinder42/06480da344334d135e36245947bfa933 to your computer and use it in GitHub Desktop.
Save reinder42/06480da344334d135e36245947bfa933 to your computer and use it in GitHub Desktop.
Daily, weekly and monthly backup script for Raspberry Pi 5 ft. Docker and Pishrink
#!/bin/bash
# !!! USE AT YOUR OWN RISK !!! (Always test your backup restore plan!)
# Depends on: Pishrink (./pishrink.sh)
# Suitable for: Backing up stuff on a Raspberry Pi to a connected USB drive, which syncs to a network share
# Makes a daily backup of files and Docker containers, a weekly sparse disk image, and a monthly full disk image
# Configuration
BACKUP_DATE=$(date +%Y-%m-%d)
USB_MOUNT="/mnt/backup"
BACKUP_DIR="$USB_MOUNT/backups/$BACKUP_DATE"
NETWORK_MOUNT="/mnt/share"
NETWORK_PATH="//nas.local/Backup"
NETWORK_USER="user"
NETWORK_PASS="pass"
SOURCE_DISK="/dev/nvme0n1" # Disk to back up
RSYNC_OPTS="-aq --delete"
IMPORTANT_DIRS=("/etc" "/home/pi" "/opt" "/var/log") # Customize paths to back up
DAY_OF_MONTH=$(date +%d)
# Purge unused Docker images
echo "Purging unused Docker images..."
docker image prune -af || { echo "Failed to purge Docker images"; exit 1; }
# Ensure required directories exist
mkdir -p $USB_MOUNT
mkdir -p $NETWORK_MOUNT
# Mount the USB drive if not already mounted
if ! mountpoint -q $USB_MOUNT; then
echo "Mounting USB drive..."
mount /dev/sda1 $USB_MOUNT || { echo "Failed to mount USB drive"; exit 1; }
else
echo "USB drive already mounted at $USB_MOUNT"
fi
# Ensure required directories exist
mkdir -p $BACKUP_DIR
# Purge backups
find "$USB_MOUNT/backups/" -type f -mtime +61 -exec rm {} \;
# Daily: Rsync backup of important directories
echo "Performing daily rsync backup..."
TMP_BACKUP_DIR="$BACKUP_DIR/tmp"
ZIP_FILE="$BACKUP_DIR/files.zip"
mkdir -p $TMP_BACKUP_DIR
# Sync and prepare directories
for DIR in "${IMPORTANT_DIRS[@]}"; do
echo "Backing up $DIR to $TMP_BACKUP_DIR"
rsync $RSYNC_OPTS "$DIR" "$TMP_BACKUP_DIR" || { echo "Rsync backup failed for $DIR"; exit 1; }
done
# Create a single compressed ZIP file
echo "Creating a compressed archive: $ZIP_FILE"
cd $TMP_BACKUP_DIR || { echo "Failed to change directory to $TMP_BACKUP_DIR"; exit 1; }
sudo zip -qr "$ZIP_FILE" . || { echo "Failed to create ZIP file"; exit 1; }
cd - >/dev/null
# Remove the temporary uncompressed directory (feel free to omit)
rm -rf "$TMP_BACKUP_DIR"
# Loop through all running containers
for container_id in $(docker ps -q); do
# Get the container name
container_name=$(docker inspect --format='{{.Name}}' "$container_id" | cut -c2-)
echo "Backing up container: $container_name ($container_id)"
# Export the container's filesystem
export_file="${BACKUP_DIR}/container_${container_name}.tar"
docker export "$container_id" -o "$export_file"
echo "Filesystem for $container_name saved to $export_file"
# List associated volumes
volumes=$(docker inspect --format='{{range .Mounts}}{{if eq .Type "volume"}}{{.Name}} {{end}}{{end}}' "$container_id")
# Backup volumes if any
for volume in $volumes; do
volume_backup_file="${BACKUP_DIR}/volume_${volume}.tar.gz"
echo "Backing up volume: $volume for container: $container_name"
docker run --rm -v "$volume:/volume" -v "$BACKUP_DIR:/backup" busybox tar czvf "/backup/volume_${volume}.tar.gz" -C /volume .
echo "Volume $volume saved to $volume_backup_file"
done
done
# Determine whether to run weekly or monthly backup
if (( DAY_OF_MONTH == 1 )); then
# Monthly: Full disk image
echo "Creating full monthly disk image..."
BACKUP_IMAGE="$BACKUP_DIR/backup_monthly.img"
dd if=$SOURCE_DISK of=$BACKUP_IMAGE bs=4M status=progress || { echo "Disk backup failed"; exit 1; }
echo "Shrinking backup image with pishrink..."
./pishrink.sh $BACKUP_IMAGE || { echo "Failed to shrink backup"; exit 1; }
elif (( DAY_OF_MONTH % 7 == 0 )); then
# Weekly: Sparse disk image
echo "Creating sparse weekly disk image..."
BACKUP_IMAGE="$BACKUP_DIR/backup_weekly.img"
dd if=$SOURCE_DISK of=$BACKUP_IMAGE bs=4M status=progress conv=sparse || { echo "Disk backup failed"; exit 1; }
echo "Shrinking backup image with pishrink..."
./pishrink.sh $BACKUP_IMAGE || { echo "Failed to shrink backup"; exit 1; }
fi
# Mount the network drive if not already mounted
if ! mountpoint -q $NETWORK_MOUNT; then
echo "Mounting network drive..."
mount -t cifs $NETWORK_PATH $NETWORK_MOUNT -o username=$NETWORK_USER,password=$NETWORK_PASS,vers=3.0 || { echo "Failed to mount network drive"; exit 1; }
else
echo "Network drive already mounted at $NETWORK_MOUNT"
fi
echo "Syncing backups to network drive..."
rsync $RSYNC_OPTS "$USB_MOUNT/backups/" "$NETWORK_MOUNT/backup_rpi" || { echo "Sync to network failed"; exit 1; }
# Unmount the network drive and USB drive
if mountpoint -q $NETWORK_MOUNT; then
echo "Unmounting network drive..."
umount $NETWORK_MOUNT || { echo "Failed to unmount network drive"; exit 1; }
fi
if mountpoint -q $USB_MOUNT; then
echo "Unmounting USB drive..."
umount $USB_MOUNT || { echo "Failed to unmount USB drive"; exit 1; }
fi
echo "Backup task completed successfully."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment