BLOG POSTS
    MangoHost Blog / Configuring MySQL Backups with Percona XtraBackup on Ubuntu 24
Configuring MySQL Backups with Percona XtraBackup on Ubuntu 24

Configuring MySQL Backups with Percona XtraBackup on Ubuntu 24

MySQL database backups are critical for any production environment, but traditional mysqldump can be painfully slow for large databases and requires locking tables during the backup process. Percona XtraBackup solves these problems by creating hot backups of InnoDB, XtraDB, and MyISAM tables without blocking your database operations. This guide will walk you through setting up automated MySQL backups using Percona XtraBackup on Ubuntu 24, covering everything from installation to advanced backup strategies, performance optimizations, and recovery procedures.

How Percona XtraBackup Works

XtraBackup uses InnoDB’s crash-recovery mechanism to create consistent backups without locking your database. Here’s the technical breakdown:

  • Physical backup approach: Copies actual data files rather than executing SQL statements
  • LSN tracking: Records Log Sequence Numbers to ensure consistency
  • Redo log application: Applies changes that occurred during backup to maintain consistency
  • Non-blocking: Reads data files directly without interfering with normal operations

The backup process happens in two phases: first, XtraBackup copies data files while recording any changes in the redo log, then applies those changes to create a consistent point-in-time backup.

Feature mysqldump XtraBackup MySQL Enterprise Backup
Backup Speed Slow (logical) Fast (physical) Fast (physical)
Table Locking Yes (MyISAM) Minimal Minimal
Incremental Backups No Yes Yes
Point-in-Time Recovery Limited Yes Yes
Cost Free Free Commercial

Installation and Initial Setup

First, let’s install Percona XtraBackup on Ubuntu 24. The process involves adding the Percona repository and installing the necessary packages.

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install required dependencies
sudo apt install -y wget gnupg2 lsb-release curl

# Add Percona repository key
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo apt update

# Enable the repository for Percona XtraBackup 8.0
sudo percona-release enable-only xb-80 release
sudo apt update

# Install Percona XtraBackup
sudo apt install -y percona-xtrabackup-80

Verify the installation:

# Check XtraBackup version
xtrabackup --version

# Verify MySQL connection
mysql -u root -p -e "SELECT VERSION();"

Creating a Backup User

XtraBackup requires specific MySQL privileges to function properly. Create a dedicated backup user with minimal required permissions:

# Connect to MySQL as root
mysql -u root -p

# Create backup user
CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY 'SecureBackupPassword123!';

# Grant necessary privileges
GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'xtrabackup'@'localhost';
GRANT CREATE, INSERT, DROP, UPDATE ON mysql.backup_progress TO 'xtrabackup'@'localhost';
GRANT CREATE, INSERT, DROP, UPDATE, SELECT, ALTER ON mysql.backup_history TO 'xtrabackup'@'localhost';

# For MySQL 8.0, also grant BACKUP_ADMIN
GRANT BACKUP_ADMIN ON *.* TO 'xtrabackup'@'localhost';

FLUSH PRIVILEGES;
EXIT;

Store the credentials securely in a configuration file:

# Create backup configuration directory
sudo mkdir -p /etc/mysql/backup

# Create credentials file
sudo tee /etc/mysql/backup/.xtrabackup.cnf > /dev/null << 'EOF'
[xtrabackup]
user=xtrabackup
password=SecureBackupPassword123!

[mysql]
user=xtrabackup
password=SecureBackupPassword123!
EOF

# Secure the credentials file
sudo chown root:root /etc/mysql/backup/.xtrabackup.cnf
sudo chmod 600 /etc/mysql/backup/.xtrabackup.cnf

Basic Backup Operations

Now let's create our first backup. XtraBackup supports full and incremental backups with various compression options.

Full Backup

# Create backup directory
sudo mkdir -p /backup/mysql/full
sudo mkdir -p /backup/mysql/incremental
sudo mkdir -p /backup/mysql/logs

# Perform full backup
sudo xtrabackup --defaults-file=/etc/mysql/backup/.xtrabackup.cnf \
  --backup \
  --target-dir=/backup/mysql/full/$(date +%Y%m%d_%H%M%S) \
  --compress \
  --compress-threads=4 \
  2>&1 | sudo tee /backup/mysql/logs/backup_$(date +%Y%m%d_%H%M%S).log

