Un backup que no has restaurado nunca no es un backup: es una esperanza con timestamp. En esta guía verás cómo diseñar una estrategia completa de backup de MySQL y MariaDB a S3: desde el clásico mysqldump hasta el backup físico en caliente con Mariabackup / Percona XtraBackup y los binary logs para recuperación a cualquier punto en el tiempo (PITR), con cifrado, automatización con cron, inmutabilidad anti-ransomware con Object Lock y, lo más importante, cómo probar de verdad que tus copias restauran.
Lógico vs. físico: dos filosofías de copia
Antes de tocar un comando conviene entender que en MySQL y MariaDB existen dos familias de backup, y no compiten tanto como se complementan.
- Backup lógico (
mysqldump,mariadb-dump): exporta el contenido como sentencias SQL. Es portable entre versiones mayores y entre arquitecturas, ideal para clonar una base concreta o migrar. Su talón de Aquiles es el tiempo: en bases grandes, volcar y restaurar puede llevar horas y, por sí solo, no recupera a un segundo exacto. - Backup físico (copia en caliente de los ficheros de datos InnoDB + binary logs): copia los datos a nivel de fichero con Mariabackup (MariaDB) o Percona XtraBackup (MySQL) sin parar el servidor. Permite Point-in-Time Recovery (PITR) combinándolo con los binlogs, restauraciones mucho más rápidas en volúmenes grandes y backups incrementales. Es el enfoque recomendado en producción.
Una regla práctica: por debajo de unos pocos GB, mysqldump a S3 te resuelve la vida; a partir de ahí, o si necesitas un RPO (objetivo de punto de recuperación) de minutos, vete directo a Mariabackup/XtraBackup con archivado de binlogs.
mysqldump / mariadb-dump: el volcado lógico
En MariaDB 10.x+ el comando se llama mariadb-dump (mysqldump sigue como alias); en MySQL es mysqldump. La clave para un volcado consistente sin bloquear en tablas InnoDB es --single-transaction, que toma una instantánea transaccional. Añade --routines, --triggers y --events para no dejarte procedimientos, disparadores ni eventos programados.
Puedes comprimir y subirlo directamente a tu bucket en streaming, sin escribir en disco intermedio:
#!/usr/bin/env bash
set -euo pipefail
FECHA=$(date +%F)
ENDPOINT=https://es-mad-1.s3.otterstorage.io
BUCKET=backups-mysql
# Volcado consistente de una base InnoDB, comprimido, directo a S3
mysqldump --single-transaction --quick --routines --triggers --events midb \
| gzip -6 \
| aws s3 cp - "s3://${BUCKET}/midb/midb-${FECHA}.sql.gz" \
--endpoint-url "${ENDPOINT}"
# Todas las bases (incluye usuarios/permisos en MariaDB reciente con --system=all)
mysqldump --single-transaction --quick --all-databases \
| gzip -6 \
| aws s3 cp - "s3://${BUCKET}/full/all-${FECHA}.sql.gz" \
--endpoint-url "${ENDPOINT}"
Ojo: --single-transaction solo garantiza consistencia en tablas transaccionales (InnoDB). Si tienes tablas MyISAM necesitarás --lock-tables (que sí bloquea escrituras durante el volcado). Para configurar las credenciales del cliente, sigue la guía de access keys aisladas por bucket: lo ideal es que la clave de este script solo tenga permiso sobre backups-mysql y nada más; apóyate en la documentación de AWS CLI para el perfil. No guardes la contraseña en el comando: usa un fichero ~/.my.cnf con [client] en modo 600.
Cifrar antes de subir
Tus backups contienen datos sensibles, así que cífralos en origen (cliente) para que viajen y reposen cifrados. La forma más simple es cifrar el flujo con GnuPG antes de enviarlo:
# Cifrado simétrico con AES-256 antes de subir
mysqldump --single-transaction --quick midb \
| gzip -6 \
| gpg --symmetric --cipher-algo AES256 --batch --passphrase-file /etc/mysql/backup.key \
| aws s3 cp - s3://backups-mysql/midb/midb-$(date +%F).sql.gz.gpg \
--endpoint-url https://es-mad-1.s3.otterstorage.io
Si prefieres no gestionar tú la criptografía, restic cifra de serie con AES-256 y autenticación Poly1305, y deduplica. Más abajo lo usamos como destino. Revisa también la guía de seguridad para el modelo completo.
Mariabackup / Percona XtraBackup con destino S3
Para producción, el backup físico en caliente es el estándar: copia los ficheros de InnoDB sin detener la base, soporta incrementales y restaura mucho más rápido que reimportar un dump. En MariaDB usa Mariabackup; en MySQL, Percona XtraBackup. Ambos pueden emitir el backup como un flujo (xbstream) que subes directo a S3.
Backup completo en streaming
# MariaDB: backup completo en streaming, comprimido y directo a S3
mariabackup --backup --stream=xbstream --user=bkp --password=*** \
| zstd -3 \
| aws s3 cp - "s3://backups-mysql/phys/full-$(date +%F).xb.zst" \
--endpoint-url https://es-mad-1.s3.otterstorage.io
# MySQL con Percona XtraBackup es equivalente:
# xtrabackup --backup --stream=xbstream --target-dir=/tmp | zstd | aws s3 cp - s3://...
Backup incremental
Los incrementales solo copian las páginas cambiadas desde una referencia, así que son rápidos y ocupan poco. Necesitan conocer el LSN del backup base (Mariabackup lo guarda en xtrabackup_checkpoints).
# Incremental respecto al último backup base local
mariabackup --backup --target-dir=/var/backups/mysql/inc \
--incremental-basedir=/var/backups/mysql/base --user=bkp --password=***
# Y se sincroniza a S3 (ver rclone más abajo)
rclone copy /var/backups/mysql/inc otter:backups-mysql/phys/inc-$(date +%F)
Binary logs para PITR
El backup físico te da una foto; los binary logs te dan la película. Con un backup base + los binlogs posteriores puedes restaurar a un segundo exacto (justo antes de un DROP TABLE accidental). Actívalos en my.cnf:
[mysqld]
server_id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800 # 7 días en disco local
sync_binlog = 1
Archiva los binlogs de forma continua a S3 con mysqlbinlog en modo stream, que va escribiendo los segmentos según se generan; luego un rclone los sincroniza al bucket:
# Lee los binlogs del servidor en crudo y los va guardando localmente
mysqlbinlog --read-from-remote-server --host=127.0.0.1 --user=repl --password=*** \
--raw --stop-never --result-file=/var/backups/mysql/binlog/ mysql-bin.000001 &
# Sincroniza el directorio de binlogs a S3 cada minuto (cron)
rclone sync /var/backups/mysql/binlog otter:backups-mysql/binlog --checksum
Programar con cron
La automatización es lo que convierte un script en una política de backup. Un esquema típico combina un completo semanal, incrementales diarios y archivado continuo de binlogs:
# /etc/cron.d/mysql-backup
# m h dom mon dow usuario comando
0 2 * * 0 mysql /opt/scripts/mariabackup-full.sh
0 2 * * 1-6 mysql /opt/scripts/mariabackup-incremental.sh
*/1 * * * * mysql rclone sync /var/backups/mysql/binlog otter:backups-mysql/binlog --checksum
# Restauración de verificación, primer día de cada mes a las 5:00
0 5 1 * * mysql /opt/scripts/verificar-restore.sh
Con el modelo de OtterStorage sin coste por peticiones (PUT/GET/LIST) ni por borrados, sincronizar binlogs cada minuto o lanzar incrementales frecuentes no infla la factura: solo pagas por TB almacenado. Eso cambia el cálculo respecto a otros proveedores donde cada operación cuenta.
Subir con rclone o restic a un bucket
Si haces backup lógico y quieres una capa de sincronización o cifrado y deduplicación, rclone y restic son excelentes complementos.
rclone para sincronizar volcados
# ~/.config/rclone/rclone.conf
[otter]
type = s3
provider = Other
endpoint = https://es-mad-1.s3.otterstorage.io
region = eu-mad
access_key_id = AKIAOTTEREXAMPLE
secret_access_key = clave_secreta_aqui
# Subir los volcados locales y reflejar en destino los borrados de origen
rclone sync /var/backups/mysql otter:backups-mysql --transfers 8 --checksum --progress
Tienes los detalles en la guía de rclone.
restic para snapshots cifrados y deduplicados
export RESTIC_REPOSITORY="s3:https://es-mad-1.s3.otterstorage.io/backups-mysql/restic"
export RESTIC_PASSWORD_FILE="/etc/mysql/restic.pass"
export AWS_ACCESS_KEY_ID="AKIAOTTEREXAMPLE"
export AWS_SECRET_ACCESS_KEY="clave_secreta_aqui"
# Inicializar el repositorio una vez
restic init
# Backup en streaming del dump (sin tocar disco), con etiqueta
mysqldump --single-transaction --quick midb \
| restic backup --stdin --stdin-filename midb.sql --tag mysql
# Política de retención: 7 diarios, 4 semanales, 6 mensuales
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Consulta la guía de restic para afinar la retención. Restic cifra todo en cliente, así que ni siquiera nosotros podríamos leer tus snapshots.
mysqldump vs. Mariabackup/XtraBackup: cuándo cada uno
| Criterio | mysqldump / mariadb-dump | Mariabackup / XtraBackup |
|---|---|---|
| Tipo de copia | Lógica (SQL) | Física (ficheros) + binary logs |
| PITR (punto en el tiempo) | Solo con binlogs, a mano | Sí, a un segundo exacto |
| Incrementales | No, siempre completo | Sí, completos e incrementales |
| Impacto en el servidor | Carga de CPU/IO al volcar | En caliente, impacto mínimo |
| Rendimiento en bases grandes | Lento (horas) al restaurar | Rápido (copia de ficheros) |
| Portabilidad entre versiones mayores | Alta | Misma versión mayor |
| Cifrado y compresión | Manual (GPG / restic / gzip) | Integrados (--stream + zstd) |
| Mejor caso de uso | Bases pequeñas, migraciones, clonado | Producción, RPO bajo, grandes volúmenes |
Inmutabilidad con Object Lock contra ransomware
El ransomware moderno busca primero tus backups: si los cifra o borra, no tienes alternativa al rescate. La defensa es hacer que las copias sean inmutables. Activa Object Lock (WORM) en el bucket de backups con OtterVault: durante el periodo de retención, ni un atacante con tus credenciales podrá sobrescribir ni borrar los objetos.
OtterStorage ofrece dos modos. Governance permite a usuarios con un permiso especial saltarse la retención (útil para corregir errores); Compliance no admite excepciones ni siquiera para la cuenta raíz, lo que cumple los requisitos de inmutabilidad regulatoria. Para protección a nivel de bucket completo dispones además de Legal Hold.
# Crear el bucket con versionado y Object Lock activado (requisito de WORM)
aws s3api create-bucket --bucket backups-mysql \
--object-lock-enabled-for-bucket \
--endpoint-url https://es-mad-1.s3.otterstorage.io
# Retención por defecto: 30 días en modo Compliance
aws s3api put-object-lock-configuration --bucket backups-mysql \
--object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":30}}}' \
--endpoint-url https://es-mad-1.s3.otterstorage.io
Object Lock exige versionado activo en el bucket. Profundiza en la guía de Object Lock y en versionado; para expirar binlogs antiguos automáticamente, combínalo con reglas de lifecycle.
Probar la restauración (lo que de verdad importa)
Repite con nosotros: una restauración no probada es deuda técnica. Programa restauraciones periódicas en un entorno aislado y comprueba que la base levanta y los datos cuadran. Para un volcado lógico:
# Descargar y restaurar a una base de verificación
aws s3 cp s3://backups-mysql/midb/midb-2026-06-26.sql.gz - \
--endpoint-url https://es-mad-1.s3.otterstorage.io \
| gunzip \
| mysql midb_restore
# Comprobación rápida de integridad
mysql -N -e "SELECT COUNT(*) FROM midb_restore.clientes;"
Con backup físico, la restauración pasa por preparar (aplicar el redo log) y luego copiar de vuelta. La recuperación a un punto exacto añade los binlogs hasta el instante elegido:
# 1) Recuperar y preparar el backup base (descargado y descomprimido a /restore)
mariabackup --prepare --target-dir=/restore
# 2) Restaurar los ficheros (servidor parado) y arrancar
mariabackup --copy-back --target-dir=/restore
chown -R mysql:mysql /var/lib/mysql && systemctl start mariadb
# 3) PITR: reproducir los binlogs hasta justo antes del borrado accidental
mysqlbinlog --stop-datetime="2026-06-26 14:24:59" \
/var/backups/mysql/binlog/mysql-bin.0000* | mysql
Documenta el tiempo que tarda (tu RTO real) y automatiza la verificación mensual con el script que añadimos antes al cron. Sigue el principio 3-2-1: tres copias, en dos medios, una fuera de tu infraestructura. S3 con Object Lock cubre esa copia inmutable externa. Si vienes de otro proveedor, OtterBridge ayuda con la migración asistida y OtterSync replica entre regiones para tener tu copia fuera de sitio en EU-FRA o US-EAST.
Echa un vistazo a la solución de backups y a los precios por TB (HDD 8 €, SSD 16 €, NVMe 45 €, sin coste por peticiones) para dimensionar tu estrategia.
Preguntas frecuentes
¿mysqldump o Mariabackup/XtraBackup para producción? +
¿Cuánto cuesta subir tantos binlogs e incrementales a S3? +
¿Cómo protejo los backups de MySQL contra ransomware? +
Backups inmutables para tus bases de datos
Object Lock, retención y sin coste por peticiones.
Hazte Founding Otter