💾

Backup de Volúmenes Docker

👨‍🍳 Chef⏱️ 25 minutos

📋 Prerequisitos sugeridos

  • Docker con volúmenes

Lo que vas a construir

Scripts para hacer backup y restore de volúmenes Docker. Protegerás tus datos de PostgreSQL, Redis, y cualquier otro servicio.

Al terminar tendrás:

  • Script de backup que crea archivos .tar.gz
  • Script de restore para recuperar datos
  • Backup automático con cron

Paso 1: Crea un volumen de prueba

# Crear volumen
docker volume create test_data

# Crear contenedor con datos
docker run --rm -v test_data:/data alpine sh -c "
  echo 'Datos importantes' > /data/archivo.txt
  echo 'Más datos' > /data/config.json
  mkdir /data/subdir
  echo 'Anidado' > /data/subdir/nested.txt
"

# Verificar contenido
docker run --rm -v test_data:/data alpine ls -la /data

Paso 2: Backup manual

# Backup a archivo .tar.gz
docker run --rm \
  -v test_data:/source:ro \
  -v $(pwd):/backup \
  alpine tar czf /backup/test_data_backup.tar.gz -C /source .

# Verificar
ls -lh test_data_backup.tar.gz
tar tzf test_data_backup.tar.gz

Paso 3: Script de backup

Crea backup-volume.sh:

#!/bin/bash
# backup-volume.sh - Backup de volumen Docker

VOLUME_NAME=${1:?Uso: ./backup-volume.sh NOMBRE_VOLUMEN}
BACKUP_DIR=${2:-./backups}
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${VOLUME_NAME}_${TIMESTAMP}.tar.gz"

mkdir -p "$BACKUP_DIR"

echo "Haciendo backup de '$VOLUME_NAME' a '$BACKUP_FILE'..."

docker run --rm \
  -v "$VOLUME_NAME":/source:ro \
  -v "$BACKUP_DIR":/backup \
  alpine tar czf "/backup/${VOLUME_NAME}_${TIMESTAMP}.tar.gz" -C /source .

if [ $? -eq 0 ]; then
  echo "✅ Backup completado: $BACKUP_FILE"
  echo "   Tamaño: $(ls -lh "$BACKUP_FILE" | awk '{print $5}')"
else
  echo "❌ Error en backup"
  exit 1
fi
chmod +x backup-volume.sh
./backup-volume.sh test_data ./backups

Paso 4: Script de restore

Crea restore-volume.sh:

#!/bin/bash
# restore-volume.sh - Restaurar volumen Docker

BACKUP_FILE=${1:?Uso: ./restore-volume.sh ARCHIVO.tar.gz NOMBRE_VOLUMEN}
VOLUME_NAME=${2:?Uso: ./restore-volume.sh ARCHIVO.tar.gz NOMBRE_VOLUMEN}

if [ ! -f "$BACKUP_FILE" ]; then
  echo "❌ Archivo no encontrado: $BACKUP_FILE"
  exit 1
fi

echo "⚠️  Esto sobrescribirá el volumen '$VOLUME_NAME'"
read -p "¿Continuar? (y/N) " confirm
[ "$confirm" != "y" ] && exit 0

# Crear volumen si no existe
docker volume create "$VOLUME_NAME" 2>/dev/null

echo "Restaurando '$BACKUP_FILE' a '$VOLUME_NAME'..."

docker run --rm \
  -v "$VOLUME_NAME":/target \
  -v "$(pwd)":/backup \
  alpine sh -c "rm -rf /target/* && tar xzf /backup/$(basename $BACKUP_FILE) -C /target"

if [ $? -eq 0 ]; then
  echo "✅ Restore completado"
else
  echo "❌ Error en restore"
  exit 1
fi

Paso 5: Probar restore

# Eliminar volumen original
docker volume rm test_data

# Restaurar desde backup
./restore-volume.sh backups/test_data_*.tar.gz test_data_restored

# Verificar
docker run --rm -v test_data_restored:/data alpine cat /data/archivo.txt

Paso 6: Backup de PostgreSQL (mejor práctica)

Para bases de datos, usa pg_dump antes de comprimir:

#!/bin/bash
# backup-postgres.sh

CONTAINER=${1:-postgres}
BACKUP_DIR=./backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DIR

docker exec $CONTAINER pg_dumpall -U postgres | gzip > "$BACKUP_DIR/postgres_$TIMESTAMP.sql.gz"

echo "✅ Backup: $BACKUP_DIR/postgres_$TIMESTAMP.sql.gz"

Restore:

gunzip -c backups/postgres_*.sql.gz | docker exec -i postgres psql -U postgres

Paso 7: Backup automático (cron)

# Editar crontab
crontab -e

# Backup diario a las 3am
0 3 * * * /opt/scripts/backup-volume.sh postgres_data /opt/backups >> /var/log/backup.log 2>&1

# Backup cada 6 horas
0 */6 * * * /opt/scripts/backup-postgres.sh postgres >> /var/log/backup.log 2>&1

Paso 8: Rotación de backups

Mantén solo los últimos N backups:

# Eliminar backups de más de 7 días
find /opt/backups -name "*.tar.gz" -mtime +7 -delete

# Mantener solo los últimos 5
ls -t /opt/backups/*.tar.gz | tail -n +6 | xargs rm -f

Script completo con rotación

#!/bin/bash
# backup-with-rotation.sh

VOLUME=$1
BACKUP_DIR=/opt/backups
KEEP=7

./backup-volume.sh "$VOLUME" "$BACKUP_DIR"

# Eliminar viejos
ls -t "$BACKUP_DIR/${VOLUME}_"*.tar.gz 2>/dev/null | tail -n +$((KEEP+1)) | xargs rm -f

echo "Backups actuales:"
ls -lh "$BACKUP_DIR/${VOLUME}_"*.tar.gz

Verificar integridad

# Verificar que el tar es válido
tar tzf backup.tar.gz > /dev/null && echo "✅ Archivo válido" || echo "❌ Corrupto"

# Ver contenido sin extraer
tar tzf backup.tar.gz | head -20

Próximo paso

CI/CD con GitHub Actions