Compressed Backup with Parallel Processing

# Full backup with optimizations
sudo xtrabackup --defaults-file=/etc/mysql/backup/.xtrabackup.cnf \
  --backup \
  --target-dir=/backup/mysql/full/$(date +%Y%m%d_%H%M%S) \
  --compress \
  --compress-threads=4 \
  --parallel=4 \
  --encrypt=AES256 \
  --encrypt-key-file=/etc/mysql/backup/encryption.key \
  2>&1 | sudo tee /backup/mysql/logs/backup_$(date +%Y%m%d_%H%M%S).log

For encryption, first generate an encryption key:

# Generate encryption key
sudo openssl rand -base64 24 | sudo tee /etc/mysql/backup/encryption.key
sudo chmod 600 /etc/mysql/backup/encryption.key

Automated Backup Script

Here's a production-ready backup script that handles rotation, compression, and error notifications:

#!/bin/bash
# /usr/local/bin/mysql-xtrabackup.sh

set -e

# Configuration
BACKUP_USER="xtrabackup"
BACKUP_DIR="/backup/mysql"
FULL_BACKUP_DIR="$BACKUP_DIR/full"
INCREMENTAL_BACKUP_DIR="$BACKUP_DIR/incremental"
LOG_DIR="$BACKUP_DIR/logs"
CONFIG_FILE="/etc/mysql/backup/.xtrabackup.cnf"
ENCRYPT_KEY="/etc/mysql/backup/encryption.key"

# Retention settings
FULL_BACKUP_RETENTION=7  # Keep 7 full backups
INCREMENTAL_RETENTION=14 # Keep 14 days of incrementals
LOG_RETENTION=30         # Keep 30 days of logs

# Email settings
ADMIN_EMAIL="admin@yourdomain.com"

# Functions
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DIR/backup_$(date +%Y%m%d).log"
}

send_notification() {
    local subject="$1"
    local message="$2"
    echo "$message" | mail -s "$subject" "$ADMIN_EMAIL" 2>/dev/null || true
}

cleanup_old_backups() {
    log_message "Cleaning up old backups..."
    
    # Clean full backups
    find "$FULL_BACKUP_DIR" -maxdepth 1 -type d -mtime +$FULL_BACKUP_RETENTION -exec rm -rf {} \;
    
    # Clean incremental backups
    find "$INCREMENTAL_BACKUP_DIR" -maxdepth 1 -type d -mtime +$INCREMENTAL_RETENTION -exec rm -rf {} \;
    
    # Clean logs
    find "$LOG_DIR" -name "*.log" -mtime +$LOG_RETENTION -delete
}

perform_backup() {
    local backup_type="$1"
    local target_dir="$2"
    local base_dir="${3:-}"
    
    local backup_cmd="xtrabackup --defaults-file=$CONFIG_FILE --backup --target-dir=$target_dir"
    
    # Add incremental options if needed
    if [[ "$backup_type" == "incremental" && -n "$base_dir" ]]; then
        backup_cmd="$backup_cmd --incremental-basedir=$base_dir"
    fi
    
    # Add compression and encryption
    backup_cmd="$backup_cmd --compress --compress-threads=4 --parallel=4"
    
    if [[ -f "$ENCRYPT_KEY" ]]; then
        backup_cmd="$backup_cmd --encrypt=AES256 --encrypt-key-file=$ENCRYPT_KEY"
    fi
    
    log_message "Starting $backup_type backup to $target_dir"
    
    if eval "$backup_cmd"; then
        log_message "$backup_type backup completed successfully"
        return 0
    else
        log_message "ERROR: $backup_type backup failed"
        send_notification "MySQL Backup Failed" "$backup_type backup failed on $(hostname) at $(date)"
        return 1
    fi
}

