Percona XtraBackup copia los archivos de datos de InnoDB mientras MySQL sigue atendiendo lecturas y escrituras, y registra en paralelo los cambios que se producen durante la copia. El resultado es un backup físico consistente, mucho más rápido de hacer y de restaurar que un volcado con mysqldump en bases de datos grandes. En esta guía instalarás XtraBackup en Ubuntu 24.04 con MySQL 8.0, harás backups completos e incrementales, los enviarás a otro servidor, restaurarás uno y dejarás programada una copia diaria.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS y MySQL 8.0 (el paquete
mysql-serverde Ubuntu) o Percona Server 8.0, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios
sudo. - Espacio libre para los backups de al menos el tamaño de
/var/lib/mysql. Compruébalo consudo du -sh /var/lib/mysql. - Tablas en InnoDB. XtraBackup también copia tablas MyISAM, pero las bloquea durante su copia.
Importantela versión de XtraBackup debe corresponder a la de MySQL. XtraBackup 8.0 sirve para MySQL 8.0; para MySQL 8.4 necesitas XtraBackup 8.4 (paquete
percona-xtrabackup-84).
Paso 1: Instalar Percona XtraBackup
Instala el paquete percona-release, que configura los repositorios oficiales de Percona:
sudo apt update
sudo apt install -y curl gnupg2 lsb-release
curl -fsSLO https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo apt install -y ./percona-release_latest.generic_all.deb
Activa el repositorio de herramientas e instala XtraBackup 8.0 junto con zstd, que se usa para descomprimir los backups:
sudo percona-release enable-only tools release
sudo apt update
sudo apt install -y percona-xtrabackup-80 zstd
Comprueba que la versión coincide con la de MySQL:
xtrabackup --version
mysql --version
La primera línea de xtrabackup --version indica la versión de XtraBackup y la de MySQL en la que se basa. Si al hacer un backup XtraBackup avisa de que no reconoce la versión del servidor, actualiza el paquete antes de confiar en esas copias.
Paso 2: Crear el usuario de backup
XtraBackup se conecta a MySQL para coordinar la copia. Crea un usuario con los permisos mínimos que indica Percona. Entra en la consola de MySQL:
sudo mysql
Ejecuta estas sentencias sustituyendo your_backup_password:
CREATE USER 'bkpuser'@'localhost' IDENTIFIED BY 'your_backup_password';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkpuser'@'localhost';
GRANT SELECT ON performance_schema.log_status TO 'bkpuser'@'localhost';
GRANT SELECT ON performance_schema.keyring_component_status TO 'bkpuser'@'localhost';
EXIT;
Para no escribir la contraseña en la línea de comandos, guárdala en un archivo de opciones que solo pueda leer root:
sudo nano /etc/mysql/xtrabackup.cnf
[xtrabackup]
user=bkpuser
password=your_backup_password
sudo chmod 600 /etc/mysql/xtrabackup.cnf
Todos los comandos siguientes usan --defaults-extra-file=/etc/mysql/xtrabackup.cnf, que debe ser siempre la primera opción.
Paso 3: Hacer un backup completo
Crea el directorio de backups y lanza una copia completa. Se ejecuta como root porque necesita leer /var/lib/mysql:
sudo mkdir -p /backups/mysql
sudo xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup \
--parallel=4 \
--target-dir=/backups/mysql/full
La copia termina con esta línea si todo ha ido bien:
... [Note] [MY-011825] [Xtrabackup] completed OK!
El directorio contiene, además de los datos, archivos de metadatos. xtrabackup_checkpoints indica el tipo de backup y hasta qué LSN (número de secuencia del registro de InnoDB) llega:
sudo cat /backups/mysql/full/xtrabackup_checkpoints
backup_type = full-backuped
from_lsn = 0
to_lsn = 24681935
last_lsn = 24682011
flushed_lsn = 24681935
Paso 4: Preparar el backup
Los archivos copiados no son consistentes por sí mismos: hay que aplicarles los cambios registrados durante la copia. Este paso se llama preparar y se hace una vez antes de restaurar:
sudo xtrabackup --prepare --target-dir=/backups/mysql/full
Al terminar verás de nuevo completed OK! y xtrabackup_checkpoints mostrará backup_type = full-prepared. Un backup preparado ya no admite incrementales encima, por eso en el siguiente paso se prepara de otra forma.
Paso 5: Hacer backups incrementales
Un backup incremental copia solo las páginas de InnoDB que han cambiado desde el backup anterior, lo que ahorra tiempo y espacio. Parte de un backup completo sin preparar:
sudo xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup --target-dir=/backups/mysql/base
Horas después, crea el primer incremental tomando base como referencia:
sudo xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup --target-dir=/backups/mysql/inc1 \
--incremental-basedir=/backups/mysql/base
El segundo incremental toma como referencia el primero:
sudo xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup --target-dir=/backups/mysql/inc2 \
--incremental-basedir=/backups/mysql/inc1
Para restaurar, se prepara la base y se le aplican los incrementales en orden. Todos los pasos salvo el último llevan --apply-log-only, que evita deshacer transacciones que un incremental posterior podría completar:
sudo xtrabackup --prepare --apply-log-only --target-dir=/backups/mysql/base
sudo xtrabackup --prepare --apply-log-only --target-dir=/backups/mysql/base \
--incremental-dir=/backups/mysql/inc1
sudo xtrabackup --prepare --target-dir=/backups/mysql/base \
--incremental-dir=/backups/mysql/inc2
Después de esto, /backups/mysql/base contiene el estado de la base de datos en el momento de inc2 y está listo para restaurar.
Paso 6: Comprimir y enviar backups a otro servidor
Un backup en el mismo disco que la base de datos no te protege si pierdes el servidor. XtraBackup puede emitir la copia como un flujo xbstream que se envía por SSH y se desempaqueta en destino sin pasar por el disco local. El servidor remoto necesita tener instalado XtraBackup, que incluye la utilidad xbstream.
Envía un backup comprimido con zstd, sustituyendo your_user y backup_server_ip:
sudo xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup --compress=zstd --compress-threads=4 --stream=xbstream \
--target-dir=/tmp | \
ssh your_user@backup_server_ip "mkdir -p ~/mysql-backup && xbstream -x -C ~/mysql-backup"
En el servidor remoto, los archivos llegan con extensión .zst. Antes de preparar el backup hay que descomprimirlo, para lo que ese servidor también necesita el paquete zstd:
xtrabackup --decompress --remove-original --parallel=4 --target-dir="$HOME/mysql-backup"
Consejopara cifrar además el backup, genera una clave con
openssl rand -base64 24, guárdala en un archivo protegido y añade--encrypt=AES256 --encrypt-key-file=/ruta/clave. Para restaurar necesitarás--decrypt=AES256con la misma clave antes de descomprimir. Sin la clave el backup es irrecuperable.
Paso 7: Restaurar un backup
La restauración sustituye el directorio de datos completo, así que requiere detener MySQL. Usa un backup ya preparado (paso 4 o paso 5).
Detén MySQL y aparta el directorio de datos actual en lugar de borrarlo:
sudo systemctl stop mysql
sudo mv /var/lib/mysql /var/lib/mysql.old
sudo mkdir /var/lib/mysql
Copia el backup al directorio de datos. --copy-back lee la ruta de datadir de la configuración de MySQL y exige que el directorio esté vacío:
sudo xtrabackup --copy-back --target-dir=/backups/mysql/full
Devuelve la propiedad de los archivos al usuario mysql y arranca el servicio:
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod 750 /var/lib/mysql
sudo systemctl start mysql
Comprueba que MySQL está activo y que tus datos están ahí:
sudo systemctl status mysql --no-pager
sudo mysql -e "SHOW DATABASES;"
Cuando hayas verificado la restauración, puedes borrar /var/lib/mysql.old. Antes, copia de ahí los binlogs si vas a hacer una recuperación a un punto en el tiempo.
Recuperación a un punto en el tiempo
Si el binlog está activo (lo está por defecto en MySQL 8.0, con archivos binlog.000NNN en el directorio de datos), puedes aplicar los cambios posteriores al backup hasta justo antes de un incidente. El backup guarda la posición exacta en la que termina:
sudo cat /backups/mysql/full/xtrabackup_binlog_info
binlog.000014 1587
Copia los binlogs del directorio de datos antiguo a un lugar seguro:
sudo mkdir -p /root/binlogs
sudo sh -c 'cp /var/lib/mysql.old/binlog.[0-9]* /root/binlogs/'
Aplica los binlogs desde esa posición hasta la fecha deseada:
sudo mysqlbinlog --start-position=1587 --stop-datetime="2026-09-25 14:29:59" \
/root/binlogs/binlog.000014 /root/binlogs/binlog.000015 | sudo mysql
Paso 8: Automatizar el backup diario
Un script sencillo hace un backup completo comprimido cada noche en un directorio con fecha y elimina los de más de siete días. Créalo:
sudo nano /usr/local/sbin/mysql-xtrabackup.sh
#!/usr/bin/env bash
set -euo pipefail
BACKUP_ROOT="/backups/mysql/daily"
RETENTION_DAYS=7
TARGET="${BACKUP_ROOT}/$(date +%F_%H%M)"
mkdir -p "$BACKUP_ROOT"
xtrabackup --defaults-extra-file=/etc/mysql/xtrabackup.cnf \
--backup \
--compress=zstd \
--parallel=4 \
--target-dir="$TARGET"
find "$BACKUP_ROOT" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -exec rm -rf {} +
Hazlo ejecutable:
sudo chmod 750 /usr/local/sbin/mysql-xtrabackup.sh
Crea un servicio de systemd que lo ejecute:
sudo nano /etc/systemd/system/mysql-xtrabackup.service
[Unit]
Description=Backup de MySQL con Percona XtraBackup
After=mysql.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/mysql-xtrabackup.sh
Y un temporizador que lo lance cada día a las 02:30:
sudo nano /etc/systemd/system/mysql-xtrabackup.timer
[Unit]
Description=Backup diario de MySQL
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
Activa el temporizador y lanza una ejecución manual para probarlo:
sudo systemctl daemon-reload
sudo systemctl enable --now mysql-xtrabackup.timer
sudo systemctl start mysql-xtrabackup.service
Comprueba el resultado y la próxima ejecución programada:
sudo journalctl -u mysql-xtrabackup.service -n 5 --no-pager
systemctl list-timers mysql-xtrabackup.timer
El registro debe terminar con completed OK!. Si el script falla, systemd marca el servicio como failed, lo que puedes vigilar con tu sistema de monitorización.
Importanteun backup que nunca se ha restaurado no está probado. Descomprime y prepara periódicamente una copia en otro servidor y arranca MySQL sobre ella para confirmar que es válida.
Solución de problemas
Access denied for user 'bkpuser'@'localhost'. Revisa la contraseña en /etc/mysql/xtrabackup.cnf y que --defaults-extra-file es la primera opción del comando; si va detrás de otra, se ignora.
Unsupported server version o avisos de versión al empezar. El servidor MySQL es más reciente que XtraBackup. Actualiza con sudo apt update && sudo apt install --only-upgrade percona-xtrabackup-80, o usa la serie de XtraBackup que corresponda a tu MySQL.
--copy-back falla con Original data directory /var/lib/mysql is not empty!. El directorio de datos debe estar vacío. Muévelo como en el paso 7 en lugar de copiar encima.
MySQL no arranca tras restaurar. Revisa sudo journalctl -u mysql -n 50 y /var/log/mysql/error.log. La causa más frecuente es no haber cambiado el propietario de los archivos a mysql o haber restaurado un backup sin preparar.
Conclusión
Has instalado Percona XtraBackup, hecho backups completos e incrementales sin detener MySQL, enviado una copia comprimida a otro servidor, restaurado un backup y programado copias diarias con un temporizador de systemd. Como siguientes pasos, envía los backups diarios a almacenamiento externo compatible con S3 con xbcloud, combina un completo semanal con incrementales diarios para ahorrar espacio y activa la retención de binlogs suficiente para cubrir el intervalo entre backups.
