BorgBackup (Borg) es una herramienta de copias de seguridad que divide los archivos en fragmentos, guarda cada fragmento una sola vez (deduplicación), los comprime y los cifra antes de enviarlos al repositorio. Así, copiar un servidor cada día apenas ocupa algo más que los cambios de ese día, y el servidor de copias no puede leer los datos. En este tutorial configurarás Borg en Ubuntu 24.04 para enviar copias cifradas a un servidor remoto por SSH, las automatizarás con un timer de systemd, aplicarás una política de retención y restaurarás archivos.

Requisitos previos

Para seguir esta guía necesitas:

  • Dos servidores con Ubuntu 24.04 LTS, por ejemplo dos VPS de CubePath:
    • El servidor a respaldar (en esta guía, web01).
    • El servidor de copias, con espacio suficiente para el repositorio. Idealmente en otra ubicación o centro de datos.
  • Un usuario no root con privilegios sudo en ambos.
  • Conectividad SSH desde el servidor a respaldar hacia el servidor de copias.

En los ejemplos, backup_server_ip es la IP del servidor de copias.

Cómo organiza Borg las copias

Conviene tener claros tres conceptos antes de empezar:

  • Repositorio: el directorio que contiene todos los datos deduplicados y cifrados. Normalmente se usa un repositorio por servidor respaldado.
  • Archivo (archive): cada copia que haces con borg create. Tiene un nombre, por ejemplo web01-2026-09-25T02:00, y contiene la vista completa de los directorios copiados en ese momento.
  • Clave y frase de paso: con el modo repokey, la clave de cifrado se guarda dentro del repositorio, protegida por una frase de paso. Necesitas ambas para restaurar. Si pierdes la frase de paso, los datos son irrecuperables.

Paso 1: Instalar BorgBackup en ambos servidores

Borg debe estar instalado en los dos servidores, porque en el servidor de copias se ejecuta borg serve para recibir los datos. Ejecuta en ambos:

sudo apt update
sudo apt install borgbackup

Comprueba la versión:

borg --version
borg 1.2.8

Usa la misma versión principal (1.2.x) en ambos servidores. Los comandos de esta guía son para Borg 1.2.

Paso 2: Preparar el servidor de copias

En el servidor de copias, crea un usuario de sistema borg sin contraseña, que solo servirá para recibir copias, y el directorio de los repositorios:

sudo adduser --system --group --shell /bin/bash --home /srv/borg borg
sudo install -d -m 700 -o borg -g borg /srv/borg/.ssh

Comprueba que el directorio pertenece al nuevo usuario:

ls -ld /srv/borg /srv/borg/.ssh

En ambas líneas deben aparecer borg borg como propietario y grupo, y drwx------ en .ssh.

Paso 3: Crear la clave SSH del servidor a respaldar

Las copias se ejecutan como root en web01, porque hay que leer archivos como /etc o los directorios de todos los usuarios. En web01, crea una clave SSH para root sin frase de paso, para que el timer pueda usarla sin intervención:

sudo ssh-keygen -t ed25519 -f /root/.ssh/borg_ed25519 -N '' -C 'borg-web01'
sudo cat /root/.ssh/borg_ed25519.pub

Copia la línea que empieza por ssh-ed25519.

En el servidor de copias, añade esa clave a authorized_keys del usuario borg, pero restringida: con la opción command, esa clave solo puede ejecutar borg serve y solo sobre su propio repositorio. Aunque alguien robe la clave de web01, no obtendrá una shell ni podrá tocar los repositorios de otros servidores:

sudo nano /srv/borg/.ssh/authorized_keys
command="borg serve --restrict-to-path /srv/borg/web01",restrict ssh-ed25519 AAAAC3Nza... borg-web01

Ajusta los permisos del archivo:

sudo chown borg:borg /srv/borg/.ssh/authorized_keys
sudo chmod 600 /srv/borg/.ssh/authorized_keys

Si el servidor de copias tiene UFW activo, asegúrate de que el puerto SSH está permitido (sudo ufw allow OpenSSH).

Paso 4: Inicializar el repositorio cifrado

El resto de pasos se ejecutan en web01 como root. Abre una shell de root:

sudo -i

Genera una frase de paso aleatoria y guárdala en un archivo que solo root pueda leer:

install -m 600 /dev/null /root/.borg-passphrase
openssl rand -base64 32 > /root/.borg-passphrase

