Cifra tus copias con BorgBackup, automatízalas con borgmatic en Docker Compose y réplica externa por SSH en modo append-only, con comprobaciones y restauración.
Laboratorio · Almacenamiento
Pulsa una pieza para ver su paso.
- Datos a copiar · paso 1 carpeta ./datos, solo lectura
- borgmatic + Borg · paso 3 contenedor con cron
- Repositorio local · paso 3 ./repositorio, cifrado
- Réplica SSH · paso 2 OpenSSH + borg serve
- Repositorio remoto · paso 2 volumen append-only
- Tú (terminal) · paso 4 docker compose exec
Qué vas a montar y por qué
Vas a montar la copia de seguridad de un servidor doméstico con BorgBackup y borgmatic dentro de Docker Compose: un repositorio local cifrado, retención automática de archivos antiguos, una réplica externa por SSH en modo append-only, comprobaciones periódicas y una prueba de restauración real.
La idea encaja con la regla 3-2-1: los datos originales, una copia local cifrada y otra copia en otra ubicación. El servidor remoto de este laboratorio es un contenedor con OpenSSH que ejecuta borg serve. En producción lo normal es que ese servidor sea otra máquina tuya, un NAS en casa de un familiar o un proveedor compatible con Borg; aquí lo levantamos en contenedor para entender el esquema completo.
El servicio principal es borgmatic (imagen de la comunidad borgmatic-collective, fijada a la 2.1.10, una versión anterior a la última publicada a propósito). El contenedor de réplica es una dependencia que construimos con un Dockerfile mínimo sobre Alpine. Sustituye todos los valores CHANGE_ME_* por los tuyos y no los subas a ningún repositorio.

