Una copia de seguridad que nunca se ha restaurado es solo una suposición. Los fallos típicos (archivos truncados, directorios que faltaban en la lista, volcados vacíos, contraseñas de cifrado perdidas) solo aparecen el día que intentas recuperar los datos. En este tutorial restaurarás en un entorno aislado de Ubuntu 24.04 una copia de archivos en .tar.gz, un volcado de MySQL y uno de PostgreSQL, comprobarás que están completos, medirás cuánto tarda la recuperación y automatizarás una prueba semanal con systemd.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS donde hacer las pruebas, por ejemplo un VPS de CubePath. Lo ideal es un servidor distinto del de producción, con el mismo sistema y versiones de software.
- Un usuario no root con privilegios
sudo. - Copias existentes que probar. Los ejemplos suponen:
- Archivos: copias
.tar.gzen/var/backups/tar/daily/, como las de la guía de copias con tar y gzip. - MySQL: volcados
your_db-FECHA.sql.gzen/var/backups/mysql/, creados conmysqldump --single-transaction --routines --triggers --events your_db | gzip. - PostgreSQL: volcados en formato personalizado
your_db-FECHA.dumpen/var/backups/postgresql/, creados conpg_dump -Fc your_db.
- Archivos: copias
- Espacio libre para restaurar al menos una copia completa.
Usa solo las secciones que correspondan a lo que tienes en producción.
Advertenciano restaures nunca sobre los datos de producción para "probar". Todos los pasos de esta guía restauran en un directorio temporal o en una base de datos con otro nombre.
Paso 1: Definir qué significa una restauración correcta
Antes de restaurar nada, anota para cada servicio dos cifras y un criterio de éxito. Así la prueba tiene un resultado objetivo:
| Concepto | Pregunta | Ejemplo |
|---|---|---|
| RPO (punto de recuperación) | ¿Cuántos datos puedes permitirte perder? | 24 horas: copia diaria |
| RTO (tiempo de recuperación) | ¿Cuánto puede tardar la restauración? | 1 hora |
| Criterio de éxito | ¿Qué debe funcionar tras restaurar? | La web carga y muestra los pedidos de ayer |
Apunta también, junto a la documentación de la copia, dónde están las copias, qué claves o contraseñas hacen falta y quién tiene acceso a ellas. Si esa información solo está en el servidor que quieres recuperar, la prueba ya ha encontrado el primer fallo.
Paso 2: Comprobar la antigüedad y la integridad de las copias
La comprobación más barata es ver que la última copia existe, es reciente y no está vacía:
ls -lht /var/backups/tar/daily/ | head -3
ls -lht /var/backups/mysql/ | head -3
total 312M
-rw-r--r-- 1 root root 48M Sep 25 03:07 backup-2026-09-25_0307.tar.gz
-rw-r--r-- 1 root root 48M Sep 24 03:11 backup-2026-09-24_0311.tar.gz
Si el tamaño cae bruscamente de un día para otro, algo ha dejado de incluirse. Comprueba después que los archivos comprimidos se pueden leer completos:
sudo gzip -t /var/backups/tar/daily/backup-2026-09-25_0307.tar.gz && echo OK
sudo gzip -t /var/backups/mysql/your_db-2026-09-25.sql.gz && echo OK
OK
OK
Un volcado de mysqldump que terminó correctamente acaba con la línea -- Dump completed. Si falta, el volcado se interrumpió:
zcat /var/backups/mysql/your_db-2026-09-25.sql.gz | tail -1
-- Dump completed on 2026-09-25 3:05:12
Para PostgreSQL, pg_restore --list lee el índice del volcado sin restaurarlo. Si el archivo está dañado, falla:
sudo -u postgres pg_restore --list /var/backups/postgresql/your_db-2026-09-25.dump | head -5
;
; Archive created at 2026-09-25 03:04:01 UTC
; dbname: your_db
; TOC Entries: 214
; Compression: gzip
Estas comprobaciones detectan copias corruptas, pero no copias incompletas. Para eso hay que restaurar.
Paso 3: Restaurar archivos en un directorio aislado
Restaura la última copia en un directorio temporal y mide cuánto tarda con time:
sudo mkdir -p /srv/restore-test
time sudo tar -xzf /var/backups/tar/daily/backup-2026-09-25_0307.tar.gz -C /srv/restore-test
real 0m21.418s
user 0m3.102s
sys 0m2.550s
Comprueba que están los archivos críticos que tu servicio necesita para arrancar. Adapta la lista a tu caso:
sudo ls -l /srv/restore-test/etc/nginx/nginx.conf \
/srv/restore-test/etc/letsencrypt/live \
/srv/restore-test/var/www/your_domain/index.php
Compara la copia con el sistema actual. diff -rq lista solo los archivos que difieren o que faltan en uno de los lados:
sudo diff -rq /etc/nginx /srv/restore-test/etc/nginx
sudo diff -rq /var/www/your_domain /srv/restore-test/var/www/your_domain | head
Algunas diferencias son normales (archivos modificados después de la copia). Lo que debe alarmarte es un Only in /var/www/your_domain: uploads: ese directorio no se está copiando.
Revisa también que los propietarios se han conservado, porque una aplicación restaurada con permisos incorrectos no arrancará:
sudo stat -c '%U:%G %a %n' /srv/restore-test/var/www/your_domain /srv/restore-test/etc/ssh/sshd_config
www-data:www-data 755 /srv/restore-test/var/www/your_domain
root:root 644 /srv/restore-test/etc/ssh/sshd_config
Borra el directorio al terminar:
sudo rm -rf /srv/restore-test
Paso 4: Restaurar un volcado de MySQL en una base de datos temporal
Si el volcado se creó con --databases, contiene sus propias sentencias CREATE DATABASE y USE y restauraría sobre la base de datos original. Comprueba antes de restaurar que no las contiene:
zcat /var/backups/mysql/your_db-2026-09-25.sql.gz | grep -m1 -E '^(USE|CREATE DATABASE)'
Si este comando no devuelve nada, es seguro restaurar en otra base de datos.
En Ubuntu 24.04, el usuario root de MySQL se autentica por socket, así que sudo mysql entra sin contraseña. Crea una base de datos con otro nombre y restaura el volcado en ella:
sudo mysql -e "CREATE DATABASE restore_test CHARACTER SET utf8mb4"
time (zcat /var/backups/mysql/your_db-2026-09-25.sql.gz | sudo mysql restore_test)
Compara el número de tablas y las filas de las tablas más importantes con producción. Si la prueba se hace en otro servidor, ejecuta la consulta de producción allí:
sudo mysql -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='restore_test'"
sudo mysql -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='your_db'"
sudo mysql -N -e "SELECT COUNT(*), MAX(created_at) FROM restore_test.orders"
48213 2026-09-25 02:58:41
La fecha más reciente debe estar cerca de la hora de la copia; eso confirma que el RPO se cumple. Las columnas table_rows de information_schema son estimaciones en InnoDB, así que usa COUNT(*) para comparar.
Pasa también una comprobación de tablas y elimina la base de datos de prueba:
sudo mysqlcheck --check restore_test
sudo mysql -e "DROP DATABASE restore_test"
Paso 5: Restaurar un volcado de PostgreSQL
Crea una base de datos vacía y restaura el volcado con pg_restore. La opción --no-owner evita errores si en el servidor de prueba no existen los mismos roles que en producción, y --exit-on-error hace que cualquier fallo detenga la restauración en lugar de pasar desapercibido:
sudo -u postgres createdb restore_test
time sudo -u postgres pg_restore --no-owner --exit-on-error -d restore_test /var/backups/postgresql/your_db-2026-09-25.dump
Compara el número de tablas y las filas de una tabla importante:
sudo -u postgres psql -d restore_test -Atc "SELECT count(*) FROM pg_tables WHERE schemaname='public'"
sudo -u postgres psql -d restore_test -Atc "SELECT count(*), max(created_at) FROM orders"
37
48213|2026-09-25 02:58:41+00
Elimina la base de datos de prueba:
sudo -u postgres dropdb restore_test
Paso 6: Probar la aplicación completa
Que los archivos y los datos estén no significa que el servicio arranque. Al menos una vez por trimestre, restaura la aplicación entera en un servidor nuevo siguiendo solo tu documentación, como si el original hubiera desaparecido:
- Crea un servidor limpio con el mismo sistema operativo.
- Instala los paquetes necesarios (servidor web, PHP, base de datos) desde tu documentación o tus scripts de despliegue.
- Descarga las copias desde su ubicación externa, no desde el servidor de producción.
- Restaura archivos y bases de datos como en los pasos anteriores, esta vez en sus rutas y nombres reales.
- Comprueba el sitio sin tocar el DNS, forzando la resolución hacia el servidor nuevo:
curl -sI --resolve your_domain:443:your_test_server_ip https://your_domain
HTTP/2 200
server: nginx
content-type: text/html; charset=UTF-8
Anota el tiempo total desde que empiezas hasta que el sitio responde y compáralo con el RTO del paso 1. Apunta también cada paso que no estaba documentado o que falló: esa lista es el resultado más útil del simulacro.
Paso 7: Automatizar una prueba semanal
Los pasos 2 a 4 se pueden automatizar. Este script restaura la última copia de archivos y el último volcado de MySQL, comprueba lo mínimo y devuelve un error si algo falla, de modo que systemd marque el servicio como fallido. Adapta las rutas, el archivo crítico y la tabla a tu caso:
sudo nano /usr/local/sbin/backup-restore-test.sh
#!/usr/bin/env bash
set -euo pipefail
TAR_DIR="/var/backups/tar/daily"
SQL_DIR="/var/backups/mysql"
WORK="$(mktemp -d /srv/restore-test.XXXXXX)"
TEST_DB="restore_test"
cleanup() {
rm -rf "$WORK"
mysql -e "DROP DATABASE IF EXISTS ${TEST_DB}"
}
trap cleanup EXIT
latest() {
find "$1" -maxdepth 1 -type f -name "$2" -printf '%T@ %p\n' | sort -n | tail -n1 | cut -d' ' -f2-
}
latest_tar="$(latest "$TAR_DIR" 'backup-*.tar.gz')"
latest_sql="$(latest "$SQL_DIR" '*.sql.gz')"
# Las copias deben existir y tener menos de 26 horas.
for f in "$latest_tar" "$latest_sql"; do
if [ -z "$f" ] || [ -z "$(find "$f" -mmin -1560)" ]; then
echo "ERROR: copia ausente o antigua: ${f:-$TAR_DIR / $SQL_DIR}" >&2
exit 1
fi
done
echo "Restaurando $latest_tar"
tar -xzf "$latest_tar" -C "$WORK"
test -s "$WORK/etc/nginx/nginx.conf"
test -d "$WORK/var/www"
echo "Restaurando $latest_sql"
mysql -e "DROP DATABASE IF EXISTS ${TEST_DB}; CREATE DATABASE ${TEST_DB}"
zcat "$latest_sql" | mysql "$TEST_DB"
rows="$(mysql -N -e "SELECT COUNT(*) FROM ${TEST_DB}.orders")"
if [ "$rows" -eq 0 ]; then
echo "ERROR: la tabla orders está vacía" >&2
exit 1
fi
echo "Prueba de restauración correcta: orders=${rows}"
La función latest elige el archivo más reciente por fecha de modificación. Hazlo ejecutable y pruébalo:
sudo chmod 750 /usr/local/sbin/backup-restore-test.sh
sudo /usr/local/sbin/backup-restore-test.sh
Restaurando /var/backups/tar/daily/backup-2026-09-25_0307.tar.gz
Restaurando /var/backups/mysql/your_db-2026-09-25.sql.gz
Prueba de restauración correcta: orders=48213
Crea el servicio y el temporizador para ejecutarlo cada domingo a las 05:00:
sudo nano /etc/systemd/system/backup-restore-test.service
[Unit]
Description=Prueba semanal de restauración de copias
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-restore-test.sh
Nice=10
IOSchedulingClass=idle
sudo nano /etc/systemd/system/backup-restore-test.timer
[Unit]
Description=Ejecuta la prueba de restauración cada semana
[Timer]
OnCalendar=Sun *-*-* 05:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup-restore-test.timer
systemctl list-timers backup-restore-test.timer
Revisa el resultado de la última ejecución con:
systemctl status backup-restore-test.service --no-pager
journalctl -u backup-restore-test.service -n 20 --no-pager
Si la prueba falla, el servicio aparecerá como failed. Conéctalo a tu sistema de alertas: la mayoría de herramientas de monitorización pueden vigilar unidades de systemd fallidas (systemctl --failed).
Solución de problemas
ERROR 1049 (42000): Unknown databaseal restaurar MySQL: no creaste antes la base de datosrestore_test.pg_restore: error: role "app_user" does not exist: añade--no-ownery, si el volcado incluye permisos,--no-privileges, o crea los roles antes con un volcado depg_dumpall --globals-only.- La restauración de archivos llena el disco: restaura en un volumen con más espacio o extrae solo los directorios que quieras comprobar indicando sus rutas al final del comando
tar. - El sitio restaurado no arranca aunque los datos están: suele faltar algo que no estaba en la copia (certificados, crontabs, archivos
.env, unidades de systemd propias). Añádelo a la lista de rutas y repite la prueba.
Conclusión
Ahora compruebas la integridad de las copias, las restauras en un entorno aislado, validas su contenido con datos reales y mides el tiempo de recuperación frente a tu RTO. La prueba automática semanal cubre los fallos más comunes y el simulacro trimestral encuentra los que no se pueden automatizar. Como siguientes pasos, guarda las copias fuera del servidor con rclone, documenta el procedimiento de restauración junto a las copias y revisa el RPO y el RTO cada vez que cambie la aplicación.
