Ir al contenido

Automatizar backups incrementales de volúmenes Docker con restic y S3

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
#

Tenía varios contenedores Docker con datos importantes: bases de datos, archivos de configuración, datos de aplicaciones. Necesitaba backups automáticos, incrementales y con retención inteligente. No quería gastar mucho en almacenamiento externo.

La solución: restic + S3. Restic es pequeño, eficiente y maneja incrementales nativamente. S3 es barato y confiable.

Setup inicial
#

Primero instalé restic en el host:

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

Creé credenciales de AWS IAM con permisos solo para S3:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketVersioning",
        "s3:ListBucketVersions"
      ],
      "Resource": "arn:aws:s3:::mi-bucket-backups"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::mi-bucket-backups/*"
    }
  ]
}

Inicializar repositorio restic
#

export AWS_ACCESS_KEY_ID="tu_access_key"
export AWS_SECRET_ACCESS_KEY="tu_secret_key"
export RESTIC_PASSWORD="contraseña_muy_segura"

restic -r s3:s3.amazonaws.com/mi-bucket-backups init

Guardé estas variables en /etc/restic/env con permisos 600.

Script de backup
#

Creé /usr/local/bin/backup-docker-volumes.sh:

#!/bin/bash

source /etc/restic/env

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/restic-backup-${TIMESTAMP}.log"

# Array de volúmenes a respaldar
VOLUMES=(
  "postgres_data"
  "nginx_config"
  "app_data"
)

# Montar volúmenes temporalmente
MOUNT_POINT="/mnt/backup_volumes"
mkdir -p ${MOUNT_POINT}

for VOLUME in "${VOLUMES[@]}"; do
  VOLUME_PATH="${MOUNT_POINT}/${VOLUME}"
  mkdir -p ${VOLUME_PATH}
  
  docker run --rm -v ${VOLUME}:/data -v ${MOUNT_POINT}:/mnt alpine \
    cp -r /data/* /mnt/${VOLUME}/ 2>/dev/null || true
done

# Ejecutar backup incremental
restic -r s3:s3.amazonaws.com/mi-bucket-backups backup ${MOUNT_POINT} \
  --tag docker-volumes \
  --tag $(date +%Y-%m-%d) \
  >> ${LOG_FILE} 2>&1

BACKUP_STATUS=$?

# Limpiar
rm -rf ${MOUNT_POINT}

# Rotación: mantener últimos 30 días diarios, 12 meses semanales
restic -r s3:s3.amazonaws.com/mi-bucket-backups forget \
  --keep-daily 30 \
  --keep-weekly 12 \
  --keep-monthly 6 \
  --prune \
  >> ${LOG_FILE} 2>&1

if [ $BACKUP_STATUS -eq 0 ]; then
  echo "Backup exitoso" >> ${LOG_FILE}
  exit 0
else
  echo "Error en backup" >> ${LOG_FILE}
  exit 1
fi

Lo hice ejecutable:

chmod +x /usr/local/bin/backup-docker-volumes.sh

Automatizar con systemd
#

Creé /etc/systemd/system/docker-backup.service:

[Unit]
Description=Docker Volumes Backup to S3
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-docker-volumes.sh
StandardOutput=journal
StandardError=journal

Y el timer en /etc/systemd/system/docker-backup.timer:

[Unit]
Description=Daily Docker Backup Timer

[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Activé el timer:

systemctl daemon-reload
systemctl enable docker-backup.timer
systemctl start docker-backup.timer

Verificación y monitoreo
#

Para ver el estado:

systemctl status docker-backup.timer
journalctl -u docker-backup.service -f

Para listar snapshots:

restic -r s3:s3.amazonaws.com/mi-bucket-backups snapshots

Para restaurar un volumen específico:

restic -r s3:s3.amazonaws.com/mi-bucket-backups restore latest \
  --target /tmp/restore \
  --path /mnt/backup_volumes/postgres_data

Resultado final
#

Los backups corren automáticamente cada madrugada. Restic comprime y deduplica, así que el almacenamiento en S3 es mínimo. La rotación automática mantiene 30 días de backups diarios sin acumular basura.

Llevó una tarde configurarlo, pero ahora duermo tranquilo.