Backup_scripts
Chapter 37: Backup and Restore Scripts
Section titled “Chapter 37: Backup and Restore Scripts”Overview
Section titled “Overview”This chapter covers comprehensive backup and restore scripts for databases, files, and entire systems. These scripts are essential for disaster recovery and data protection.
Database Backup Scripts
Section titled “Database Backup Scripts”MySQL/MariaDB Backup
Section titled “MySQL/MariaDB Backup”#!/usr/bin/env bash# backup_mysql.sh - Backup MySQL/MariaDB databases
set -euo pipefail
# ConfigurationDB_HOST="${DB_HOST:-localhost}"DB_USER="${DB_USER:-root}"DB_PASSWORD="${DB_PASSWORD:-}"BACKUP_DIR="${BACKUP_DIR:-/backups/mysql}"RETENTION_DAYS="${RETENTION_DAYS:-30}"COMPRESSION="${COMPRESSION:-gzip}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; exit 1; }
# Validate configuration: "${DB_PASSWORD:?DB_PASSWORD environment variable required}"
# Create backup directorymkdir -p "$BACKUP_DIR"
# Get list of databasesget_databases() { if [[ "$DB_PASSWORD" == "" ]]; then mysql -h "$DB_HOST" -u "$DB_USER" -e "SHOW DATABASES;" | grep -v Database else mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" -e "SHOW DATABASES;" | grep -v Database fi}
# Backup single databasebackup_database() { local db_name="$1" local timestamp timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="${BACKUP_DIR}/${db_name}_${timestamp}.sql"
log "Backing up database: $db_name"
if [[ "$DB_PASSWORD" == "" ]]; then mysqldump -h "$DB_HOST" -u "$DB_USER" \ --single-transaction \ --quick \ --lock-tables=false \ "$db_name" > "$backup_file" else mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" \ --single-transaction \ --quick \ --lock-tables=false \ "$db_name" > "$backup_file" fi
# Compress if enabled if [[ "$COMPRESSION" == "gzip" ]]; then gzip "$backup_file" backup_file="${backup_file}.gz" fi
log "Backup created: $backup_file"}
# Cleanup old backupscleanup_old_backups() { log "Cleaning up backups older than $RETENTION_DAYS days..."
find "$BACKUP_DIR" -name "*.sql*" -mtime +$RETENTION_DAYS -delete
log "Cleanup complete"}
# Mainmain() { log "Starting MySQL backup..."
# Backup all databases while read -r db; do # Skip system databases [[ "$db" == "information_schema" ]] && continue [[ "$db" == "performance_schema" ]] && continue [[ "$db" == "mysql" ]] && continue
backup_database "$db" done < <(get_databases)
# Cleanup cleanup_old_backups
log "MySQL backup complete"}
mainPostgreSQL Backup
Section titled “PostgreSQL Backup”#!/usr/bin/env bash# backup_postgres.sh - Backup PostgreSQL databases
set -euo pipefail
# ConfigurationDB_HOST="${DB_HOST:-localhost}"DB_USER="${DB_USER:-postgres}"DB_PORT="${DB_PORT:-5432}"BACKUP_DIR="${BACKUP_DIR:-/backups/postgres}"RETENTION_DAYS="${RETENTION_DAYS:-30}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
# Create backup directorymkdir -p "$BACKUP_DIR"
# Get list of databasesget_databases() { psql -h "$DB_HOST" -U "$DB_USER" -p "$DB_PORT" -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;"}
# Backup single databasebackup_database() { local db_name="$1" local timestamp timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="${BACKUP_DIR}/${db_name}_${timestamp}.sql"
log "Backing up database: $db_name"
PGPASSWORD="$DB_PASSWORD" pg_dump -h "$DB_HOST" -U "$DB_USER" -p "$DB_PORT" \ -Fc \ "$db_name" > "$backup_file"
log "Backup created: $backup_file"}
# Mainmain() { log "Starting PostgreSQL backup..."
while read -r db; do db=$(echo "$db" | xargs) [[ -z "$db" ]] && continue backup_database "$db" done < <(get_databases)
log "PostgreSQL backup complete"}
mainFile Backup Scripts
Section titled “File Backup Scripts”Incremental Backup with rsync
Section titled “Incremental Backup with rsync”#!/usr/bin/env bash# backup_incremental.sh - Incremental file backup
set -euo pipefail
# ConfigurationSOURCE_DIR="${1:-/data}"BACKUP_DIR="${2:-/backups}"EXCLUDE_FILE="${EXCLUDE_FILE:-/etc/backup/exclude.txt}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
# Create timestamped backup directorytimestamp=$(date +%Y%m%d_%H%M%S)current_backup="$BACKUP_DIR/current"new_backup="$BACKUP_DIR/backup_$timestamp"
log "Starting incremental backup..."
# Create hard-link based incremental backup# This saves space by using hard links for unchanged files
if [[ -d "$current_backup" ]]; then # Use rsync with link-dest for incremental backup rsync -avh \ --delete \ --delete-excluded \ --link-dest="$current_backup" \ $([[ -f "$EXCLUDE_FILE" ]] && echo "--exclude-from=$EXCLUDE_FILE") \ "$SOURCE_DIR/" "$new_backup/"
# Remove old current and link new backup rm -rf "$current_backup" ln -s "$new_backup" "$current_backup"else # First backup - full backup rsync -avh \ --delete \ --delete-excluded \ $([[ -f "$EXCLUDE_FILE" ]] && echo "--exclude-from=$EXCLUDE_FILE") \ "$SOURCE_DIR/" "$new_backup/"
ln -s "$new_backup" "$current_backup"fi
log "Backup complete: $new_backup"log "Current symlink updated to: $current_backup"Encrypted Backup
Section titled “Encrypted Backup”#!/usr/bin/env bash# backup_encrypted.sh - Encrypted file backup
set -euo pipefail
# ConfigurationSOURCE_DIR="${1:-/data}"BACKUP_DIR="${2:-/backups/encrypted}"ENCRYPTION_KEY="${ENCRYPTION_KEY:-}"PASSPHRASE="${PASSPHRASE:-}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
# Validate encryption key: "${ENCRYPTION_KEY:?ENCRYPTION_KEY environment variable required}"
# Create backup directorymkdir -p "$BACKUP_DIR"
# Create backup filenametimestamp=$(date +%Y%m%d_%H%M%S)backup_file="$BACKUP_DIR/backup_${timestamp}.tar.gz.gpg"
log "Creating encrypted backup..."
# Create compressed archive and encrypttar -czf - -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" | \ gpg --symmetric \ --batch \ --passphrase "$ENCRYPTION_KEY" \ --cipher-algo AES256 \ --compress-algo none \ -o "$backup_file"
log "Encrypted backup created: $backup_file"log "Backup size: $(du -h "$backup_file" | cut -f1)"Restore Scripts
Section titled “Restore Scripts”Database Restore
Section titled “Database Restore”#!/usr/bin/env bash# restore_mysql.sh - Restore MySQL database
set -euo pipefail
# ConfigurationDB_HOST="${DB_HOST:-localhost}"DB_USER="${DB_USER:-root}"DB_PASSWORD="${DB_PASSWORD:-}"BACKUP_FILE="${1:-}"TARGET_DB="${2:-}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; exit 1; }
# Validate[[ -z "$BACKUP_FILE" ]] && error "Usage: $0 <backup_file> [target_database]"[[ -f "$BACKUP_FILE" ]] || error "Backup file not found: $BACKUP_FILE": "${DB_PASSWORD:?DB_PASSWORD environment variable required}"
# Determine if compressedif [[ "$BACKUP_FILE" == *.gz ]]; then DECOMPRESS="zcat"else DECOMPRESS="cat"fi
log "Restoring database from: $BACKUP_FILE"
# Restore databaseif [[ -n "$TARGET_DB" ]]; then log "Restoring to database: $TARGET_DB" $DECOMPRESS "$BACKUP_FILE" | \ mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$TARGET_DB"else # Extract database name from filename db_name=$(basename "$BACKUP_FILE" | sed 's/_[0-9].*//') log "Restoring to database: $db_name" $DECOMPRESS "$BACKUP_FILE" | \ mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD"fi
log "Restore complete"Backup Verification
Section titled “Backup Verification”Verify Backup Integrity
Section titled “Verify Backup Integrity”#!/usr/bin/env bash# verify_backup.sh - Verify backup integrity
set -euo pipefail
BACKUP_FILE="${1:-}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; exit 1; }
[[ -z "$BACKUP_FILE" ]] && error "Usage: $0 <backup_file>"[[ -f "$BACKUP_FILE" ]] || error "File not found: $BACKUP_FILE"
log "Verifying backup: $BACKUP_FILE"
# Check file exists and is readableif [[ -r "$BACKUP_FILE" ]]; then log "✓ File is readable"else error "✗ File is not readable"fi
# Check file is not emptyif [[ -s "$BACKUP_FILE" ]]; then log "✓ File is not empty" log " Size: $(du -h "$BACKUP_FILE" | cut -f1)"else error "✗ File is empty"fi
# Verify based on extensioncase "$BACKUP_FILE" in *.tar.gz|*.tgz) log "Verifying tar.gz integrity..." if tar -tzf "$BACKUP_FILE" &>/dev/null; then log "✓ tar.gz integrity OK" else error "✗ tar.gz integrity check failed" fi ;; *.sql.gz) log "Verifying SQL backup..." if zcat "$BACKUP_FILE" | head -1 | grep -q "MySQL\|MariaDB"; then log "✓ SQL backup appears valid" fi ;; *.gpg) log "✓ GPG encrypted backup - cannot verify without key" ;;esac
log "Verification complete"Automated Backup Schedule
Section titled “Automated Backup Schedule”Backup Rotation Script
Section titled “Backup Rotation Script”#!/usr/bin/env bash# backup_rotation.sh - Manage backup rotation
set -euo pipefail
BACKUP_DIR="${BACKUP_DIR:-/backups}"DAILY_COUNT="${DAILY_COUNT:-7}"WEEKLY_COUNT="${WEEKLY_COUNT:-4}"MONTHLY_COUNT="${MONTHLY_COUNT:-12}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
rotate_backups() { local pattern="$1" local keep_count="$2"
log "Rotating backups matching: $pattern (keep $keep_count)"
# List backups sorted by date local backups backups=($(ls -1t "$BACKUP_DIR"/$pattern 2>/dev/null))
# Keep only the specified number if [[ ${#backups[@]} -gt $keep_count ]]; then for ((i=keep_count; i<${#backups[@]}; i++)); do log "Removing old backup: ${backups[$i]}" rm -f "${backups[$i]}" done fi
log "Rotation complete"}
main() { log "Starting backup rotation..."
# Daily backups (keep 7) rotate_backups "*daily*" $DAILY_COUNT
# Weekly backups (keep 4) rotate_backups "*weekly*" $WEEKLY_COUNT
# Monthly backups (keep 12) rotate_backups "*monthly*" $MONTHLY_COUNT
log "Backup rotation complete"}
mainSummary
Section titled “Summary”In this chapter, you learned:
- ✅ MySQL/MariaDB backup scripts
- ✅ PostgreSQL backup scripts
- ✅ Incremental backup with rsync
- ✅ Encrypted backups
- ✅ Database restore scripts
- ✅ Backup verification
- ✅ Backup rotation management
- ✅ Automated backup scheduling
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Log Analysis Scripts.
Previous Chapter: System Administration Scripts Next Chapter: Log Analysis Scripts