Define las variables que Borg lee del entorno: la URL del repositorio, cómo obtener la frase de paso y qué clave SSH usar:

export BORG_REPO='ssh://borg@backup_server_ip/srv/borg/web01'
export BORG_PASSCOMMAND='cat /root/.borg-passphrase'
export BORG_RSH='ssh -i /root/.ssh/borg_ed25519'

Inicializa el repositorio con cifrado repokey-blake2 (autenticado con BLAKE2b, más rápido que SHA-256 en la mayoría de CPU sin aceleración específica):

borg init --encryption=repokey-blake2

La primera vez SSH te pedirá aceptar la huella del servidor de copias. Borg muestra después un aviso como este:

IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!
...

Comprueba que el repositorio responde:

borg info
Repository ID: 5c1e0f...
Location: ssh://borg@backup_server_ip/srv/borg/web01
Encrypted: Yes (repokey BLAKE2b)
...

Paso 5: Exportar la clave y guardar la frase de paso

Aunque la clave está dentro del repositorio, si este se corrompe perderías el acceso a todos los datos. Exporta una copia de la clave:

borg key export "$BORG_REPO" /root/borg-web01.key

Guarda fuera de ambos servidores el archivo /root/borg-web01.key y el contenido de /root/.borg-passphrase, por ejemplo en tu gestor de contraseñas. Si web01 se pierde por completo, son lo único que necesitarás, junto con el repositorio, para restaurar.

Paso 6: Hacer la primera copia

Crea un archivo con los directorios importantes. {hostname} y {now} son marcadores que Borg sustituye por el nombre del host y la fecha; el :: indica que el repositorio se toma de BORG_REPO:

borg create --stats --compression zstd,6 --exclude-caches \
    --exclude '/home/*/.cache' \
    --exclude '/var/tmp/*' \
    ::'{hostname}-{now:%Y-%m-%dT%H:%M}' \
    /etc /home /root /var/www

Qué hacen las opciones:

  • --compression zstd,6 comprime con zstd, un buen equilibrio entre velocidad y tamaño.
  • --exclude-caches omite los directorios marcados con un archivo CACHEDIR.TAG.
  • --exclude omite rutas con patrones de tipo shell.

Al terminar, --stats muestra un resumen:

Archive name: web01-2026-09-25T10:31
Number of files: 18342
                       Original size      Compressed size    Deduplicated size
This archive:                1.92 GB            842.13 MB            801.77 MB
All archives:                1.92 GB            842.13 MB            801.77 MB

Ejecuta el mismo comando otra vez y observa la columna Deduplicated size de This archive: al no haber cambios, será de unos pocos kilobytes. Esa es la cantidad de espacio nuevo que ocupa cada copia.

Lista los archivos del repositorio:

borg list
web01-2026-09-25T10:31               Thu, 2026-09-25 10:31:02 [3b0c...]
web01-2026-09-25T10:36               Thu, 2026-09-25 10:36:44 [9e41...]

Paso 7: Automatizar las copias con un script y systemd

Crea un script que haga la copia, aplique la retención y libere espacio. Sal de la shell de root con exit y crea el archivo:

sudo nano /usr/local/sbin/borg-backup.sh
#!/usr/bin/env bash
set -uo pipefail

export BORG_REPO='ssh://borg@backup_server_ip/srv/borg/web01'
export BORG_PASSCOMMAND='cat /root/.borg-passphrase'
export BORG_RSH='ssh -i /root/.ssh/borg_ed25519'

borg create --stats --compression zstd,6 --exclude-caches \
    --exclude '/home/*/.cache' \
    --exclude '/var/tmp/*' \
    ::'{hostname}-{now:%Y-%m-%dT%H:%M}' \
    /etc /home /root /var/www
create_exit=$?

borg prune --list --glob-archives '{hostname}-*' \
    --keep-daily 7 --keep-weekly 4 --keep-monthly 6
prune_exit=$?

borg compact
compact_exit=$?

# Borg devuelve 0 (correcto), 1 (aviso) o 2 (error). Se devuelve el peor.
exit_code=$(( create_exit > prune_exit ? create_exit : prune_exit ))
exit_code=$(( compact_exit > exit_code ? compact_exit : exit_code ))
exit "$exit_code"

