#!/bin/bash # Load environment variables from .env file if [ -f .env ]; then # Read each line from .env and export variables while IFS= read -r line || [[ -n "$line" ]]; do # Skip comments and empty lines [[ $line =~ ^#.*$ ]] && continue [[ -z "$line" ]] && continue # Export the variable export "$line" done < .env else echo "Error: .env file not found" exit 1 fi # Variables (now using environment variables with defaults) MYSQL_USER="admin" MYSQL_PASSWORD=${MYSQL_ADMIN_PASSWORD} BACKUP_DIR="$(pwd)/backups" REMOTE_HOST="${BACKUP_SERVER_HOST}" # Remote server hostname/IP REMOTE_USER="${BACKUP_SERVER_USER}" # Remote server username REMOTE_BACKUP_DIR="${BACKUP_SERVER_PATH}" # Remote server backup path TEMP_DIR="$BACKUP_DIR/tmp" TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") MODE="" HOURLY_RETENTION=4 DAILY_RETENTION=3 WEEKLY_RETENTION=1 MIN_SPACE=20 # Minimum space in GB DB_CONTAINER="mailserver_db" # Function to display usage function usage() { echo "Usage: $0 --hourly | --daily | --weekly" exit 1 } # Function to check available space and cleanup if necessary function check_and_cleanup_space() { local available_space=$(df -BG "$REMOTE_BACKUP_DIR" | awk 'NR==2 {gsub("G","",$4); print $4}') if [ "$available_space" -le "$MIN_SPACE" ]; then echo "Available space ($available_space GB) is less than minimum required ($MIN_SPACE GB)" echo "Starting cleanup..." while [ "$available_space" -le "$MIN_SPACE" ]; do # Find oldest backup file oldest_file=$(find "$REMOTE_BACKUP_DIR" -type f -name "*.zip" -printf '%T+ %p\n' | sort | head -n 1 | awk '{print $2}') if [ -z "$oldest_file" ]; then echo "No more files to delete!" break fi # Get file size before deletion for logging file_size=$(du -h "$oldest_file" | cut -f1) # Delete the file rm -f "$oldest_file" echo "Deleted old backup: $oldest_file (Size: $file_size)" # Recalculate available space available_space=$(df -BG "$REMOTE_BACKUP_DIR" | awk 'NR==2 {gsub("G","",$4); print $4}') done echo "Cleanup complete. Available space: $available_space GB" fi } echo "##########################" echo "Starting backup..." echo "##########################" echo "" # Check if the correct parameter is passed if [[ "$1" == "--hourly" ]]; then MODE="hourly" elif [[ "$1" == "--daily" ]]; then MODE="daily" elif [[ "$1" == "--weekly" ]]; then MODE="weekly" else usage fi echo "##########################" echo "Mode: $MODE" echo "##########################" echo "" echo "##########################" echo "Creating backup directories..." echo "##########################" echo "" # Create backup and temp directories if they don't exist mkdir -p "$BACKUP_DIR" mkdir -p "$TEMP_DIR" mkdir -p "/var/log/backup" # Backup all MySQL databases echo "##########################" echo "Backing up MySQL databases..." echo "##########################" echo "" # Test database connection first if ! docker exec -i $DB_CONTAINER mariadb -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "SELECT 1;" >/dev/null 2>&1; then echo "Error: Cannot connect to MySQL database. Please check credentials." echo "Container: $DB_CONTAINER" echo "User: $MYSQL_USER" echo "Password being used: ${MYSQL_PASSWORD}" exit 1 fi # Get databases list without using -it flag (which requires terminal) databases=$(docker exec -i $DB_CONTAINER mariadb -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)") for db in $databases; do echo "Backing up database: $db" docker exec -i $DB_CONTAINER mariadb-dump -u $MYSQL_USER -p${MYSQL_PASSWORD} --databases "$db" > "$TEMP_DIR/${db}.sql" if [ $? -eq 0 ]; then zip -j "$TEMP_DIR/${db}.sql.zip" "$TEMP_DIR/${db}.sql" rm "$TEMP_DIR/${db}.sql" else echo "Error backing up database: $db" fi done DIRECTORIES_TO_BACKUP=( "/opt/2weekmail" ) for dir in "${DIRECTORIES_TO_BACKUP[@]}"; do DIR_NAME=$(basename "$dir") zip -r "$TEMP_DIR/${DIR_NAME}_${MODE}_$TIMESTAMP.zip" "$dir" -x "*/node_modules/*" "*/backups/*" done # Compress all SQL and directories into a single zip file echo "##########################" echo "Compressing backup files..." echo "##########################" echo "" FINAL_BACKUP_FILE="$BACKUP_DIR/${MODE}_backup_$TIMESTAMP.zip" find "$TEMP_DIR" -name "*.zip" | while read file; do zip -ur "$FINAL_BACKUP_FILE" "$file" done # Clean up temporary files rm -rf "$TEMP_DIR" echo "##########################" echo "Backup complete: $FINAL_BACKUP_FILE" echo "##########################" echo "" function apply_retention() { backup_type=$1 retention_count=$2 # Find all backup files of the specified type, sort them by modification time, and keep the newest backups=($(ls -t $BACKUP_DIR/${backup_type}_backup_*.zip)) # If the number of backups exceeds the retention limit, delete the older ones if [ ${#backups[@]} -gt $retention_count ]; then delete_count=$((${#backups[@]} - $retention_count)) for (( i=$retention_count; i<${#backups[@]}; i++ )); do rm -f "${backups[$i]}" echo "Deleted old $backup_type backup: ${backups[$i]}" done fi } echo "##########################" echo "Applying retention policy..." echo "##########################" echo "" # Apply retention policy if [[ "$MODE" == "hourly" ]]; then apply_retention "hourly" $HOURLY_RETENTION elif [[ "$MODE" == "daily" ]]; then apply_retention "daily" $DAILY_RETENTION elif [[ "$MODE" == "weekly" ]]; then apply_retention "weekly" $WEEKLY_RETENTION fi echo "Retention policy applied: $MODE backups cleaned." echo "##########################" echo "Backup cleanup complete." echo "##########################" echo "" echo "##########################" echo "Checking remote backup space..." echo "##########################" echo "" # Check and cleanup space before copying new backup check_and_cleanup_space # Use rsync to copy the backup if ! ssh -p "$BACKUP_SERVER_PORT" "${REMOTE_USER}@${REMOTE_HOST}" exit 2>/dev/null; then echo "Error: Cannot connect to remote backup server" echo "Backup file is saved locally at: $FINAL_BACKUP_FILE" exit 1 fi rsync -av --progress -e "ssh -p $BACKUP_SERVER_PORT" "$FINAL_BACKUP_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BACKUP_DIR}/" echo "##########################" echo "Remote backup complete" echo "##########################" echo ""