Enhancements — Backup Script¶
Extend the core backup script with production-grade features.
Enhancement 1 — Exclusion Patterns¶
Skip node_modules, .git, build artefacts, and cache directories:
# Add to defaults or config
EXCLUDES=(".git" "node_modules" "__pycache__" "*.pyc" ".DS_Store" "*.log" "target/" "dist/")
build_rsync_excludes() {
local flags=()
for pattern in "${EXCLUDES[@]}"; do
flags+=("--exclude=${pattern}")
done
echo "${flags[@]}"
}
# In do_backup():
local exclude_flags
exclude_flags=$(build_rsync_excludes)
rsync -a --delete $exclude_flags -- "$src/" "$dest/"
Load custom exclusions from a file (~/.backup_exclude):
[-f ~/.backup_exclude](<../../-f ~/.backup_exclude>) && RSYNC_EXCLUDES+=("--exclude-from=$HOME/.backup_exclude")
Enhancement 2 — Email Notification¶
Send a summary email on backup completion or failure:
# Requires: mailutils (sudo apt install mailutils) or postfix configured
NOTIFY_EMAIL="" # Set in config: NOTIFY_EMAIL="you@example.com"
send_notification() {
local subject="$1" body="$2"
[[ -z "${NOTIFY_EMAIL:-}" ]] && return 0
if command -v mail &>/dev/null; then
echo "$body" | mail -s "$subject" "$NOTIFY_EMAIL"
log_info "Notification sent to $NOTIFY_EMAIL"
else
log_warn "mail command not found — skipping notification"
fi
}
# Call at end of do_backup():
if [[ "$failed" -eq 0 ]]; then
send_notification "[OK] Backup complete — $(hostname)" \
"Backup to $DEST_DIR/$TIMESTAMP completed successfully."
else
send_notification "[FAILED] Backup errors — $(hostname)" \
"$failed source(s) failed. Check $LOGFILE for details."
fi
Enhancement 3 — S3 / Remote Upload¶
After a local backup, upload the archive to S3 (requires aws CLI):
S3_BUCKET="" # Set in config: S3_BUCKET="my-backups"
upload_to_s3() {
local archive="$1"
[[ -z "${S3_BUCKET:-}" ]] && return 0
[[ -f "$archive" ]] || return 0
local key="$(hostname)/$(basename "$archive")"
log_info "uploading to s3://${S3_BUCKET}/${key}"
if aws s3 cp "$archive" "s3://${S3_BUCKET}/${key}" --storage-class STANDARD_IA; then
log_info "S3 upload OK"
else
log_warn "S3 upload failed (backup still local)"
fi
}
Enhancement 4 — Encryption¶
Encrypt archives before storing or uploading (requires gpg):
GPG_RECIPIENT="" # Set in config: GPG_RECIPIENT="you@example.com"
encrypt_archive() {
local archive="$1"
[[ -z "${GPG_RECIPIENT:-}" ]] && return 0
if gpg --recipient "$GPG_RECIPIENT" --encrypt "$archive"; then
rm -f "$archive" # Remove unencrypted copy
log_info "encrypted: ${archive}.gpg"
else
log_warn "GPG encryption failed — keeping unencrypted archive"
fi
}
Encryption key management
If you lose your GPG private key, you cannot decrypt your backups. Store a copy of your key in a secure, separate location before encrypting production data.
Enhancement 5 — Pre/Post Hooks¶
Run custom commands before and after each backup (database dumps, service stops):
# In config file:
# PRE_HOOK="mysqldump -u root mydb > /tmp/db_dump.sql"
# POST_HOOK="rm -f /tmp/db_dump.sql"
run_hook() {
local name="$1" cmd="${2:-}"
[[ -z "$cmd" ]] && return 0
log_info "Running $name hook: $cmd"
if eval "$cmd"; then
log_info "$name hook OK"
else
log_warn "$name hook failed (exit $?) — continuing"
fi
}
# In do_backup():
run_hook "pre" "${PRE_HOOK:-}"
# ... rsync ...
run_hook "post" "${POST_HOOK:-}"
Enhancement 6 — Backup Health Dashboard¶
Generate a one-line status summary for cron mail or monitoring:
print_status() {
local newest_dir oldest_dir count total_size
newest_dir=$(find "$DEST_DIR" -maxdepth 1 -mindepth 1 -type d \
-name '[0-9][0-9][0-9][0-9]-*' | sort -r | head -1)
oldest_dir=$(find "$DEST_DIR" -maxdepth 1 -mindepth 1 -type d \
-name '[0-9][0-9][0-9][0-9]-*' | sort | head -1)
count=$(find "$DEST_DIR" -maxdepth 1 -mindepth 1 -type d \
-name '[0-9][0-9][0-9][0-9]-*' | wc -l)
total_size=$(du -sh "$DEST_DIR" | cut -f1)
echo "Backup status for $(hostname):"
echo " Backups: $count"
echo " Newest: $(basename "$newest_dir")"
echo " Oldest: $(basename "$oldest_dir")"
echo " Total: $total_size"
}