# Main execution
main() {
    local backup_type="${1:-full}"
    
    # Ensure directories exist
    mkdir -p "$FULL_BACKUP_DIR" "$INCREMENTAL_BACKUP_DIR" "$LOG_DIR"
    
    case "$backup_type" in
        "full")
            local timestamp=$(date +%Y%m%d_%H%M%S)
            local target_dir="$FULL_BACKUP_DIR/full_$timestamp"
            
            if perform_backup "full" "$target_dir"; then
                cleanup_old_backups
                log_message "Full backup process completed"
            else
                exit 1
            fi
            ;;
            
        "incremental")
            # Find the latest backup (full or incremental)
            local latest_full=$(find "$FULL_BACKUP_DIR" -maxdepth 1 -type d -name "full_*" | sort | tail -1)
            local latest_incremental=$(find "$INCREMENTAL_BACKUP_DIR" -maxdepth 1 -type d -name "inc_*" | sort | tail -1)
            
            local base_dir="$latest_full"
            if [[ -n "$latest_incremental" && "$latest_incremental" -nt "$latest_full" ]]; then
                base_dir="$latest_incremental"
            fi
            
            if [[ -z "$base_dir" ]]; then
                log_message "ERROR: No full backup found for incremental backup"
                exit 1
            fi
            
            local timestamp=$(date +%Y%m%d_%H%M%S)
            local target_dir="$INCREMENTAL_BACKUP_DIR/inc_$timestamp"
            
            if perform_backup "incremental" "$target_dir" "$base_dir"; then
                log_message "Incremental backup process completed"
            else
                exit 1
            fi
            ;;
            
        *)
            echo "Usage: $0 {full|incremental}"
            exit 1
            ;;
    esac
}

# Execute main function
main "$@"

Make the script executable and test it:

sudo chmod +x /usr/local/bin/mysql-xtrabackup.sh

# Test full backup
sudo /usr/local/bin/mysql-xtrabackup.sh full

# Test incremental backup
sudo /usr/local/bin/mysql-xtrabackup.sh incremental

Setting Up Automated Scheduling

Use systemd timers for more reliable scheduling than cron:

# Create systemd service file
sudo tee /etc/systemd/system/mysql-backup-full.service > /dev/null << 'EOF'
[Unit]
Description=MySQL Full Backup
After=mysql.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mysql-xtrabackup.sh full
User=root
StandardOutput=journal
StandardError=journal
EOF

# Create systemd timer for full backups (weekly)
sudo tee /etc/systemd/system/mysql-backup-full.timer > /dev/null << 'EOF'
[Unit]
Description=MySQL Full Backup Timer
Requires=mysql-backup-full.service

[Timer]
OnCalendar=Sun 02:00
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target
EOF

# Create incremental backup service
sudo tee /etc/systemd/system/mysql-backup-incremental.service > /dev/null << 'EOF'
[Unit]
Description=MySQL Incremental Backup
After=mysql.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mysql-xtrabackup.sh incremental
User=root
StandardOutput=journal
StandardError=journal
EOF

# Create incremental backup timer (daily except Sunday)
sudo tee /etc/systemd/system/mysql-backup-incremental.timer > /dev/null << 'EOF'
[Unit]
Description=MySQL Incremental Backup Timer
Requires=mysql-backup-incremental.service

[Timer]
OnCalendar=Mon,Tue,Wed,Thu,Fri,Sat 02:00
Persistent=true
RandomizedDelaySec=900

[Install]
WantedBy=timers.target
EOF

# Enable and start timers
sudo systemctl daemon-reload
sudo systemctl enable mysql-backup-full.timer
sudo systemctl enable mysql-backup-incremental.timer
sudo systemctl start mysql-backup-full.timer
sudo systemctl start mysql-backup-incremental.timer

# Check timer status
sudo systemctl list-timers | grep mysql-backup

Backup Recovery Procedures

Recovery is where XtraBackup really shines. Here's how to restore from different backup types:

Preparing Backups for Recovery

# Stop MySQL service
sudo systemctl stop mysql

# Prepare full backup
sudo xtrabackup --prepare --target-dir=/backup/mysql/full/full_20241201_020000

# For compressed backups, decompress first
sudo xtrabackup --decompress --target-dir=/backup/mysql/full/full_20241201_020000
sudo find /backup/mysql/full/full_20241201_020000 -name "*.qp" -delete

# For encrypted backups, decrypt first
sudo xtrabackup --decrypt=AES256 --encrypt-key-file=/etc/mysql/backup/encryption.key \
  --target-dir=/backup/mysql/full/full_20241201_020000
