Un plan de recuperación ante desastres (DR) responde a una pregunta concreta: si este servidor desaparece ahora mismo, ¿cuánto tardas en volver a dar servicio y cuántos datos pierdes? En esta guía convertirás esa pregunta en un plan que funciona en la práctica: definirás objetivos RTO y RPO, automatizarás copias cifradas fuera del servidor con restic y systemd, harás un simulacro de restauración en una máquina limpia y dejarás documentado el procedimiento. El ejemplo usa un servidor web con Nginx y MySQL en Ubuntu 24.04.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS que quieras proteger, por ejemplo un VPS de CubePath. En la guía se llama web01.
  • Un segundo servidor en otra ubicación que actúe como almacenamiento de copias (backup_server_ip), con SSH y espacio suficiente. Debe estar en otro centro de datos o con otro proveedor: una copia en el mismo servidor no sirve para un desastre.
  • Un usuario no root con privilegios sudo en ambos servidores.
  • Un tercer servidor temporal (puede ser un VPS que destruyes al acabar) para el simulacro de restauración del paso 7.

Paso 1: Definir RTO y RPO por servicio

Antes de tocar comandos, decide qué necesitas recuperar y con qué urgencia. Dos métricas guían todo lo demás:

  • RTO (Recovery Time Objective): tiempo máximo que el servicio puede estar caído. Un RTO de 4 horas significa que en 4 horas debe estar funcionando otra vez.
  • RPO (Recovery Point Objective): cantidad máxima de datos que aceptas perder, medida en tiempo. Un RPO de 24 horas significa que basta con una copia diaria; un RPO de 5 minutos exige replicación o copias muy frecuentes.

Haz un inventario de lo que ejecuta cada servidor y asigna objetivos realistas. Un ejemplo:

ServicioDatos críticosRTORPOMétodo
Web (Nginx + PHP)/var/www, /etc/nginx, certificados4 h24 hCopia diaria con restic
Base de datos MySQLTodas las bases4 h24 hmysqldump diario + restic
Configuración del sistema/etc, paquetes instalados8 h24 hCopia diaria con restic

Si algún servicio necesita un RPO de minutos, las copias diarias no bastan: tendrás que añadir replicación a un segundo servidor. Esta guía cubre el caso más habitual, RPO de horas, que es la base sobre la que se construye cualquier otra estrategia.

Anota también las dependencias externas que no están en el servidor: registros DNS, claves de API, credenciales del proveedor, contactos. Las necesitarás en el paso 8.

Paso 2: Preparar el servidor de copias

Las copias deben estar fuera del servidor protegido y en otra ubicación, siguiendo la regla 3-2-1: tres copias de los datos, en dos soportes distintos, una de ellas fuera del sitio.

En el servidor de copias, crea un usuario dedicado y el directorio donde restic guardará el repositorio:

sudo adduser --disabled-password --gecos "" backup
sudo install -d -o backup -g backup -m 700 /srv/restic

En web01, las copias se ejecutarán como root, así que genera una clave SSH para root sin frase de paso:

sudo ssh-keygen -t ed25519 -f /root/.ssh/restic -N "" -C "restic@web01"
sudo cat /root/.ssh/restic.pub

Copia la clave pública que aparece en pantalla y añádela en el servidor de copias:

sudo install -d -o backup -g backup -m 700 /home/backup/.ssh
sudo nano /home/backup/.ssh/authorized_keys

Pega la clave, guarda el archivo y ajusta el propietario y los permisos:

sudo chown backup:backup /home/backup/.ssh/authorized_keys
sudo chmod 600 /home/backup/.ssh/authorized_keys

De vuelta en web01, crea un alias SSH para que restic sepa qué clave usar:

sudo nano /root/.ssh/config
Host backup-server
    HostName backup_server_ip
    User backup
    IdentityFile /root/.ssh/restic

Comprueba la conexión. La primera vez acepta la huella del servidor escribiendo yes:

sudo ssh backup-server 'echo conexión correcta'
conexión correcta

Paso 3: Inicializar un repositorio restic cifrado

restic hace copias incrementales con deduplicación y cifra todo en el cliente, así que el servidor de copias nunca ve los datos en claro. Instálalo desde los repositorios de Ubuntu:

sudo apt update
sudo apt install restic

Genera una contraseña aleatoria para el repositorio y guárdala con permisos restrictivos:

sudo install -d -m 700 /etc/restic
openssl rand -base64 32 | sudo tee /etc/restic/password > /dev/null
sudo chmod 600 /etc/restic/password

Inicializa el repositorio en el servidor de copias:

sudo restic -r sftp:backup-server:/srv/restic/web01 --password-file /etc/restic/password init
created restic repository 3f1c9a7b2e at sftp:backup-server:/srv/restic/web01

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.