El script no usa set -e a propósito: si borg create termina con un aviso (código 1, por ejemplo porque un archivo cambió durante la lectura), la retención y la compactación deben ejecutarse igualmente.

  • borg prune conserva las últimas 7 copias diarias, 4 semanales y 6 mensuales, y marca el resto para borrar. --glob-archives limita la retención a los archivos de este host.
  • borg compact libera en disco el espacio de los archivos borrados. En Borg 1.2 es un paso obligatorio tras prune.

Hazlo ejecutable y solo accesible por root:

sudo chmod 700 /usr/local/sbin/borg-backup.sh

Crea la unidad de servicio:

sudo nano /etc/systemd/system/borg-backup.service
[Unit]
Description=Copia de seguridad con BorgBackup
Wants=network-online.target
After=network-online.target

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

Y el timer, que la ejecuta cada noche a las 02:00 con un retraso aleatorio de hasta 15 minutos:

sudo nano /etc/systemd/system/borg-backup.timer
[Unit]
Description=Copia diaria con BorgBackup

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

[Install]
WantedBy=timers.target

Persistent=true hace que, si el servidor estaba apagado a esa hora, la copia se ejecute al arrancar. Activa el timer y lanza una ejecución manual para probar el servicio:

sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer
sudo systemctl start borg-backup.service

Revisa el resultado en el registro:

sudo journalctl -u borg-backup.service -n 20 --no-pager

La salida debe incluir las estadísticas de borg create y la lista de borg prune sin errores. Comprueba la próxima ejecución programada:

systemctl list-timers borg-backup.timer
NEXT                         LEFT     LAST PASSED UNIT              ACTIVATES
Fri 2026-09-26 02:07:13 CEST 15h left -    -      borg-backup.timer borg-backup.service

Paso 8: Restaurar archivos

Para trabajar con el repositorio de forma interactiva, abre una shell de root y exporta de nuevo las tres variables del paso 4.

Lista el contenido de un archivo concreto, filtrando por ruta:

borg list ::web01-2026-09-25T10:36 etc/nginx

Borg guarda las rutas sin la / inicial y borg extract restaura en el directorio actual. Por eso lo más seguro es extraer en un directorio temporal y copiar después lo que necesites:

mkdir -p /tmp/restore && cd /tmp/restore
borg extract --list ::web01-2026-09-25T10:36 etc/nginx
etc/nginx
etc/nginx/nginx.conf
etc/nginx/sites-available/default
...

Compara con la versión actual y, si es la correcta, cópiala a su sitio:

diff -r /tmp/restore/etc/nginx /etc/nginx
cp -a /tmp/restore/etc/nginx/. /etc/nginx/

Para restaurar un servidor completo, instala Borg en el servidor nuevo, importa la clave si el repositorio estuviera dañado (borg key import), configura el acceso SSH y extrae los directorios necesarios de la misma forma.

Paso 9: Verificar el repositorio

borg check comprueba la consistencia del repositorio y de los archivos. Ejecútalo periódicamente, por ejemplo una vez al mes:

borg check

Si no muestra errores y termina con código 0, el repositorio está bien. La opción --verify-data además descifra y verifica todos los datos, lo que es más fiable pero mucho más lento en repositorios grandes.

Una comprobación no sustituye a una restauración de prueba: extrae de vez en cuando un directorio completo y verifica que la aplicación funciona con él.

Solución de problemas

Remote: borg: command not found. Borg no está instalado en el servidor de copias. Instala el paquete borgbackup allí.

Failed to create/acquire the lock. Otra ejecución sigue en curso o una anterior se interrumpió. Si estás seguro de que no hay ningún borg en marcha en ningún servidor, libera el bloqueo con borg break-lock.

passphrase supplied in BORG_PASSCOMMAND is incorrect. El archivo /root/.borg-passphrase no contiene la frase con la que se creó el repositorio o tiene un salto de línea de más. Recupérala desde tu gestor de contraseñas.

Repository path not allowed. La ruta de BORG_REPO no está dentro de la indicada en --restrict-to-path en authorized_keys.

Conclusión

Tienes copias diarias cifradas y deduplicadas de web01 en un servidor remoto, con una clave SSH que solo puede ejecutar borg serve, retención automática y un procedimiento de restauración probado. Como siguientes pasos, añade borg check mensual a otro timer, envía una alerta cuando borg-backup.service falle (por ejemplo con OnFailure= en la unidad) y respalda más servidores creando un repositorio y una clave restringida para cada uno.