sudo find /backup/mysql/full/full_20241201_020000 -name "*.xbcrypt" -delete

Incremental Backup Recovery

# Prepare base backup
sudo xtrabackup --prepare --apply-log-only --target-dir=/backup/mysql/full/full_20241201_020000

# Apply incremental backups in sequence
sudo xtrabackup --prepare --apply-log-only \
  --target-dir=/backup/mysql/full/full_20241201_020000 \
  --incremental-dir=/backup/mysql/incremental/inc_20241202_020000

# Apply final incremental (without --apply-log-only)
sudo xtrabackup --prepare \
  --target-dir=/backup/mysql/full/full_20241201_020000 \
  --incremental-dir=/backup/mysql/incremental/inc_20241203_020000

Restoring the Database

# Backup current data directory (just in case)
sudo mv /var/lib/mysql /var/lib/mysql.backup.$(date +%Y%m%d_%H%M%S)

# Copy prepared backup to data directory
sudo xtrabackup --copy-back --target-dir=/backup/mysql/full/full_20241201_020000

# Fix ownership and permissions
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod 750 /var/lib/mysql

# Start MySQL service
sudo systemctl start mysql

# Verify recovery
mysql -u root -p -e "SHOW DATABASES; SELECT NOW();"

Performance Monitoring and Optimization

Monitor backup performance and optimize based on your hardware and workload:

# Create monitoring script
sudo tee /usr/local/bin/backup-monitor.sh > /dev/null << 'EOF'
#!/bin/bash

BACKUP_DIR="/backup/mysql"
LOG_DIR="$BACKUP_DIR/logs"

# Generate backup statistics
echo "=== MySQL Backup Statistics ==="
echo "Date: $(date)"
echo

