Percona XtraBackup is an open-source hot backup utility for MySQL and Percona Server that performs physical backups without locking tables or interrupting active queries on InnoDB tables. Unlike mysqldump, which produces logical SQL dumps, XtraBackup copies InnoDB data files directly and replays the transaction log to bring the backup to a consistent state, making it far faster for large databases. It supports full backups, incremental backups based on LSN (Log Sequence Number), and streaming backups to remote storage. This tutorial covers installing Percona XtraBackup 8.0 on RHEL 8 and performing full, incremental, and restore operations.

Prerequisites

  • RHEL 8 server running MySQL 8.0 or Percona Server 8.0
  • Root or sudo privileges
  • Sufficient disk space — backup directory needs at least as much free space as the MySQL data directory
  • A MySQL user with the following privileges: BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT

Step 1 — Install the Percona Repository and XtraBackup

Percona provides an official repository tool that configures the correct RPM repo for your OS version. Install it first, then use dnf to install XtraBackup 8.0.

# Download and install the Percona repository package
dnf install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm

# Enable the Percona XtraBackup 8.0 repository
percona-release enable-only tools release

# Install XtraBackup
dnf install -y percona-xtrabackup-80

# Verify the installed version
xtrabackup --version

Step 2 — Create a Dedicated Backup User

Create a MySQL user with the minimum required privileges for XtraBackup. Using a dedicated backup user rather than root follows the principle of least privilege.

mysql -u root -p << 'SQL'
CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY 'StrongBackupPass123!';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'xtrabackup'@'localhost';
FLUSH PRIVILEGES;
SQL

Step 3 — Take a Full Backup

The --backup flag copies InnoDB data files and logs to the target directory. The backup is not yet in a consistent state at this point — the prepare step handles that.

# Create the backup destination directory
mkdir -p /backup/full

# Run the full backup
xtrabackup 
  --backup 
  --user=xtrabackup 
  --password='StrongBackupPass123!' 
  --target-dir=/backup/full

# The backup is complete when you see:
# "completed OK!" at the end of output

# Review backup metadata
cat /backup/full/xtrabackup_info

Step 4 — Prepare the Full Backup

The prepare step applies the InnoDB transaction log to the data files, rolling back uncommitted transactions and making the backup crash-consistent. This must be done before restoration. When planning incremental backups, add --apply-log-only here to preserve future incremental merging.

# Prepare the full backup (makes it consistent)
xtrabackup --prepare --target-dir=/backup/full

# Output ends with:
# "completed OK!"
# The backup is now ready to restore

Step 5 — Take and Prepare Incremental Backups

Incremental backups copy only InnoDB pages modified since the base backup, identified by comparing LSN values. Chain them back to the full backup using --incremental-basedir.

# First incremental — based on the full backup
mkdir -p /backup/inc1

xtrabackup 
  --backup 
  --user=xtrabackup 
  --password='StrongBackupPass123!' 
  --target-dir=/backup/inc1 
  --incremental-basedir=/backup/full

# Second incremental — based on inc1
mkdir -p /backup/inc2

xtrabackup 
  --backup 
  --user=xtrabackup 
  --password='StrongBackupPass123!' 
  --target-dir=/backup/inc2 
  --incremental-basedir=/backup/inc1

# Prepare: re-prepare full with --apply-log-only, then merge incrementals
xtrabackup --prepare --apply-log-only --target-dir=/backup/full

xtrabackup --prepare --apply-log-only 
  --target-dir=/backup/full 
  --incremental-dir=/backup/inc1

# Last incremental does NOT use --apply-log-only
xtrabackup --prepare 
  --target-dir=/backup/full 
  --incremental-dir=/backup/inc2

Step 6 — Restore the Backup

Stop MySQL, clear the existing data directory, then copy the prepared backup files back. Use xtrabackup --copy-back or rsync for the file transfer, then fix ownership before restarting MySQL.

# Stop MySQL before restoring
systemctl stop mysqld

# Empty the data directory (adjust path if different)
rm -rf /var/lib/mysql/*

# Method 1 — use xtrabackup copy-back
xtrabackup --copy-back --target-dir=/backup/full

# Method 2 — use rsync (faster for large datasets)
# rsync -avrP /backup/full/ /var/lib/mysql/

# Fix file ownership — MySQL runs as the mysql user
chown -R mysql:mysql /var/lib/mysql

# Start MySQL and verify
systemctl start mysqld
systemctl status mysqld

mysql -u root -p -e "SHOW DATABASES;"

Conclusion

You have set up a complete backup strategy for MySQL 8.0 on RHEL 8 using Percona XtraBackup, covering full backups, incremental backups chained by LSN, and the two-phase prepare-then-restore workflow. Because XtraBackup operates at the physical file level rather than generating SQL, backup and restore times scale with data file size rather than row count, making it significantly faster than mysqldump for databases in the tens or hundreds of gigabytes. Incorporating this into a nightly cron job with offsite copies to object storage provides a robust, low-RTO recovery capability.

Next steps: How to Stream XtraBackup to Amazon S3 on RHEL 8, How to Automate MySQL Backups with Cron on RHEL 8, and How to Encrypt XtraBackup Archives on RHEL 8.