--- title: "Backups en caliente de MySQL con Percona XtraBackup" description: "Haz copias en caliente de MySQL 8.0 en Ubuntu 24.04 con Percona XtraBackup: completas e incrementales, comprimidas, enviadas por SSH, restauradas y programadas." category: "Operaciones de Base de Datos" language: es source: https://cubepath.com/es/docs/operaciones-de-base-de-datos/percona-xtrabackup-backups-en-caliente-mysql updated: 2026-09-25 --- # Backups en caliente de MySQL con Percona XtraBackup 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-server` de 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 con `sudo du -sh /var/lib/mysql`. - Tablas en InnoDB. XtraBackup también copia tablas MyISAM, pero las bloquea durante su copia. > Importante: la 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: ```bash 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: ```bash 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: ```bash 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: ```bash sudo mysql ``` Ejecuta estas sentencias sustituyendo `your_backup_password`: ```sql 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: ```bash sudo nano /etc/mysql/xtrabackup.cnf ``` ```ini [xtrabackup] user=bkpuser password=your_backup_password ``` ```bash 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`: ```bash 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: ```output ... [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: ```bash sudo cat /backups/mysql/full/xtrabackup_checkpoints ``` ```output 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: ```bash 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: ```bash 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: ```bash 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: ```bash 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: ```bash 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`: ```bash 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`: ```bash xtrabackup --decompress --remove-original --parallel=4 --target-dir="$HOME/mysql-backup" ``` > Consejo: para 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=AES256` con 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: ```bash 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: ```bash sudo xtrabackup --copy-back --target-dir=/backups/mysql/full ``` Devuelve la propiedad de los archivos al usuario `mysql` y arranca el servicio: ```bash 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í: ```bash 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: ```bash sudo cat /backups/mysql/full/xtrabackup_binlog_info ``` ```output binlog.000014 1587 ``` Copia los binlogs del directorio de datos antiguo a un lugar seguro: ```bash 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: ```bash 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: ```bash sudo nano /usr/local/sbin/mysql-xtrabackup.sh ``` ```bash #!/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: ```bash sudo chmod 750 /usr/local/sbin/mysql-xtrabackup.sh ``` Crea un servicio de systemd que lo ejecute: ```bash sudo nano /etc/systemd/system/mysql-xtrabackup.service ``` ```ini [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: ```bash sudo nano /etc/systemd/system/mysql-xtrabackup.timer ``` ```ini [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: ```bash 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: ```bash 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. > Importante: un 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.