mysqldump exports SQL statements. That’s simple and portable, but slow for large databases: a 100 GB database can take hours to dump, and even longer to restore, because every row is re-inserted and every index rebuilt. Percona XtraBackup takes physical backups instead: it copies the InnoDB data files while the server keeps running, and restores are basically a file copy. It’s free, open source, and the standard tool for hot backups of large MySQL databases.

mysqldump vs XtraBackup

mysqldump XtraBackup
Backup type Logical (SQL) Physical (data files)
Speed for large DBs Slow Fast
Restore speed Slow (re-insert, rebuild indexes) Fast (copy files back)
Locks None for InnoDB with --single-transaction None for InnoDB (brief lock for metadata)
Incremental No Yes
Portable across versions Yes Same major version only
Restore single table Easy Possible, more work

Use mysqldump for small databases and migrations. Use XtraBackup when backups or restores take too long. See mysqldump backup and restore.

Install

Install the XtraBackup version that matches your MySQL server (8.0 for MySQL 8.0, 8.4 for MySQL 8.4):

wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo percona-release enable pxb-84-lts      # or: pxb-80 for MySQL 8.0
sudo apt update && sudo apt install -y percona-xtrabackup-84
xtrabackup --version

For MariaDB, use its own fork instead: sudo apt install mariadb-backup (command mariabackup, same options).

Backup user

CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY 'StrongPassword!';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'xtrabackup'@'localhost';
GRANT SELECT ON performance_schema.log_status TO 'xtrabackup'@'localhost';
GRANT SELECT ON performance_schema.keyring_component_status TO 'xtrabackup'@'localhost';

Store the credentials in /root/.my.cnf under [xtrabackup] (mode 600), so they don’t appear in the process list. See MySQL configuration files.

Full backup

sudo xtrabackup --backup --target-dir=/backup/full-$(date +%F)

XtraBackup must run on the database server (it reads the data directory directly), usually as root.

Prepare

A raw backup is not yet consistent: it contains data files copied at slightly different moments, plus the redo log captured during the copy. Prepare applies the log, to make the files consistent:

sudo xtrabackup --prepare --target-dir=/backup/full-2026-11-04

Prepare a copy (not your only backup) if you want to keep applying incrementals later.

Restore

sudo systemctl stop mysql
sudo mv /var/lib/mysql /var/lib/mysql.old            # keep until verified
sudo xtrabackup --copy-back --target-dir=/backup/full-2026-11-04
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql

--copy-back requires an empty data directory. Use --move-back to move files instead of copying them, when disk space is tight.

Incremental backups

Incrementals copy only pages changed since the previous backup, which makes them much smaller and faster:

# Sunday: full
sudo xtrabackup --backup --target-dir=/backup/base
# Monday: incremental on top of the full
sudo xtrabackup --backup --target-dir=/backup/inc1 --incremental-basedir=/backup/base
# Tuesday: incremental on top of Monday
sudo xtrabackup --backup --target-dir=/backup/inc2 --incremental-basedir=/backup/inc1

To restore, prepare the base with --apply-log-only, apply each incremental in order, and prepare the final result normally:

sudo xtrabackup --prepare --apply-log-only --target-dir=/backup/base
sudo xtrabackup --prepare --apply-log-only --target-dir=/backup/base --incremental-dir=/backup/inc1
sudo xtrabackup --prepare --target-dir=/backup/base --incremental-dir=/backup/inc2

Compression and streaming

# compressed backup (zstd)
sudo xtrabackup --backup --compress --compress-threads=4 --target-dir=/backup/full
sudo xtrabackup --decompress --target-dir=/backup/full     # before prepare

# stream straight to another server
sudo xtrabackup --backup --stream=xbstream --target-dir=/tmp | \
  ssh backup@backup-host "xbstream -x -C /backup/db1/$(date +%F)"

Streaming avoids needing free space for a full copy on the database server. Encrypt backups that leave the server (--encrypt, or an encrypted backup tool). See restic backups.

Point-in-time recovery

XtraBackup records the binary log position in xtrabackup_binlog_info. Restore the backup, then replay binlogs from that position, to recover to any moment. See MySQL point-in-time recovery.

Building replicas

A prepared XtraBackup is the fastest way to seed a new replica: copy it to the new server, restore, and start replication from the recorded position or GTID set. See MySQL replication.

Automate and test

  • Schedule: weekly full, daily incrementals (or daily full for smaller databases), with cron or systemd timers. See database backup automation.
  • Keep copies off the server (3-2-1 rule). See the 3-2-1 backup rule.
  • Test restores regularly on a separate VPS. An untested backup isn’t a backup.

Quick reference

Task Command
Full backup xtrabackup --backup --target-dir=DIR
Incremental --incremental-basedir=PREV
Prepare xtrabackup --prepare --target-dir=DIR
Restore xtrabackup --copy-back --target-dir=DIR
Compress --compress, then --decompress
Stream --stream=xbstream | ssh … xbstream -x
MariaDB mariabackup (same options)

Related: MySQL performance tuning and MySQL InnoDB buffer pool.

  • MySQL
  • Backup
  • Database