Skip to main content

USB Cold-Backup Scripts (Expansion24 / Expansion26)

Overview

Two UnRAID User Scripts mirror array shares to two USB-attached cold-backup drives via rclone sync. Both scripts were extended with Pushover push notifications on completion (success or failure), since User Scripts has no built-in alerting.

Backup Mapping

Target Drive Shares Covered
Expansion24 (24TB) Data (full), Photos, Multimedia/Movies, Multimedia/ST
Expansion26 (26TB) Multimedia/Audio, Multimedia/Books, Multimedia/Personal, Multimedia/TV, Multimedia/To Review, Multimedia/Ubooquity, Multimedia/audiobookshelf

Script Logic (both jobs share this pattern)

#!/bin/bash
 
# === Pushover Config ===
PUSHOVER_TOKEN="<redacted — see Pushover application dashboard>"
PUSHOVER_USER="<redacted — user key, not stored in script comments>"
 
FAILED_JOBS=""
START_TIME=$(date +%s)
 
send_pushover() {
  local STATUS="$1"
  local MESSAGE="$2"
  curl -s \
    --form-string "token=${PUSHOVER_TOKEN}" \
    --form-string "user=${PUSHOVER_USER}" \
    --form-string "title=UnRAID Backup - <ExpansionXX>" \
    --form-string "message=${MESSAGE}" \
    --form-string "priority=$( [ "$STATUS" = "FAIL" ] && echo 1 || echo 0 )" \
    https://api.pushover.net/1/messages.json > /dev/null
}
 
run_sync() {
  local SRC="$1" DEST="$2" LOG="$3" LABEL="$4"
  rclone sync "$SRC" "$DEST" --log-file="$LOG" -v
  if [ $? -ne 0 ]; then
    FAILED_JOBS="${FAILED_JOBS}\n- ${LABEL}"
  fi
}
 
# One run_sync call per share/subfolder pair...
 
END_TIME=$(date +%s)
DURATION=$(( (END_TIME - START_TIME) / 60 ))
 
if [ -z "$FAILED_JOBS" ]; then
  send_pushover "OK" "All jobs completed successfully in ${DURATION} min."
else
  send_pushover "FAIL" "Backup finished with failures in ${DURATION} min.\nFailed jobs:${FAILED_JOBS}"
fi

Key design points:

  • Each rclone sync call is wrapped in run_sync(), capturing its own exit code — a failure in job 2 of 7 doesn't mask or get overwritten by job 7's exit code.
  • All jobs run independently to completion regardless of earlier failures (no set -e) — by design, so one broken share doesn't block backups of everything else.
  • A single Pushover notification fires at the end summarizing success or listing exactly which job(s) failed, with failure notifications set to priority=1 to bypass Pushover quiet hours.

Notes / Gotchas

  • Both PUSHOVER_TOKEN and PUSHOVER_USER should be redacted from any wiki copy of this script — store actual credentials in the User Scripts plugin directly, not in a doc that might get exported or shared.
  • $? only reflects the immediately preceding command — if a future edit pipes rclone output through another command (| tee, | grep, etc.), the exit code capture breaks silently. Keep the exit-code check directly after the rclone call.
  • These backups are not parity-protected — they live on Unassigned Devices, outside the array. A single backup drive failure has no recovery path other than re-running the sync from source.

Last Updated

2026-06-23