Ir al contenido

Automatizar backups incrementales de volúmenes Docker a almacenamiento remoto con restic

Rogelio Guerra Riverón
Autor
Rogelio Guerra Riverón
Construyendo mi propia infraestructura web desde cero. Aquí documento cada paso: servidores, redes, contenedores y lo que vaya surgiendo.

El problema
#

Tengo varios contenedores corriendo en mi servidor doméstico con datos importantes. No puedo depender de backups manuales. Necesitaba algo que:

  • Hiciera backups incrementales (no ocupar todo el almacenamiento remoto)
  • Se ejecutara automáticamente sin intervención
  • Permitiera recuperar datos puntuales
  • Rotara snapshots antiguos automáticamente

Después de probar varias opciones, restic fue lo que mejor se adaptó.

¿Por qué restic?
#

Es simple, rápido y soporta múltiples backends (S3, B2, SFTP, local). Los backups son incrementales por defecto, encriptados y deduplicados. Perfecto para mi caso de uso.

Instalación
#

En mi servidor con Ubuntu/Debian:

curl -b /tmp/restic.bz2 https://github.com/restic/restic/releases/download/v0.16.0/restic_0.16.0_linux_amd64.bz2
bunzip2 /tmp/restic.bz2
sudo mv /tmp/restic /usr/local/bin/
sudo chmod +x /usr/local/bin/restic
restic version

Configuración del repositorio remoto
#

Uso Backblaze B2 por ser económico. Creas una clave de aplicación en el panel web y configurar las credenciales:

export B2_ACCOUNT_ID="tu_account_id"
export B2_ACCOUNT_KEY="tu_app_key"

restic init -r b2:nombre-bucket:/backups/docker

Esto crea el repositorio remoto. Guarda la contraseña en un lugar seguro (no en variables de entorno públicas).

Script de backup automatizado
#

Creo /usr/local/bin/backup-docker-volumes.sh:

#!/bin/bash

set -e

# Credenciales y configuración
export B2_ACCOUNT_ID="${B2_ACCOUNT_ID}"
export B2_ACCOUNT_KEY="${B2_ACCOUNT_KEY}"
export RESTIC_REPOSITORY="b2:nombre-bucket:/backups/docker"
export RESTIC_PASSWORD="${RESTIC_PASSWORD}"

# Log
LOGFILE="/var/log/docker-backup.log"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Iniciando backup de volúmenes Docker" >> $LOGFILE

# Volúmenes a respaldar
VOLUMES=("database_data" "app_config" "media_files")

for volume in "${VOLUMES[@]}"; do
    MOUNT_PATH="/mnt/backup-temp/$volume"
    
    # Montar volumen temporalmente
    mkdir -p $MOUNT_PATH
    docker run --rm -v $volume:$MOUNT_PATH -v backup-data:/backup \
        alpine tar czf /backup/${volume}.tar.gz -C $MOUNT_PATH .
    
    # Respaldar con restic
    if restic backup /backup/${volume}.tar.gz \
        --tag "docker-$volume" \
        --hostname "servidor-docker" >> $LOGFILE 2>&1; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✓ $volume respaldado correctamente" >> $LOGFILE
    else
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] ✗ Error en backup de $volume" >> $LOGFILE
    fi
    
    rm -f /backup/${volume}.tar.gz
done

# Limpiar snapshots antiguos
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Ejecutando rotación de snapshots" >> $LOGFILE
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune >> $LOGFILE 2>&1

echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup completado" >> $LOGFILE

Automatización con cron
#

Edito /etc/cron.d/docker-backup:

# Backup diario a las 2:00 AM
0 2 * * * root /usr/bin/env bash /usr/local/bin/backup-docker-volumes.sh

Y creo /etc/default/docker-backup para las variables:

export B2_ACCOUNT_ID="valor_real"
export B2_ACCOUNT_KEY="valor_real"
export RESTIC_PASSWORD="contraseña_fuerte"

Aseguré que solo root pueda leerlo: chmod 600 /etc/default/docker-backup

Monitoreo y recuperación
#

Para listar snapshots disponibles:

restic snapshots

Para recuperar un volumen específico:

restic restore latest --target /tmp/restore --select "database_data"

Resultados reales
#

Después de 3 meses:

  • Backups de ~15GB se comprimen a 3GB incremental
  • Costo aproximado: 2-3 USD/mes en B2
  • Tiempo de backup: 8 minutos diarios
  • Recuperación de un volumen completo: ~2 minutos

Notas importantes
#

  • Validé que los backups sean recuperables (fundamental)
  • Ajusté la política de retención según mis necesidades
  • Monitoreo los logs regularmente para detectar fallos
  • Las credenciales están en archivos protegidos, nunca en git

Es una solución sencilla que funciona. No es sofisticada, pero es confiable.