Paso 4: Crear el script de copia

Copiar los archivos de datos de MySQL mientras el servicio está en marcha produce copias inconsistentes. Lo correcto es generar un volcado con mysqldump --single-transaction, que obtiene una vista coherente de las tablas InnoDB sin bloquearlas, y que restic copie ese volcado junto con el resto de archivos.

Crea el script:

sudo nano /usr/local/sbin/dr-backup
#!/usr/bin/env bash
set -euo pipefail

export RESTIC_REPOSITORY="sftp:backup-server:/srv/restic/web01"
export RESTIC_PASSWORD_FILE="/etc/restic/password"

DUMP_DIR="/var/backups/mysql"
install -d -m 700 "$DUMP_DIR"

# Volcado coherente de todas las bases de datos
mysqldump --single-transaction --routines --triggers --events --all-databases \
  | gzip > "$DUMP_DIR/all-databases.sql.gz.tmp"
mv "$DUMP_DIR/all-databases.sql.gz.tmp" "$DUMP_DIR/all-databases.sql.gz"

# Lista de paquetes instalados a mano, para reconstruir el sistema
apt-mark showmanual > /var/backups/apt-manual.txt

restic backup --tag daily --exclude-caches \
  /etc /var/www /home /root /usr/local /var/backups

# Retención: 7 diarias, 4 semanales y 6 mensuales
restic forget --tag daily --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

En Ubuntu, root se autentica en MySQL mediante el socket local, por lo que mysqldump no necesita contraseña al ejecutarse como root. Si usas PostgreSQL, sustituye el volcado por sudo -u postgres pg_dumpall | gzip > ....

Hazlo ejecutable y lánzalo una vez a mano:

sudo chmod 750 /usr/local/sbin/dr-backup
sudo /usr/local/sbin/dr-backup

Al final verás un resumen parecido a este:

Files:        8214 new,     0 changed,     0 unmodified
Dirs:         1102 new,     0 changed,     0 unmodified
Added to the repository: 412.337 MiB (138.921 MiB stored)

processed 8214 files, 1.204 GiB in 0:41
snapshot 9b2e4c1a saved

Comprueba que la instantánea existe:

sudo restic -r sftp:backup-server:/srv/restic/web01 --password-file /etc/restic/password snapshots

Paso 5: Programar las copias con un temporizador de systemd

Un temporizador de systemd registra cada ejecución en el journal y, con Persistent=true, lanza la copia perdida si el servidor estaba apagado a la hora prevista.

Crea el servicio:

sudo nano /etc/systemd/system/dr-backup.service
[Unit]
Description=Copia de seguridad para recuperación ante desastres
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/dr-backup
Nice=10
IOSchedulingClass=idle

Crea el temporizador, que se ejecutará cada día a las 02:30 con un margen aleatorio de hasta 15 minutos:

sudo nano /etc/systemd/system/dr-backup.timer
[Unit]
Description=Copia diaria para recuperación ante desastres

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15m
Persistent=true

[Install]
WantedBy=timers.target

Activa el temporizador y comprueba cuándo se ejecutará la próxima vez:

sudo systemctl daemon-reload
sudo systemctl enable --now dr-backup.timer
systemctl list-timers dr-backup.timer
NEXT                        LEFT     LAST PASSED UNIT            ACTIVATES
Fri 2026-09-26 02:37:12 UTC 11h left -    -      dr-backup.timer dr-backup.service

Tras la primera ejecución automática, revisa el resultado con:

sudo journalctl -u dr-backup.service --since today

Una copia que falla en silencio es peor que no tener copia. Integra el estado de dr-backup.service en tu monitorización: por ejemplo, alerta si systemctl is-failed dr-backup.service devuelve failed o si la última instantánea de restic snapshots --latest 1 tiene más de 26 horas.

Paso 6: Verificar la integridad del repositorio

restic check comprueba la estructura del repositorio y, con --read-data-subset, descarga y verifica una parte de los datos. Ejecútalo al menos una vez por semana:

sudo restic -r sftp:backup-server:/srv/restic/web01 --password-file /etc/restic/password check --read-data-subset=10%
using temporary cache in /tmp/restic-check-cache-1234567890
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
read 10.0% of data packs
[0:04] 100.00%  3 / 3 packs
no errors were found

Verificar el repositorio no es lo mismo que comprobar que puedes recuperar el servicio. Para eso está el simulacro.

Paso 7: Hacer un simulacro de restauración

El único modo de conocer tu RTO real es restaurar en un servidor limpio y cronometrarlo. Crea un servidor temporal con Ubuntu 24.04 y anota la hora de inicio.

En el servidor temporal, instala restic y el mismo software que tenía web01:

sudo apt update
sudo apt install restic nginx mysql-server

Copia a este servidor, por un canal seguro, la clave /root/.ssh/restic, el archivo /root/.ssh/config y la contraseña del repositorio (en un desastre real las sacarías de tu gestor de contraseñas). Colócalos en las mismas rutas, con permisos 600, y autoriza la clave en el servidor de copias si usas una distinta. Después conéctate una vez para aceptar la huella del servidor de copias, porque restic no puede responder a esa pregunta:

sudo ssh backup-server true

Lista las instantáneas disponibles:

sudo restic -r sftp:backup-server:/srv/restic/web01 --password-file /etc/restic/password snapshots

Restaura la última instantánea en un directorio aparte, no sobre /, para revisar antes de copiar:

sudo restic -r sftp:backup-server:/srv/restic/web01 --password-file /etc/restic/password \
  restore latest --target /srv/restore

Recupera los datos de la web y la configuración de Nginx:

sudo rsync -a /srv/restore/var/www/ /var/www/
sudo rsync -a /srv/restore/etc/nginx/ /etc/nginx/
sudo nginx -t && sudo systemctl reload nginx

Importa las bases de datos:

gunzip -c /srv/restore/var/backups/mysql/all-databases.sql.gz | sudo mysql

Comprueba que la aplicación responde y que los datos son los esperados:

curl -I -H "Host: your_domain" http://127.0.0.1/
sudo mysql -e "SHOW DATABASES;"
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)

Anota la hora de fin. La diferencia es tu RTO medido; compárala con el objetivo del paso 1. Si ha tardado demasiado, lo habitual es automatizar la instalación del sistema base (con cloud-init o Ansible) para que el simulacro se limite a restaurar datos. Cuando termines, destruye el servidor temporal.

Paso 8: Documentar el plan

Un plan que solo está en la cabeza de una persona no es un plan. Crea un documento corto (un runbook) y guárdalo fuera de la infraestructura que protege, por ejemplo en un repositorio Git o en la wiki del equipo. Esta plantilla cubre lo esencial:

# Plan de recuperación: web01

## Objetivos
- RTO: 4 h · RPO: 24 h
- Último simulacro: 2026-09-25, RTO medido: 1 h 20 min

## Qué se copia y dónde
- Rutas: /etc, /var/www, /home, /root, /usr/local, /var/backups
- Bases de datos: mysqldump diario en /var/backups/mysql
- Repositorio: sftp:backup-server:/srv/restic/web01 (otro centro de datos)
- Contraseña del repositorio y clave SSH: gestor de contraseñas, entrada "restic web01"

## Procedimiento de restauración
1. Crear servidor Ubuntu 24.04 e instalar restic, nginx, mysql-server
2. Recuperar clave SSH, config y contraseña del gestor de contraseñas
3. restic restore latest --target /srv/restore
4. Copiar /var/www y /etc/nginx, importar all-databases.sql.gz
5. Comprobar la web con curl y cambiar el registro DNS A a la nueva IP

## Dependencias externas
- DNS: proveedor, cuenta y registros afectados
- Certificados TLS: se reemiten con certbot tras el cambio de DNS

## Contactos
- Responsable técnico, suplente y proveedor

Actualiza el documento cada vez que cambie algo relevante (una ruta nueva, otra base de datos, un servicio más) y después de cada simulacro.

Solución de problemas

  • Fatal: unable to open config file: ... no such file or directory: la ruta del repositorio es incorrecta o el repositorio no se ha inicializado. Revisa la ruta en el script y en /root/.ssh/config.
  • unable to create lock in backend: repository is already locked: una ejecución anterior se interrumpió. Comprueba que no hay otra copia en marcha y ejecuta restic unlock con los mismos parámetros -r y --password-file.
  • mysqldump: Got error: 1045: Access denied: el script no se está ejecutando como root o root usa contraseña en MySQL. Crea un usuario de copias con los privilegios SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS y guarda sus credenciales en /root/.my.cnf con permisos 600.
  • La copia tarda mucho o satura el disco: los parámetros Nice e IOSchedulingClass=idle del servicio reducen el impacto. Excluye directorios que no necesitas con --exclude, por ejemplo cachés de la aplicación.

Conclusión

Tienes objetivos RTO y RPO definidos, copias cifradas y automáticas en otra ubicación, una verificación periódica del repositorio y un procedimiento de restauración probado y documentado. Repite el simulacro al menos cada trimestre: es la única forma de saber que el plan sigue funcionando. Como siguientes pasos, añade una segunda copia en otro proveedor (restic admite backends compatibles con S3), automatiza la instalación del servidor base con Ansible o cloud-init para reducir el RTO y, si algún servicio necesita un RPO de minutos, configura replicación de la base de datos hacia otro centro de datos.