Created
January 26, 2025 18:26
-
-
Save meminens/f9a226a9031e3c082e7c6fec9eebb126 to your computer and use it in GitHub Desktop.
Arch Linux Backup Script
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 | |
#=============================================================================== | |
# SYSTEM BACKUP SCRIPT | |
# This script performs a system backup, including: | |
# - Backing up directories, specific files, and package lists. | |
# - Creating an encrypted archive of the backup. | |
# - Uploading the backup to cloud storage using rclone. | |
# - Cleaning up old backups. | |
# | |
# Configuration and setup: | |
# - Modify the settings below to configure backup directories, cloud storage, and other parameters. Look for "<..>". | |
# - Make sure the required dependencies are installed: rsync, rclone, gpg, tar, pacman. | |
#=============================================================================== | |
set -e | |
trap 'handle_error "Unexpected script failure"' ERR | |
# CONFIGURATION SETTINGS | |
# Set the root directory for backups | |
BACKUP_ROOT="<path_to_backup_dir>" # Example: /home/user/backups | |
# Log file path | |
LOG_FILE="<path_to_log_file>" # Example: /var/log/backup.log | |
# Maximum size for log file (in bytes) | |
LOG_MAX_SIZE=$((10 * 1024 * 1024)) # 10 MB maximum log size | |
# Number of backups to keep before removing old ones | |
KEEP_BACKUPS=3 # Example: Keep last 3 backups | |
# Rclone remote name for cloud uploads | |
RCLONE_REMOTE="<remote_name>" # Example: myremote | |
# GPG passphrase for encrypting backups | |
PASSWORD="<gpg_passphrase>" | |
# SOURCE DIRECTORIES AND FILES TO BACK UP | |
declare -A SOURCE_DIRS=( | |
["/etc"]="" # No exclusions by default | |
["/home"]="" # No exclusions by default | |
["/var/lib/pacman/local"]="" # No exclusions by default | |
["/boot"]="" # No exclusions by default | |
) | |
# Specific files to back up | |
SPECIFIC_FILES=( | |
"/home/<user>/.bashrc" | |
"/home/<user>/.bash_history" | |
"/home/<user>/.dircolors" | |
) | |
# LOGGING FUNCTION | |
log_message() { | |
if [[ -f "$LOG_FILE" && $(stat -c %s "$LOG_FILE") -gt $LOG_MAX_SIZE ]]; then | |
mv "$LOG_FILE" "${LOG_FILE}.old" | |
fi | |
echo "$(date '+%Y-%m-%d %H:%M:%S') [$2] $1" >> "$LOG_FILE" | |
} | |
# ERROR HANDLER | |
handle_error() { | |
log_message "$1" "ERROR" | |
[[ -n "$backup_dir" ]] && rm -rf "$backup_dir" | |
[[ -n "$archive_name" && -f "$BACKUP_ROOT/$archive_name" ]] && rm -f "$BACKUP_ROOT/$archive_name" | |
exit 1 | |
} | |
# CHECK DEPENDENCIES | |
check_dependencies() { | |
for cmd in rsync rclone gpg tar pacman; do | |
command -v $cmd &>/dev/null || handle_error "$cmd is not installed or not in PATH" | |
done | |
} | |
# CREATE BACKUP DIRECTORY | |
create_backup_dir() { | |
local timestamp=$(date +%Y%m%d_%H%M%S) | |
local backup_dir="$BACKUP_ROOT/$timestamp" | |
mkdir -p "$backup_dir" || handle_error "Failed to create backup directory" | |
echo "$backup_dir" | |
} | |
# BACK UP DIRECTORIES | |
backup_directories() { | |
local backup_dir="$1" | |
for src_dir in "${!SOURCE_DIRS[@]}"; do | |
[[ -d "$src_dir" ]] || { log_message "WARNING: Source directory $src_dir not found" "WARNING"; continue; } | |
log_message "Backing up $src_dir" "INFO" | |
local exclude_opts="" | |
if [[ -n "${SOURCE_DIRS[$src_dir]}" ]]; then | |
IFS=',' read -ra excludes <<< "${SOURCE_DIRS[$src_dir]}" | |
for item in "${excludes[@]}"; do | |
exclude_opts+="--exclude=${item} " | |
done | |
fi | |
local rel_path=${src_dir#/} | |
local target_dir="$backup_dir/$rel_path" | |
mkdir -p "$(dirname "$target_dir")" | |
rsync -aAXH --info=progress2 $exclude_opts \ | |
--exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","*.socket","S.*"} \ | |
"$src_dir/" "$target_dir/" || handle_error "Failed to backup $src_dir" | |
done | |
} | |
# BACK UP SPECIFIC FILES | |
backup_specific_files() { | |
local backup_dir="$1" | |
for file in "${SPECIFIC_FILES[@]}"; do | |
[[ -f "$file" ]] || { log_message "WARNING: Specific file $file not found" "WARNING"; continue; } | |
log_message "Backing up $file" "INFO" | |
local rel_path=${file#/} | |
local target_dir="$backup_dir/$(dirname "$rel_path")" | |
mkdir -p "$target_dir" | |
cp --preserve=all "$file" "$target_dir/" || handle_error "Failed to backup $file" | |
done | |
} | |
# BACK UP PACKAGE LISTS | |
backup_packages() { | |
local backup_dir="$1" | |
log_message "Backing up package information" "INFO" | |
pacman -Qqen > "$backup_dir/pkglist.txt" && pacman -Qqem > "$backup_dir/foreignpkglist.txt" || handle_error "Failed to backup package lists" | |
} | |
# CREATE BACKUP SUMMARY | |
create_backup_summary() { | |
local backup_dir="$1" | |
{ | |
echo "Backup completed at $(date)" | |
echo "System: $(uname -a)" | |
echo "Disk usage:" | |
df -h | |
} > "$backup_dir/BACKUP_INFO" || handle_error "Failed to create backup summary" | |
} | |
# CLEAN UP OLD BACKUPS | |
cleanup_old_backups() { | |
log_message "Cleaning up old backups" "INFO" | |
readarray -t dirs < <(find "$BACKUP_ROOT" -maxdepth 1 -type d -name "2*" | sort -r) | |
readarray -t archives < <(find "$BACKUP_ROOT" -maxdepth 1 -type f -name "*.tar.gz" | sort -r) | |
for ((i=KEEP_BACKUPS; i<${#dirs[@]}; i++)); do | |
rm -rf "${dirs[i]}" | |
log_message "Removed old backup directory: $(basename "${dirs[i]}")" "INFO" | |
done | |
for ((i=KEEP_BACKUPS; i<${#archives[@]}; i++)); do | |
rm -f "${archives[i]}" | |
log_message "Removed old archive: $(basename "${archives[i]}")" "INFO" | |
done | |
} | |
# CREATE ENCRYPTED ARCHIVE | |
create_archive() { | |
local backup_dir="$1" | |
local archive_name=$(basename "$backup_dir").tar.gz | |
local encrypted_archive="$BACKUP_ROOT/$archive_name" | |
log_message "Creating password-protected archive of backup directory" "INFO" | |
tar --exclude="*.socket" --exclude="S.*" -czf - -C "$BACKUP_ROOT" "$(basename "$backup_dir")" | \ | |
gpg --pinentry-mode=loopback --passphrase "$PASSWORD" --symmetric --cipher-algo AES256 -o "$encrypted_archive" || handle_error "Failed to create and encrypt archive" | |
echo "$archive_name" | |
} | |
# UPLOAD TO CLOUD | |
upload_to_cloud() { | |
local archive_name="$1" | |
log_message "Uploading backup to cloud storage" "INFO" | |
sudo -u <user> rclone --verbose --config "<path_to_rclone_config_dir/>rclone.conf" copyto "$BACKUP_ROOT/$archive_name" "$RCLONE_REMOTE:Arch_Backup.tar.gz" --progress || handle_error "Failed to upload backup to cloud" | |
} | |
# MAIN FUNCTION | |
main() { | |
[[ $EUID -ne 0 ]] && handle_error "Please run as root" | |
check_dependencies | |
log_message "Starting system backup" "INFO" | |
local backup_dir | |
backup_dir=$(create_backup_dir) | |
backup_directories "$backup_dir" | |
backup_packages "$backup_dir" | |
backup_specific_files "$backup_dir" | |
create_backup_summary "$backup_dir" | |
local archive_name | |
archive_name=$(create_archive "$backup_dir") | |
cleanup_old_backups | |
upload_to_cloud "$archive_name" | |
log_message "Backup completed successfully" "INFO" | |
} | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment