Added shell command for deploy, updated readme, backup script.

This commit is contained in:
Aner Zakobar
2026-04-29 20:23:42 +03:00
parent d49f0161ca
commit d6aa39ff04
3 changed files with 262 additions and 107 deletions
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# offload-backup.sh — back up /mnt/data directly to a USB drive using restic.
#
# Run on the Pi (see homey-offload-backup in the dev shell).
# Scans for plugged-in USB partitions, lets you pick one, mounts it if needed,
# initialises a restic repo on it, and runs a backup of all service data dirs.
#
# The restic password is read from the sops-managed secret at runtime;
# no S3 credentials are needed — this is a direct local backup.
#
# Usage: sudo bash offload-backup.sh
set -euo pipefail
REPO_NAME="homey-backup"
DATA_DIR="/mnt/data"
PASSWORD_FILE="/run/secrets/restic/password"
BACKUP_PATHS=(
"$DATA_DIR/openldap"
"$DATA_DIR/authelia"
"$DATA_DIR/gitea"
"$DATA_DIR/nextcloud"
"$DATA_DIR/jellyfin"
"$DATA_DIR/transmission"
)
EXCLUDE_ARGS=(
--exclude "$DATA_DIR/nextcloud/db"
--exclude "$DATA_DIR/restic-cache"
)
# ---------------------------------------------------------------------------
# Find USB partitions
# ---------------------------------------------------------------------------
echo "Scanning for USB drives..."
mapfile -t USB_PARTS < <(
lsblk -o NAME,SIZE,TRAN,LABEL,MOUNTPOINT -rn \
| awk '$3 == "usb" && $2 != "" {print $1, $2, $4, $5}'
)
if [ "${#USB_PARTS[@]}" -eq 0 ]; then
echo "No USB partitions found. Plug in a USB drive and try again." >&2
exit 1
fi
echo ""
echo "Available USB partitions:"
for i in "${!USB_PARTS[@]}"; do
read -r dev size label mount <<< "${USB_PARTS[$i]}"
label="${label:-(no label)}"
mount="${mount:-(not mounted)}"
printf " [%d] /dev/%s %s label=%s mount=%s\n" \
"$((i + 1))" "$dev" "$size" "$label" "$mount"
done
echo ""
read -rp "Select a partition [1-${#USB_PARTS[@]}]: " CHOICE
if ! [[ "$CHOICE" =~ ^[0-9]+$ ]] \
|| [ "$CHOICE" -lt 1 ] \
|| [ "$CHOICE" -gt "${#USB_PARTS[@]}" ]; then
echo "Invalid selection." >&2
exit 1
fi
read -r SELECTED_DEV _ _ EXISTING_MOUNT <<< "${USB_PARTS[$((CHOICE - 1))]}"
SELECTED_DEV="/dev/$SELECTED_DEV"
# ---------------------------------------------------------------------------
# Mount if needed
# ---------------------------------------------------------------------------
MOUNTED_HERE=false
MOUNT_DIR=""
if [ -n "$EXISTING_MOUNT" ]; then
MOUNT_DIR="$EXISTING_MOUNT"
echo "Using existing mount at $MOUNT_DIR"
else
MOUNT_DIR=$(mktemp -d)
echo "Mounting $SELECTED_DEV at $MOUNT_DIR..."
mount "$SELECTED_DEV" "$MOUNT_DIR"
MOUNTED_HERE=true
fi
cleanup() {
if [ "$MOUNTED_HERE" = true ] && [ -n "$MOUNT_DIR" ]; then
echo "Unmounting $MOUNT_DIR..."
umount "$MOUNT_DIR"
rmdir "$MOUNT_DIR"
fi
}
trap cleanup EXIT
# ---------------------------------------------------------------------------
# Initialise restic repo if this is the first run
# ---------------------------------------------------------------------------
REPO="$MOUNT_DIR/$REPO_NAME"
if ! restic -r "$REPO" --password-file "$PASSWORD_FILE" snapshots &>/dev/null 2>&1; then
echo "Initialising restic repository at $REPO..."
restic -r "$REPO" --password-file "$PASSWORD_FILE" init
fi
# ---------------------------------------------------------------------------
# Run the backup
# ---------------------------------------------------------------------------
echo ""
echo "Backing up to $REPO..."
restic -r "$REPO" \
--password-file "$PASSWORD_FILE" \
--cache-dir "$DATA_DIR/restic-cache" \
backup \
"${BACKUP_PATHS[@]}" \
"${EXCLUDE_ARGS[@]}"
echo ""
echo "Snapshots on this drive:"
restic -r "$REPO" --password-file "$PASSWORD_FILE" snapshots