Created
December 7, 2024 14:16
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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