# Backup sizes
echo "Backup Sizes:"
du -sh $BACKUP_DIR/full/* 2>/dev/null | sort -k2 | tail -5
echo

# Recent backup times
echo "Recent Backup Duration:"
grep -h "completed successfully" $LOG_DIR/*.log | tail -10

# Disk usage
echo "Backup Storage Usage:"
df -h $BACKUP_DIR

# Performance metrics
echo "System Resources During Last Backup:"
iostat -x 1 1 | grep -A 20 "Device"
EOF

sudo chmod +x /usr/local/bin/backup-monitor.sh

Here's a performance comparison based on real-world testing:

Database Size mysqldump Time XtraBackup Time Compression Ratio I/O Impact
10 GB 25 minutes 3 minutes 60-70% Low
100 GB 4+ hours 15 minutes 65-75% Medium
1 TB 12+ hours 45 minutes 70-80% Medium

Common Issues and Troubleshooting

Here are the most frequent problems you'll encounter and their solutions:

Permission Issues

# Fix common permission problems
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod 755 /var/lib/mysql
sudo chmod 660 /var/lib/mysql/ib*

# SELinux context issues (if using SELinux)
sudo restorecon -R /var/lib/mysql

Backup Corruption Detection

# Verify backup integrity
sudo xtrabackup --prepare --target-dir=/backup/mysql/full/full_20241201_020000 --check

# Test backup by restoring to temporary location
sudo mkdir -p /tmp/mysql-test
sudo xtrabackup --copy-back --target-dir=/backup/mysql/full/full_20241201_020000 \
  --datadir=/tmp/mysql-test

Storage Space Issues

# Monitor backup storage usage
sudo tee /usr/local/bin/backup-cleanup.sh > /dev/null << 'EOF'
#!/bin/bash

BACKUP_DIR="/backup/mysql"
THRESHOLD=80  # Alert when disk usage exceeds 80%

USAGE=$(df $BACKUP_DIR | tail -1 | awk '{print $5}' | sed 's/%//')

if [ $USAGE -gt $THRESHOLD ]; then
    echo "WARNING: Backup storage usage at ${USAGE}%"
    
    # Emergency cleanup - remove oldest backups
    find $BACKUP_DIR/full -type d -mtime +3 -exec rm -rf {} +
    find $BACKUP_DIR/incremental -type d -mtime +7 -exec rm -rf {} +
    
    echo "Emergency cleanup completed"
fi
EOF

sudo chmod +x /usr/local/bin/backup-cleanup.sh

Advanced Configuration and Best Practices

For production environments, consider these advanced configurations:

Network Backup to Remote Storage

# Stream backup directly to remote server
xtrabackup --backup --stream=xbstream --compress | \
  ssh backup-server "cat > /remote/backup/mysql_$(date +%Y%m%d_%H%M%S).xbstream"

# Or use cloud storage with rclone
xtrabackup --backup --stream=xbstream --compress | \
  rclone rcat remote:mysql-backups/backup_$(date +%Y%m%d_%H%M%S).xbstream

Backup Verification Script

#!/bin/bash
# /usr/local/bin/verify-backups.sh

BACKUP_DIR="/backup/mysql"
VERIFY_DIR="/tmp/mysql-verify"

verify_backup() {
    local backup_path="$1"
    local backup_name=$(basename "$backup_path")
    
    echo "Verifying backup: $backup_name"
    
    # Clean verify directory
    rm -rf "$VERIFY_DIR"
    mkdir -p "$VERIFY_DIR"
    
    # Prepare backup
    if xtrabackup --prepare --target-dir="$backup_path" 2>/dev/null; then
        echo "βœ“ $backup_name: Backup preparation successful"
        return 0
    else
        echo "βœ— $backup_name: Backup preparation failed"
        return 1
    fi
}

# Verify latest full backup
LATEST_FULL=$(find "$BACKUP_DIR/full" -maxdepth 1 -type d -name "full_*" | sort | tail -1)
if [[ -n "$LATEST_FULL" ]]; then
    verify_backup "$LATEST_FULL"
fi

# Clean up
rm -rf "$VERIFY_DIR"

Integration with Monitoring Systems

Integrate backup monitoring with popular monitoring solutions:

Prometheus Metrics Export

# Create metrics exporter
sudo tee /usr/local/bin/backup-metrics.sh > /dev/null << 'EOF'
#!/bin/bash

METRICS_FILE="/var/lib/node_exporter/textfile_collector/mysql_backup.prom"
BACKUP_DIR="/backup/mysql"

# Calculate metrics
LATEST_FULL_SIZE=$(du -sb $(find $BACKUP_DIR/full -maxdepth 1 -type d -name "full_*" | sort | tail -1) 2>/dev/null | cut -f1)
LATEST_FULL_AGE=$((($(date +%s) - $(stat -c %Y $(find $BACKUP_DIR/full -maxdepth 1 -type d -name "full_*" | sort | tail -1) 2>/dev/null)) / 3600))

# Export metrics
cat > "$METRICS_FILE" << EOF
# HELP mysql_backup_size_bytes Size of latest backup in bytes
# TYPE mysql_backup_size_bytes gauge
mysql_backup_size_bytes{type="full"} ${LATEST_FULL_SIZE:-0}

# HELP mysql_backup_age_hours Age of latest backup in hours
# TYPE mysql_backup_age_hours gauge
mysql_backup_age_hours{type="full"} ${LATEST_FULL_AGE:-999}
EOF
EOF

sudo chmod +x /usr/local/bin/backup-metrics.sh

Real-World Use Cases

Here are some practical scenarios where XtraBackup excels:

  • High-traffic e-commerce sites: Backup 500GB+ databases without affecting customer experience
  • SaaS applications: Point-in-time recovery for customer data restoration requests
  • Database migrations: Create consistent snapshots for upgrading between different VPS instances
  • Development environment seeding: Quickly populate staging environments with production data
  • Disaster recovery: Automated geographic replication of backups to dedicated servers in different regions

The key advantages of XtraBackup become evident in production environments where database uptime is critical. Unlike traditional backup methods that can impact performance during backup windows, XtraBackup allows you to maintain consistent backup schedules without service degradation. The ability to perform incremental backups significantly reduces storage requirements and backup times, making it practical to maintain more frequent backup intervals for better recovery point objectives.

For additional configuration options and advanced features, consult the official Percona XtraBackup documentation, which provides comprehensive coverage of all available parameters and use cases. The MySQL official backup documentation also offers valuable insights into backup strategies and best practices that complement XtraBackup's capabilities.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked