La forma más segura de hacer copias de seguridad de un VPS Linux es enviar cada día snapshots cifrados con restic a un almacenamiento fuera del servidor, mantener una política de retención clara y probar la restauración con regularidad. Esta guía lo configura en Debian 12/13, Ubuntu 22.04/24.04 y Rocky Linux / AlmaLinux: instalar restic, elegir un repositorio externo (almacenamiento compatible con S3, SFTP o rclone), volcar las bases de datos, programarlo con un timer de systemd y comprobar que de verdad puedes recuperar tus datos.
Por qué tus copias deben estar fuera del VPS
Una copia en el mismo servidor no es una copia de seguridad: lo que elimine el servidor elimina también la copia. En un VPS cloud de JUSTG, varias acciones normales borran los datos para siempre:
- Cambio de ubicación (Change location): se instala un VPS nuevo con otra IP y no se copia nada. En cuanto está listo, el VPS antiguo y todos sus datos se eliminan.
- Renovación vencida: un servicio impagado se suspende en la fecha de vencimiento; si la factura sigue sin pagar 7 días después, el servicio se cancela y sus datos se eliminan.
- Reinstalar el sistema operativo borra el disco, y una cancelación Immediate elimina el servicio y sus datos en cuanto se procesa.
Súmale un rm -rf mal escrito, una actualización que sale mal o un servidor comprometido, y la regla queda clara: la única copia en la que puedes confiar es la que haces tú y guardas en otro sitio. Antes de cualquier cambio de ubicación, reinstalación o cancelación, lanza una copia a mano y comprueba que existe el snapshot nuevo.
Paso 1: Instala restic
Debian 12/13, Ubuntu 24.04 y EPEL traen versiones suficientemente recientes:
# Debian 12/13, Ubuntu 24.04
apt update && apt install -y restic
restic version
# Rocky Linux / AlmaLinux 8, 9 (from EPEL)
dnf install -y epel-release && dnf install -y restic
Ubuntu 22.04 trae restic 0.12, que no tiene compresión ni admite el formato de repositorio actual. Ahí, instala el binario oficial y verifica su checksum:
# Official static binary (Ubuntu 22.04 or any distro with an old restic)
VER=0.18.1 # check https://github.com/restic/restic/releases for the latest version
cd /tmp
curl -fLO https://github.com/restic/restic/releases/download/v${VER}/restic_${VER}_linux_amd64.bz2
curl -fLO https://github.com/restic/restic/releases/download/v${VER}/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS # must print: OK
bunzip2 restic_${VER}_linux_amd64.bz2
install -m 0755 restic_${VER}_linux_amd64 /usr/local/bin/restic
restic version
Paso 2: Elige un repositorio externo
El repositorio es donde se guardan los snapshots cifrados. Elige una opción y guárdala en /etc/restic/env (líneas simples NAME=value).
Opción A: almacenamiento de objetos compatible con S3
Sirve cualquier proveedor con API S3. Crea un bucket y una clave de acceso limitada a ese bucket, y usa el endpoint del proveedor en la URL:
# /etc/restic/env (S3-compatible object storage)
RESTIC_REPOSITORY=s3:https://s3.example.com/example-backups/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
AWS_ACCESS_KEY_ID=REPLACE_WITH_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=REPLACE_WITH_SECRET_KEY
# Optional: monitoring ping URL (Step 11)
HC_PING_URL=
Opción B: SFTP a un segundo servidor
Usa otro servidor que controles, mejor en otra ubicación o con otro proveedor, con un usuario dedicado backup propietario de /srv/restic:
# /etc/restic/env (SFTP to a second server)
RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
HC_PING_URL=
# One-time SSH key login for root on the VPS
ssh-keygen -t ed25519 -N "" -f /root/.ssh/id_ed25519 # skip if the key exists
ssh-copy-id [email protected]
ssh [email protected] true # accept the host key once
Opción C: un backend de rclone
rclone conecta restic con decenas de servicios (Backblaze B2, Google Drive, OneDrive, WebDAV y más):
# Install and configure rclone (interactive wizard, run as root)
apt install -y rclone # Rocky/Alma: dnf install -y rclone (EPEL)
rclone config # create a remote, for example named "offsite"
# /etc/restic/env (any rclone backend)
RESTIC_REPOSITORY=rclone:offsite:example-backups/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
RCLONE_CONFIG=/root/.config/rclone/rclone.conf
HC_PING_URL=
/etc/restic/env normalmente también permiten borrar copias, así que un atacante que se haga con el VPS podría vaciar el repositorio. Para más protección, activa el versionado o el object lock del bucket, o usa un destino de solo añadir, y no guardes la contraseña de la cuenta de almacenamiento en el servidor.Paso 3: Crea el archivo de contraseña y el repositorio
install -d -m 0700 /etc/restic
openssl rand -base64 32 > /etc/restic/password
chmod 600 /etc/restic/password
nano /etc/restic/env # paste one of the examples above
chmod 600 /etc/restic/env
# Load the settings into the current shell and create the repository
set -a; . /etc/restic/env; set +a
restic init
/etc/restic/password y /etc/restic/env a un gestor de contraseñas u otro lugar seguro fuera del servidor: si el VPS desaparece, son lo único que necesitas para restaurar.Paso 4: Decide qué incluir y qué excluir
Copia la configuración y los datos, no todo el sistema operativo: reinstalar el sistema es más rápido que restaurar archivos del sistema, y los snapshots se mantienen pequeños.
# /etc/restic/include - one path per line; delete lines that do not exist
/etc
/root
/home
/srv
/var/www
/opt
/usr/local
/var/spool/cron
/var/backups/db
# /etc/restic/excludes
/root/.cache
/home/*/.cache
**/node_modules
**/*.tmp
/var/www/*/cache
- Incluye:
/etc, los directorios personales, las raíces web, los datos de las aplicaciones, los crontabs y la carpeta de volcados. - Omite:
/proc,/sys,/dev,/run,/tmp, los archivos de swap y las cachés. No están en la lista, así que nunca se leen. - Los archivos de la base de datos (
/var/lib/mysql,/var/lib/postgresql) copiados con la base en marcha pueden quedar inconsistentes. Copia volcados (paso 5). - Docker: no copies
/var/lib/dockerentero; copia tus archivos compose y los volúmenes o carpetas que guardan datos.
Paso 5: Vuelca las bases de datos antes de cada copia
Este script guarda volcados comprimidos en /var/backups/db, que ya está en la lista. Ajusta MYSQL y POSTGRES a tu servidor:
#!/bin/bash
# /usr/local/sbin/db-dump.sh - consistent database dumps before each backup
set -euo pipefail
umask 077
MYSQL=yes # yes / no
POSTGRES=no # yes / no
OUT=/var/backups/db
mkdir -p "$OUT"
if [ "$MYSQL" = yes ]; then
DUMP=$(command -v mariadb-dump || command -v mysqldump)
"$DUMP" --all-databases --single-transaction --quick \
--routines --events --triggers | gzip > "$OUT/mysql-all.sql.gz.tmp"
mv "$OUT/mysql-all.sql.gz.tmp" "$OUT/mysql-all.sql.gz"
fi
if [ "$POSTGRES" = yes ]; then
cd /tmp
runuser -u postgres -- pg_dumpall | gzip > "$OUT/postgres-all.sql.gz.tmp"
mv "$OUT/postgres-all.sql.gz.tmp" "$OUT/postgres-all.sql.gz"
fi
--single-transaction genera un volcado coherente de las tablas InnoDB sin bloquear tu web. En Debian y Ubuntu, root entra en MariaDB/MySQL por el socket local sin contraseña; si tu servidor la necesita, ponla en /root/.my.cnf (permisos 600), no en la línea de comandos.
Paso 6: Crea el script de copia
Un solo script hace los volcados, la copia, la retención y una comprobación semanal, y avisa a una URL de monitorización opcional (paso 10). Cualquier fallo lo detiene con un error, así que nada falla en silencio:
#!/bin/bash
# /usr/local/sbin/restic-backup.sh - dump databases, back up, apply retention
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
set -a; . /etc/restic/env; set +a
fail() {
[ -n "${HC_PING_URL:-}" ] && curl -fsS -m 10 --retry 3 "$HC_PING_URL/fail" >/dev/null || true
}
trap fail ERR
/usr/local/sbin/db-dump.sh
restic backup \
--files-from /etc/restic/include \
--exclude-file /etc/restic/excludes \
--exclude-caches \
--tag daily
restic forget --prune \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6
# Sundays: verify the repository and read back 5% of the data
if [ "$(date +%u)" = 7 ]; then
restic check --read-data-subset=5%
fi
if [ -n "${HC_PING_URL:-}" ]; then
curl -fsS -m 10 --retry 3 "$HC_PING_URL" >/dev/null
fi
Da permisos de ejecución a los scripts y lanza la primera copia a mano. La primera vez sube todo; las siguientes, solo los cambios.
chmod 700 /usr/local/sbin/db-dump.sh /usr/local/sbin/restic-backup.sh
/usr/local/sbin/restic-backup.sh # first run by hand, watch for errors
restic snapshots
Paso 7: Prográmalo con un timer de systemd (o con cron)
El timer de systemd es la opción recomendada: registra todo en el journal, recupera una ejecución perdida (Persistent=true) y baja la prioridad de CPU y disco de la tarea para que tu web siga rápida.
# /etc/systemd/system/restic-backup.service
[Unit]
Description=restic backup to off-site repository
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Daily restic backup
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=30min
Persistent=true
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
# Test the service once and read its log
systemctl start restic-backup.service
journalctl -u restic-backup.service -n 50 --no-pager
Si prefieres cron, usa esto en lugar del timer, no los dos a la vez:
# /etc/cron.d/restic-backup (only if you do not use the systemd timer)
30 3 * * * root /usr/local/sbin/restic-backup.sh >> /var/log/restic-backup.log 2>&1
Paso 8: Retención con restic forget --prune
restic forget decide qué snapshots se conservan y --prune elimina los datos que ya no necesita ningún snapshot. La política del script conserva:
| Opción | Qué conserva |
|---|---|
--keep-daily 7 | el último snapshot de cada uno de los últimos 7 días |
--keep-weekly 4 | un snapshot por semana durante 4 semanas |
--keep-monthly 6 | un snapshot por mes durante 6 meses |
Antes de fiarte de una política nueva, pruébala con --dry-run:
set -a; . /etc/restic/env; set +a
restic snapshots
# Preview what a policy would remove, without deleting anything
restic forget --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6
restic stats --mode raw-data
El script también ejecuta restic check cada domingo. Verifica la estructura del repositorio, y --read-data-subset=5% descarga un 5 % distinto de los datos cada vez, así que detectas cualquier corrupción antes de necesitar la copia. Puedes lanzar restic check a mano siempre que quieras más seguridad.
Paso 9: Prueba una restauración
Una copia que nunca has restaurado es solo una esperanza. Una vez al mes, restaura algo real en una carpeta temporal y compáralo:
set -a; . /etc/restic/env; set +a
restic snapshots
restic ls latest /etc/nginx
# Restore one directory to a scratch location, then compare
restic restore latest --target /tmp/restore-test --include /etc/nginx
diff -r /etc/nginx /tmp/restore-test/etc/nginx && echo "restore OK"
# Check that the database dump is complete (last line: "-- Dump completed on ...")
restic dump latest /var/backups/db/mysql-all.sql.gz | zcat | tail -n 1
rm -rf /tmp/restore-test
Ensaya también el peor día una vez, por ejemplo en un VPS de prueba: un servidor nuevo y solo los dos archivos guardados.
# On the NEW server: install restic, then restore the two saved files
install -d -m 0700 /etc/restic
nano /etc/restic/password && nano /etc/restic/env
set -a; . /etc/restic/env; set +a
restic restore latest --target /srv/restore
rsync -a /srv/restore/var/www/ /var/www/ # copy back what you need
zcat /srv/restore/var/backups/db/mysql-all.sql.gz | mariadb # or: mysql
restic restore latest --target / en un sistema en marcha: sobrescribe archivos en uso, incluida configuración del sistema que puede no encajar en el servidor nuevo. Restaura en una carpeta aparte y copia solo lo que necesites.Paso 10: Recibe un aviso cuando falle una copia
Las copias suelen fallar en silencio: una clave caducada, un bucket lleno, una contraseña cambiada. Un servicio tipo "dead man's switch" lo resuelve: el script hace ping a una URL tras cada ejecución correcta y recibes una alerta si falta un ping. Alójalo tú mismo (por ejemplo, el proyecto de código abierto Healthchecks) o usa cualquier servicio parecido, y añade la URL de tu comprobación a /etc/restic/env:
HC_PING_URL=https://hc.example.com/ping/REPLACE-WITH-YOUR-CHECK-ID
El script llama a la URL si todo va bien y a $HC_PING_URL/fail si hay un error. Configura un periodo de un día con unas horas de margen. Si no usas un servicio así, revisa al menos cada semana restic snapshots y journalctl -u restic-backup.service.
FAQ
¿Cuánto almacenamiento necesito?
Para el primer snapshot, más o menos el tamaño de tus datos (a menudo menos gracias a la compresión), y luego un poco cada día por los cambios. Tras unas semanas, ejecuta restic stats --mode raw-data para ver la cifra real.
restic dice que el repositorio está bloqueado. ¿Qué hago?
Una ejecución anterior se interrumpió y dejó un bloqueo obsoleto. Comprueba que no hay ningún proceso de restic en marcha (pgrep -a restic), ejecuta restic unlock y vuelve a lanzar la copia.
restic usa demasiada memoria en un plan de 512 MB. ¿Puedo reducirla?
Añade GOGC=20 a /etc/restic/env para que restic libere memoria más a menudo, mantén la lista de rutas centrada en lo importante y asegúrate de que el VPS tiene swap. Si aun así se queda sin memoria, mejora el plan desde la página del servicio. Si necesitas ayuda, abre un ticket: JUSTG ofrece soporte con IA 24/7 por ticket, y los ingenieros intervienen cuando hace falta una persona.