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.