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.gz en /var/backups/tar/daily/, como las de la guía de copias con tar y gzip.
    • MySQL: volcados your_db-FECHA.sql.gz en /var/backups/mysql/, creados con mysqldump --single-transaction --routines --triggers --events your_db | gzip.
    • PostgreSQL: volcados en formato personalizado your_db-FECHA.dump en /var/backups/postgresql/, creados con pg_dump -Fc your_db.
  • Espacio libre para restaurar al menos una copia completa.

Usa solo las secciones que correspondan a lo que tienes en producción.

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:

ConceptoPreguntaEjemplo
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:

  1. Crea un servidor limpio con el mismo sistema operativo.
  2. Instala los paquetes necesarios (servidor web, PHP, base de datos) desde tu documentación o tus scripts de despliegue.
  3. Descarga las copias desde su ubicación externa, no desde el servidor de producción.
  4. Restaura archivos y bases de datos como en los pasos anteriores, esta vez en sus rutas y nombres reales.
  5. 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 database al restaurar MySQL: no creaste antes la base de datos restore_test.
  • pg_restore: error: role "app_user" does not exist: añade --no-owner y, si el volcado incluye permisos, --no-privileges, o crea los roles antes con un volcado de pg_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.