Requisitos
Paso a paso
Prepara la estructura y las claves SSH
Primero creamos las carpetas y una clave SSH dedicada a las copias. Usar una clave exclusiva permite restringirla en el servidor a ejecutar únicamente
borg serve, sin shell interactiva.La clave se genera sin passphrase porque la usará un proceso automático; su protección está en que en el servidor solo puede lanzar Borg. El cifrado de los datos lo da la passphrase del repositorio, que es otra cosa distinta.
Copia ahora el contenido de
ssh-cliente/id_ed25519.pub(lo necesitarás en el paso 2) antes de cambiar los propietarios. El contenedor de borgmatic se ejecuta como root y OpenSSH exige que la clave privada pertenezca al usuario que la usa y no sea accesible a otros, así que pasamos a root solo la clave privada; la carpeta queda legible para tu usuario y la pública sigue siendo legible por todos.mkdir -p borg-lab/datos borg-lab/repositorio borg-lab/ssh-cliente borg-lab/replica borg-lab/borgmatic cd borg-lab ssh-keygen -t ed25519 -N "" -C borgmatic-lab -f ssh-cliente/id_ed25519 cat ssh-cliente/id_ed25519.pub sudo chown 0:0 ssh-cliente/id_ed25519 sudo chmod 600 ssh-cliente/id_ed25519 echo "contenido de prueba" > datos/prueba.txtConfigura la réplica externa por SSH
La réplica es un contenedor Alpine con OpenSSH y BorgBackup. La clave pública del cliente se autoriza con un comando forzado:
borg serve --restrict-to-path. Según la documentación deborg serve,--restrict-to-pathlimita el acceso a esa ruta; la opciónrestrictde OpenSSH (disponible desde la 7.2) bloquea funciones peligrosas como el reenvío de puertos.Para no depender de si un servidor en modo append-only permite inicializar un repositorio, el primer arranque usa el comando forzado sin
--append-only; el repositorio remoto se crea en el paso 3 y justo después activamos--append-onlyy recreamos la réplica.Ojo con lo que garantizará ese modo: la documentación aclara que append-only afecta a la estructura de bajo nivel del repositorio, y que
delete,pruney la lectura siguen permitidos. Es una barrera contra borrados accidentales, no una inmutabilidad total.Evitamos contraseñas: solo clave pública (
sshd_configusa el formato clave valor de OpenSSH, por eso el fichero se declara comotext). SustituyeCHANGE_ME_PUBLIC_KEYpor el contenido completo deid_ed25519.pub, la línea que empieza porssh-ed25519. El entrypoint genera las claves de host en un volumen y las deja con los permisos que sshd exige.La cuenta
borgse crea conadduser -Dy se desbloquea consedsobre/etc/shadow(sustituye el campo de contraseña bloqueada!por*), sin depender de opciones depasswd; así sshd admite el login por clave. El puerto SSH de la réplica solo se publica en 127.0.0.1:2222; borgmatic accede por la red interna de Compose.Fichero
replica/sshd_configPort 22 HostKey /etc/ssh/hostkeys/ssh_host_ed25519_key PasswordAuthentication no KbdInteractiveAuthentication no PermitRootLogin no AllowUsers borg AuthorizedKeysFile /etc/ssh/authorized_keys/%u ClientAliveInterval 10 ClientAliveCountMax 30Fichero
replica/authorized_keyscommand="borg serve --restrict-to-path /srv/borg",restrict CHANGE_ME_PUBLIC_KEYSustituye cada valor CHANGE_ME_… por uno tuyo antes de arrancar: no dejes contraseñas ni claves de ejemplo.
Fichero
replica/DockerfileFROM alpine:3.24.4 RUN apk add --no-cache borgbackup openssh-server \ && adduser -D -s /bin/sh borg \ && sed -i 's/^borg:!:/borg:*:/' /etc/shadow \ && mkdir -p /srv/borg /etc/ssh/hostkeys /etc/ssh/authorized_keys \ && chown borg:borg /srv/borg COPY sshd_config /etc/ssh/sshd_config COPY authorized_keys /etc/ssh/authorized_keys/borg RUN chmod 644 /etc/ssh/authorized_keys/borg COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh EXPOSE 22 ENTRYPOINT ["/entrypoint.sh"]Fichero
replica/entrypoint.sh#!/bin/sh set -e if [ ! -f /etc/ssh/hostkeys/ssh_host_ed25519_key ]; then ssh-keygen -t ed25519 -N "" -f /etc/ssh/hostkeys/ssh_host_ed25519_key fi chmod 600 /etc/ssh/hostkeys/ssh_host_ed25519_key chown -R borg:borg /srv/borg exec /usr/sbin/sshd -D -e -f /etc/ssh/sshd_configConfigura borgmatic, crea los repositorios y activa append-only
Ahora el fichero principal. La passphrase del repositorio entra por la variable de entorno
BORG_PASSPHRASE, que Borg lee tal y como describe su guía rápida; defínela en un fichero.envjunto al Compose y reemplazaCHANGE_ME_REPO_PASSPHRASEpor una frase larga y única. Guarda además esa passphrase en tu gestor de contraseñas: sin ella, las copias son irrecuperables.Las rutas de datos se montan en solo lectura. La carpeta de claves SSH se monta también en solo lectura, y el fichero de hosts conocidos se redirige a la caché de Borg (un volumen con escritura) con
UserKnownHostsFile. Fijarstop_grace_periodda tiempo a que una copia en curso termine limpiamente al parar el contenedor, como recomienda el README de la imagen.En la configuración usamos
archive_name_formatcon un prefijo fijo: el README de la imagen advierte de que el hostname del contenedor cambia al reconstruirlo y eso rompe la poda. Cada repositorio declara su modo de cifrado con la claveencryption; es la única fuente del modo, así querepo-createse ejecuta sin la opción--encryption. Los dos repositorios se copian en secuencia.Arrancamos con el comando forzado sin
--append-only, esperamos unos segundos a que sshd acepte conexiones y creamos ambos repositorios. Si elrepo-createfalla por conexión rechazada, espera unos segundos más y repítelo (ya existentes se omiten). Cuando los repositorios existen, cambiamosreplica/authorized_keyspara añadir--append-only, reconstruimos y recreamos solo la réplica; los volúmenes conservan el repositorio remoto. La primera conexión aceptará la huella de host conStrictHostKeyChecking=accept-new; en una réplica real puedes preferir verificarla manualmente.La retención (
keep_daily,keep_weekly,keep_monthly) y las comprobaciones (checksconfrequency) son opciones de configuración de borgmatic. Si tu versión rechaza alguna, genera el fichero de referencia conborgmatic config generatey compáralo.Fichero
.envBORG_PASSPHRASE=CHANGE_ME_REPO_PASSPHRASE TZ=Europe/MadridSustituye cada valor CHANGE_ME_… por uno tuyo antes de arrancar: no dejes contraseñas ni claves de ejemplo.
Fichero
borgmatic/config.yamlsource_directories: - /mnt/source repositories: - path: /mnt/borg-repository label: local encryption: repokey-blake2 - path: ssh://borg@replica:22/srv/borg/lab.borg label: replica encryption: repokey-blake2 ssh_command: ssh -i /root/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.cache/borg/known_hosts -o ServerAliveInterval=10 -o ServerAliveCountMax=30 archive_name_format: 'servidor-casa-{now:%Y-%m-%d-%H%M%S}' keep_daily: 7 keep_weekly: 4 keep_monthly: 6 checks: - name: repository frequency: 1 week - name: archives frequency: 2 weeksFichero
compose.yamlservices: borgmatic: image: ghcr.io/borgmatic-collective/borgmatic:2.1.10 container_name: borgmatic hostname: borgmatic restart: unless-stopped stop_grace_period: 10m depends_on: - replica environment: - TZ=${TZ} - BORG_PASSPHRASE=${BORG_PASSPHRASE} - CRON=0 2 * * * volumes: - ./datos:/mnt/source:ro - ./repositorio:/mnt/borg-repository - ./borgmatic:/etc/borgmatic.d - ./ssh-cliente:/root/.ssh:ro - borg-config:/root/.config/borg - borg-cache:/root/.cache/borg - borgmatic-state:/root/.local/state/borgmatic replica: build: ./replica container_name: borg-replica restart: unless-stopped ports: - "127.0.0.1:2222:22" volumes: - replica-repo:/srv/borg - replica-hostkeys:/etc/ssh/hostkeys volumes: borg-config: borg-cache: borgmatic-state: replica-repo: replica-hostkeys:docker compose build replica docker compose up -d sleep 10 docker compose exec borgmatic borgmatic repo-create docker compose logs --tail 20 replicaFichero
replica/authorized_keyscommand="borg serve --append-only --restrict-to-path /srv/borg",restrict CHANGE_ME_PUBLIC_KEYSustituye cada valor CHANGE_ME_… por uno tuyo antes de arrancar: no dejes contraseñas ni claves de ejemplo.
docker compose build replica docker compose up -d --force-recreate replicaHaz la primera copia, comprueba y restaura
Lanzamos una copia manual con
--verbosity 1 --list --statspara ver qué archivos entran y cuánto ocupa lo nuevo. Luego listamos los archivos conrepo-list, ejecutamos las comprobaciones conchecky, lo más importante, restauramos conextracta una carpeta temporal. Una copia que nunca se ha restaurado es solo una esperanza.Para restaurar usamos
--destinationy--archive latest, de modo que no tocamos tus datos reales. Borg guarda las rutas sin la barra inicial, así que el archivo/mnt/source/prueba.txtqueda enmnt/source/prueba.txtdentro del destino; elfindlo muestra antes de leerlo.docker compose exec borgmatic borgmatic create --verbosity 1 --list --stats docker compose exec borgmatic borgmatic repo-listSalida esperada (ilustrativa: el formato exacto puede variar)
servidor-casa-2025-01-01-020000 Wed, 2025-01-01 02:00:00 [...]docker compose exec borgmatic borgmatic check --verbosity 1 docker compose exec borgmatic mkdir -p /tmp/restauracion docker compose exec borgmatic borgmatic extract --archive latest --repository local --destination /tmp/restauracion docker compose exec borgmatic find /tmp/restauracion -name prueba.txt docker compose exec borgmatic cat /tmp/restauracion/mnt/source/prueba.txt
Comprueba que funciona
Ambos repositorios contienen la copia
repo-listmuestra los archivos de los repositorios declarados; con dos repositorios, la salida los separa por etiqueta. Los nombres y fechas serán distintos a los de la salida de ejemplo. Si falta el repositorioreplica, la copia a la réplica ha fallado: revisa el apartado de problemas frecuentes.docker compose exec borgmatic borgmatic repo-listLa restauración devuelve el contenido original
El
catdel fichero restaurado en/tmp/restauraciondebe mostrar el mismo texto que escribiste endatos/prueba.txt. Esa es la prueba real de que la copia sirve, no solo de que existe.cat datos/prueba.txt docker compose exec borgmatic cat /tmp/restauracion/mnt/source/prueba.txtEl cron está programado
La variable
CRONprograma la ejecución diaria a las 02:00. Según el README de la imagen, el comando por defecto esborgmatic-start --stats -v 0, que imprime poco. Para confirmar que la tarea está instalada, lista la crontab dentro del contenedor: debe aparecer la línea con0 2 * * *.docker compose exec borgmatic crontab -l
Si algo falla
Permission denied (publickey) al copiar a la réplica
Comprueba que pegaste la línea completa de id_ed25519.pub en replica/authorized_keys (sustituyendo CHANGE_ME_PUBLIC_KEY) y que reconstruiste la imagen con docker compose build replica. Verifica también que ssh-cliente/id_ed25519 pertenece a root y tiene permisos 600.
docker compose build replica
docker compose up -d --force-recreate replicaFailed to create/acquire the lock en el repositorio remoto
Suele ocurrir tras una conexión cortada: el proceso borg serve del servidor sigue vivo sosteniendo el bloqueo. La documentación de borg serve recomienda los keepalives de SSH (ya incluidos en este laboratorio, tanto en ssh_command como en sshd_config) y esperar a que expiren; reiniciar la réplica también libera el proceso.
docker compose restart replicaLa poda no encuentra archivos o los borra todos tras reconstruir el contenedor
El nombre de host del contenedor cambia al reconstruirlo y, con el formato de nombre por defecto, borgmatic ya no reconoce los archivos anteriores. Por eso la configuración fija archive_name_format con un prefijo constante; no lo quites.
Passphrase incorrecta o el repositorio no se desbloquea
Verifica que el .env está junto al compose.yaml y que BORG_PASSPHRASE coincide con la usada al crear el repositorio. Tras cambiarla, recrea el contenedor para que la tome.
docker compose up -d --force-recreate borgmaticEndurece la instalación
Guarda la passphrase y la clave fuera del servidor
Anota la passphrase del repositorio en un gestor de contraseñas y exporta la clave del repositorio a un lugar separado. Si pierdes ambas, los datos cifrados no se pueden recuperar.
Pon una cuota al repositorio remoto
borg serve admite --storage-quota, que fija una cuota de almacenamiento para el repositorio y tiene prioridad sobre la cuota propia del repositorio. Añádela al comando forzado de authorized_keys con un valor por debajo del espacio libre real de tu disco (aquí 10G, dentro de los 20 GB del laboratorio). Después reconstruye y recrea la réplica con los mismos comandos del paso 3 (docker compose build replica y docker compose up -d --force-recreate replica).
Fichero replica/authorized_keys
command="borg serve --append-only --restrict-to-path /srv/borg --storage-quota 10G",restrict CHANGE_ME_PUBLIC_KEYSustituye cada valor CHANGE_ME_… por uno tuyo antes de arrancar: no dejes contraseñas ni claves de ejemplo.
Mantén la réplica fuera del alcance del cliente
Append-only no impide prune ni delete según la documentación de Borg, así que lo ideal es que la réplica viva en otra máquina bajo otra cuenta y que la poda del remoto la ejecutes tú desde allí, no desde el servidor que haces copiar.
Mantén el SSH de la réplica en local
El Compose publica el puerto solo en 127.0.0.1. Si trasladas la réplica a otro equipo, expón SSH únicamente a las direcciones que lo necesiten y conserva la autenticación solo por clave.
Limpieza: deshacer el laboratorio
Elimina el laboratorio por completo
down -v borra los contenedores y los volúmenes nombrados, incluida la réplica remota y sus claves de host. --rmi local elimina la imagen construida. Después borra la carpeta del laboratorio, que contiene la copia local cifrada y la clave SSH de prueba (la clave privada pertenece a root, por eso usamos sudo).
docker compose down -v --rmi local
cd ..
sudo rm -rf borg-labQué está verificado
- replica/sshd_config: sin validador automático, revísalo a mano
- replica/authorized_keys: sin validador automático, revísalo a mano
- replica/Dockerfile: sin validador automático, revísalo a mano
- .env: sin validador automático, revísalo a mano
- Sintaxis de borgmatic/config.yaml (parser YAML)
- Sintaxis de compose.yaml (parser YAML y estructura de Compose)
- replica/authorized_keys: sin validador automático, revísalo a mano
- replica/authorized_keys: sin validador automático, revísalo a mano
- Sintaxis de 13 bloque(s) de comandos (bash -n en un contenedor aislado, sin ejecutarlos)
- Versiones (4), opciones (9) y claves de configuración (27) contrastadas con 18 páginas de documentación oficial
- Arranque real del laboratorio: no ejecutado
Comprobaciones automáticas del 9 de noviembre de 2026. Lo marcado con «·» no se ha ejecutado: revísalo en tu entorno.
Fuentes
- Borg Quick Start borgbackup.readthedocs.io
- borg serve borgbackup.readthedocs.io
- borgmatic: How to set up backups torsion.org
- borgmatic: Configuration torsion.org
- borgmatic: How to extract a backup torsion.org
- docker-borgmatic github.com
¿Te ha funcionado este laboratorio?
Puntúalo de 1 a 5 estrellas. Un voto por persona; puedes cambiarlo cuando quieras.
Artículo generado mediante AI.larebelion


