¡Respaldo seguro y eficiente: Usando Azure Blob Storage para proteger tus datos!

  • Imagen de redactor Daniel J. Saldaña
  • 22 de diciembre de 2025
¡Respaldo seguro y eficiente: Usando Azure Blob Storage para proteger tus datos!

Hoy quiero abordar uno de los temas más críticos para la seguridad de cualquier sitio o proyecto: los respaldos. En este post, exploraremos cómo garantizar la protección y accesibilidad de tus datos, utilizando Azure Blob Storage como la solución ideal para crear respaldos seguros y automatizados.

¿Por qué es tan importante un sistema de respaldo?

Los datos son el activo más valioso en cualquier proyecto digital. Sin embargo, a menudo subestimamos la importancia de tener un respaldo confiable. Un fallo del sistema, un ataque malicioso o incluso un error humano pueden provocar la pérdida de información crítica. Es por eso que, tener una estrategia de respaldos sólida y eficiente es fundamental.

¿Por qué elegir Azure Blob Storage?

Cuando hablamos de soluciones en la nube, Azure se destaca como uno de los proveedores más robustos y confiables. Azure Blob Storage es una opción poderosa para almacenar grandes volúmenes de datos y realizar respaldos de forma segura, con múltiples capas de protección.

Con Azure, puedes aprovechar sus características como:

  • Escalabilidad: Puedes almacenar desde unos pocos gigabytes hasta petabytes de datos.
  • Seguridad: Azure proporciona múltiples opciones de autenticación y control de acceso, asegurando que tus datos estén protegidos.
  • Facilidad de integración: Usando herramientas como Azure CLI y APIs, podemos automatizar el proceso de respaldo y facilitar su gestión.

Cómo crear un Azure Storage Account y Blob Container

Antes de comenzar con la automatización del respaldo, necesitamos crear un Storage Account y un Blob Container en Azure. Aquí te explico cómo hacerlo:

Paso 1: Crear un Azure Storage Account

  1. Accede al portal de Azure: Dirígete a Azure Portal.

  2. Crear un nuevo Storage Account:

    • Haz clic en “Crear un recurso” en la parte superior izquierda.

    • Busca “Storage account” y selecciona la opción “Storage account - blob, file, table, queue”.

    • Completa los campos requeridos:

      • Subscription: Selecciona tu suscripción de Azure.
      • Resource group: Selecciona un grupo de recursos existente o crea uno nuevo.
      • Storage account name: Define un nombre único para tu cuenta de almacenamiento.
      • Region: Elige la región donde quieres que se almacenen tus datos.
      • Performance: Deja la opción por defecto en Standard.
      • Replication: Selecciona la opción de replicación que más se ajuste a tus necesidades (por ejemplo, LRS para localmente redundante).
  3. Revisar y crear: Revisa los detalles y haz clic en “Crear”. El proceso de creación tomará unos minutos.

Paso 2: Crear un Blob Container

  1. Accede a tu Storage Account: Una vez creado el Storage Account, haz clic en él desde el portal de Azure.

  2. Crear un Blob Container:

    • Dentro de tu Storage Account, ve a “Containers” en el menú de la izquierda.
    • Haz clic en ”+ Container”.
    • Asigna un nombre al contenedor (por ejemplo, ghost-backups).
    • Selecciona el nivel de acceso private.
    • Haz clic en “Crear”.

¡Listo! Ahora tienes un Storage Account y un Blob Container donde podrás almacenar tus respaldos.

Ejemplo práctico: Respaldo automatizado de Ghost con Azure Blob Storage

En este ejemplo, vamos a ver cómo podemos respaldar los datos de un sitio web usando Azure Blob Storage. Aunque el ejemplo es sobre Ghost, este proceso puede adaptarse fácilmente a cualquier otro sistema.

¿Qué haremos?

  • Generar el respaldo: Primero, crearemos un respaldo de los datos de Ghost y su base de datos MySQL.
  • Comprimir los archivos: Luego, comprimiremos el respaldo en un archivo .tar.gz para reducir el tamaño y hacerlo más eficiente.
  • Subir a Azure: Finalmente, utilizaremos Azure CLI para subir el archivo comprimido a Azure Blob Storage.

Paso 1: Crear el respaldo de los datos

El primer paso es crear el respaldo de los datos de tu servidor, que incluye tanto los archivos de Ghost como la base de datos MySQL.

Terminal window
# Conectar al servidor y crear respaldo
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"tar -czf /tmp/ghost-backup.tar.gz /opt/ghost/data /tmp/ghost_database.sql"

Este comando crea un archivo comprimido que contiene tanto los archivos de Ghost como la base de datos MySQL.

Paso 2: Subir el respaldo a Azure Blob Storage

Una vez que tenemos el archivo de respaldo, el siguiente paso es subirlo a Azure Blob Storage. Esto se hace de manera sencilla utilizando Azure CLI:

Terminal window
# Subir el archivo de respaldo a Azure Blob Storage
az storage blob upload \
--account-name $AZURE_STORAGE_ACCOUNT \
--container-name $CONTAINER_NAME \
--name "ghost-backup-${TIMESTAMP}.tar.gz" \
--file "/tmp/ghost-backup.tar.gz" \
--sas-token $SAS_TOKEN

Este comando sube el archivo comprimido a tu contenedor en Azure Blob Storage, asegurando que el respaldo esté almacenado de forma segura en la nube.

Paso 3: Limpiar respaldos antiguos

La gestión de los respaldos no termina solo con la creación y almacenamiento. También es importante mantener tu almacenamiento limpio y organizado. Por eso, puedes establecer una política de retención que elimine los respaldos antiguos.

Terminal window
# Limpiar respaldos antiguos en Azure
az storage blob list \
--account-name $AZURE_STORAGE_ACCOUNT \
--container-name $CONTAINER_NAME \
--query "[?properties.lastModified < '${CUTOFF_DATE}'].name" \
--output tsv \
--sas-token $SAS_TOKEN | while read blob_name; do
az storage blob delete \
--account-name $AZURE_STORAGE_ACCOUNT \
--container-name $CONTAINER_NAME \
--name "$blob_name" \
--sas-token $SAS_TOKEN
done

Este comando lista los respaldos antiguos y los elimina automáticamente según la fecha de retención configurada, ayudando a liberar espacio en Azure y mantener solo los respaldos más recientes.

Código completo de la automatización con GitHub Actions

A continuación, te presento el workflow completo de GitHub Actions que implementa todo el sistema de respaldos automatizado. Este archivo YAML incluye todas las funcionalidades que hemos discutido: conexión SSH segura, respaldo de base de datos MySQL, compresión de archivos, subida a Azure Blob Storage y limpieza automática de respaldos antiguos.

El workflow está diseñado para ejecutarse automáticamente cada día a las 3:00 AM (hora española) y también puede ejecutarse manualmente cuando sea necesario. Incluye manejo robusto de errores, validaciones de espacio en disco y notificaciones automáticas en caso de fallos.

Características principales del código:

  • ✅ Respaldo automático diario y manual bajo demanda
  • ✅ Validación de espacio en disco antes de crear respaldos
  • ✅ Compresión eficiente con tar.gz
  • ✅ Subida segura a Azure Blob Storage
  • ✅ Limpieza automática de respaldos antiguos (60 días de retención)
  • ✅ Notificaciones automáticas en caso de errores
  • ✅ Logs detallados para facilitar el debugging

Guarda este código en .github/workflows/backup-ghost-data.yml en tu repositorio:

name: Respaldo de Datos Ghost
on:
# Ejecutar manualmente
workflow_dispatch:
# Ejecutar automáticamente todos los días a las 3:00 AM hora española (1:00 AM UTC en invierno, 2:00 AM UTC en verano)
schedule:
- cron: "0 2 * * *" # 3:00 AM CEST (verano)
env:
RETENTION_DAYS: 60
CONTAINER_NAME: ghost-backups
AZURE_STORAGE_ACCOUNT: aprendejaponesbackup
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
backup:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Descargar repositorio
uses: actions/checkout@v5
- name: Configurar clave SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
- name: Crear directorio de respaldo
run: |
echo "📁 Creando directorio de backup local..."
mkdir -p backup
echo "✅ Directorio creado en: $(pwd)/backup"
echo "💾 Espacio disponible en disco:"
df -h $(pwd) | tail -1
- name: Generar nombre del archivo de respaldo
id: backup_info
run: |
echo "🏷️ Generando nombre de archivo de backup..."
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_START_TIME=$(date +%s)
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Ejecución manual - usar carpeta snapshots
FOLDER="snapshots"
FILENAME="ghost-snapshot-${TIMESTAMP}.tar.gz"
echo "📸 Tipo: Snapshot manual"
else
# Ejecución automática - carpeta raíz
FOLDER=""
FILENAME="ghost-data-backup-${TIMESTAMP}.tar.gz"
echo "⏰ Tipo: Backup automático"
fi
echo "📝 Archivo: ${FILENAME}"
echo "📂 Carpeta: ${FOLDER:-'(raíz)'}"
echo "filename=${FILENAME}" >> $GITHUB_OUTPUT
echo "folder=${FOLDER}" >> $GITHUB_OUTPUT
echo "timestamp=${TIMESTAMP}" >> $GITHUB_OUTPUT
echo "start_time=${BACKUP_START_TIME}" >> $GITHUB_OUTPUT
- name: Respaldar base de datos MySQL
run: |
echo "🗄️ Iniciando backup de la base de datos MySQL..."
echo "🔗 Conectando al servidor ${{ secrets.SERVER_HOST }}..."
# Crear directorio temporal para el backup de la BD
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"mkdir -p /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}"
# Obtener el nombre del contenedor MySQL
echo "🔍 Buscando contenedor MySQL..."
MYSQL_CONTAINER=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo docker ps --format '{{.Names}}' | grep -i mysql | head -1")
if [ -z "$MYSQL_CONTAINER" ]; then
echo "⚠️ No se encontró contenedor MySQL, intentando con 'db' o 'mysql'..."
MYSQL_CONTAINER=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo docker ps --format '{{.Names}}' | grep -E 'db|mysql' | head -1")
fi
if [ -z "$MYSQL_CONTAINER" ]; then
echo "❌ ERROR: No se pudo encontrar el contenedor MySQL"
echo "📋 Contenedores disponibles:"
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} "sudo docker ps --format '{{.Names}}'"
exit 1
fi
echo "✅ Contenedor MySQL encontrado: ${MYSQL_CONTAINER}"
# Exportar la base de datos
echo "💾 Exportando base de datos 'ghost'..."
# Intentar primero con el usuario de la base de datos
echo "🔐 Intentando con usuario de base de datos..."
if ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo docker exec -e MYSQL_PWD='${{ secrets.DATABASE_PASSWORD }}' ${MYSQL_CONTAINER} mysqldump -u ${{ secrets.DATABASE_USER }} ghost | sudo tee /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}/ghost_database.sql > /dev/null" 2>/dev/null; then
echo "✅ Backup exitoso con usuario de base de datos"
else
echo "⚠️ Falló con usuario de BD, intentando con root..."
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo docker exec -e MYSQL_PWD='${{ secrets.MYSQL_ROOT_PASSWORD }}' ${MYSQL_CONTAINER} mysqldump -u root ghost | sudo tee /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}/ghost_database.sql > /dev/null"
echo "✅ Backup exitoso con usuario root"
fi
# Verificar que el backup se creó correctamente
DB_SIZE=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"ls -lh /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}/ghost_database.sql | awk '{print \$5}'")
echo "✅ Base de datos exportada exitosamente"
echo "📊 Tamaño del dump SQL: ${DB_SIZE}"
- name: Respaldar datos de Ghost vía SSH
run: |
echo "🔗 Conectando al servidor ${{ secrets.SERVER_HOST }}..."
echo "📦 Creando backup comprimido en el servidor..."
echo "📍 Origen archivos: /opt/ghost/data/ghost"
echo "📍 Origen BD: /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}/ghost_database.sql"
echo "📍 Destino temporal: /tmp/${{ steps.backup_info.outputs.filename }}"
# Verificar espacio disponible en el servidor
echo "💾 Verificando espacio en servidor..."
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"echo 'Disco principal:' && df -h /"
# Validar espacio libre mínimo (30%)
echo "🔍 Validando espacio libre mínimo..."
DISK_USAGE=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"df / | tail -1 | awk '{print \$5}' | sed 's/%//'")
echo "📊 Uso actual del disco: ${DISK_USAGE}%"
if [ "$DISK_USAGE" -gt 70 ]; then
echo "❌ ERROR: Espacio insuficiente en el servidor"
echo " 💾 Uso actual: ${DISK_USAGE}% (máximo permitido: 70%)"
echo " 🚨 Se requiere al menos 30% de espacio libre para crear el backup"
echo " 💡 Libera espacio en el servidor antes de continuar"
exit 1
else
echo "✅ Espacio suficiente: ${DISK_USAGE}% usado ($((100-DISK_USAGE))% libre)"
fi
# Estimar tamaño del backup y verificar si cabe
echo "📏 Estimando tamaño del backup..."
DATA_SIZE_KB=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"du -sk /opt/ghost/data 2>/dev/null | awk '{print \$1}' || echo '0'")
if [ "$DATA_SIZE_KB" -gt 0 ]; then
# Estimar que el backup comprimido será ~60% del tamaño original
ESTIMATED_BACKUP_KB=$((DATA_SIZE_KB * 60 / 100))
AVAILABLE_KB=$(ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"df / | tail -1 | awk '{print \$4}'")
echo "📊 Tamaño de datos: $((DATA_SIZE_KB/1024)) MB"
echo "📊 Backup estimado: $((ESTIMATED_BACKUP_KB/1024)) MB (comprimido)"
echo "📊 Espacio disponible: $((AVAILABLE_KB/1024)) MB"
if [ "$ESTIMATED_BACKUP_KB" -gt "$AVAILABLE_KB" ]; then
echo "❌ ERROR: El backup estimado no cabe en el disco"
echo " 📦 Backup estimado: $((ESTIMATED_BACKUP_KB/1024)) MB"
echo " 💾 Espacio disponible: $((AVAILABLE_KB/1024)) MB"
echo " 🚨 Se necesitan al menos $((ESTIMATED_BACKUP_KB/1024)) MB libres"
exit 1
else
echo "✅ El backup estimado cabe en el disco"
fi
else
echo "⚠️ No se pudo estimar el tamaño, continuando..."
fi
# Crear backup que incluye archivos y base de datos
echo "📦 Creando archivo tar.gz con archivos y base de datos..."
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo tar -czf /tmp/${{ steps.backup_info.outputs.filename }} \
-C /opt/ghost/data --exclude='ghost/content/themes' ghost \
-C /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }} ghost_database.sql"
echo "✅ Backup creado en el servidor"
echo "⬇️ Descargando backup al runner de GitHub..."
scp -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }}:/tmp/${{ steps.backup_info.outputs.filename }} backup/
echo "✅ Backup descargado exitosamente"
echo "📊 Información del archivo:"
FILE_SIZE=$(ls -lh backup/${{ steps.backup_info.outputs.filename }} | awk '{print $5}')
FILE_SIZE_BYTES=$(stat -c%s backup/${{ steps.backup_info.outputs.filename }})
echo " 📏 Tamaño: ${FILE_SIZE} (${FILE_SIZE_BYTES} bytes)"
echo " 📍 Ubicación: $(pwd)/backup/${{ steps.backup_info.outputs.filename }}"
echo " 🗜️ Compresión: tar.gz"
echo " 📦 Contenido: archivos Ghost + base de datos MySQL"
- name: Limpiar archivos temporales del servidor
if: always()
run: |
echo "🧹 Limpiando archivos temporales del servidor..."
echo "📍 Eliminando: /tmp/${{ steps.backup_info.outputs.filename }}"
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo rm -f /tmp/${{ steps.backup_info.outputs.filename }}" || true
echo "📍 Eliminando directorio temporal de BD: /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}"
ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SERVER_HOST }} \
"sudo rm -rf /tmp/ghost-backup-${{ steps.backup_info.outputs.timestamp }}" || true
echo "✅ Limpieza del servidor completada"
- name: Instalar Azure CLI
run: |
echo "⚙️ Instalando Azure CLI..."
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
echo "✅ Azure CLI instalado"
- name: Subir respaldo a Azure Storage
run: |
echo "☁️ Preparando subida a Azure Storage..."
# Determinar la ruta completa del blob
if [ -n "${{ steps.backup_info.outputs.folder }}" ]; then
BLOB_NAME="${{ steps.backup_info.outputs.folder }}/${{ steps.backup_info.outputs.filename }}"
else
BLOB_NAME="${{ steps.backup_info.outputs.filename }}"
fi
echo "📍 Storage Account: ${{ env.AZURE_STORAGE_ACCOUNT }}"
echo "📍 Container: ${{ env.CONTAINER_NAME }}"
echo "📍 Blob: ${BLOB_NAME}"
# Mostrar tamaño antes de subir
FILE_SIZE=$(ls -lh backup/${{ steps.backup_info.outputs.filename }} | awk '{print $5}')
echo "📦 Archivo a subir: ${FILE_SIZE}"
echo "⬆️ Iniciando subida a Azure Storage..."
az storage blob upload \
--account-name ${{ env.AZURE_STORAGE_ACCOUNT }} \
--container-name ${{ env.CONTAINER_NAME }} \
--name "${BLOB_NAME}" \
--file backup/${{ steps.backup_info.outputs.filename }} \
--sas-token "${{ secrets.AZURE_SAS_TOKEN }}"
echo "✅ Backup subido exitosamente a Azure Storage"
echo "🌐 URL completa: https://${{ env.AZURE_STORAGE_ACCOUNT }}.blob.core.windows.net/${{ env.CONTAINER_NAME }}/${BLOB_NAME}"
- name: Limpiar respaldos antiguos en Azure Storage
run: |
echo "🗓️ Iniciando limpieza de backups antiguos..."
# Obtener fecha límite para retención
CUTOFF_DATE=$(date -d "${{ env.RETENTION_DAYS }} days ago" +%Y-%m-%d)
echo "📅 Fecha límite: ${CUTOFF_DATE} (archivos anteriores serán eliminados)"
echo "⏳ Retención configurada: ${{ env.RETENTION_DAYS }} días"
# Limpiar backups automáticos antiguos
echo "🧹 Limpiando backups automáticos antiguos..."
echo "🔍 Buscando archivos con prefijo: ghost-data-backup-"
az storage blob list \
--account-name ${{ env.AZURE_STORAGE_ACCOUNT }} \
--container-name ${{ env.CONTAINER_NAME }} \
--prefix "ghost-data-backup-" \
--query "[?properties.lastModified < '${CUTOFF_DATE}'].name" \
--output tsv \
--sas-token "${{ secrets.AZURE_SAS_TOKEN }}" | while read blob_name; do
if [ ! -z "$blob_name" ]; then
echo "🗑️ Eliminando backup automático antiguo: $blob_name"
az storage blob delete \
--account-name ${{ env.AZURE_STORAGE_ACCOUNT }} \
--container-name ${{ env.CONTAINER_NAME }} \
--name "$blob_name" \
--sas-token "${{ secrets.AZURE_SAS_TOKEN }}"
fi
done
# Limpiar snapshots manuales antiguos
echo "🧹 Limpiando snapshots manuales antiguos..."
echo "🔍 Buscando archivos con prefijo: snapshots/ghost-snapshot-"
az storage blob list \
--account-name ${{ env.AZURE_STORAGE_ACCOUNT }} \
--container-name ${{ env.CONTAINER_NAME }} \
--prefix "snapshots/ghost-snapshot-" \
--query "[?properties.lastModified < '${CUTOFF_DATE}'].name" \
--output tsv \
--sas-token "${{ secrets.AZURE_SAS_TOKEN }}" | while read blob_name; do
if [ ! -z "$blob_name" ]; then
echo "🗑️ Eliminando snapshot manual antiguo: $blob_name"
az storage blob delete \
--account-name ${{ env.AZURE_STORAGE_ACCOUNT }} \
--container-name ${{ env.CONTAINER_NAME }} \
--name "$blob_name" \
--sas-token "${{ secrets.AZURE_SAS_TOKEN }}"
fi
done
echo "✅ Limpieza de archivos antiguos completada"
- name: Limpiar clave SSH
if: always()
run: |
echo "🔐 Limpiando clave SSH temporal..."
rm -f ~/.ssh/id_rsa
echo "✅ Clave SSH eliminada"
- name: Resumen del respaldo
id: backup_summary
run: |
echo "✅ Backup completado exitosamente"
echo "📁 Archivo: ${{ steps.backup_info.outputs.filename }}"
# Calcular tiempo total de backup
END_TIME=$(date +%s)
DURATION=$((END_TIME - ${{ steps.backup_info.outputs.start_time }}))
DURATION_MIN=$((DURATION / 60))
DURATION_SEC=$((DURATION % 60))
echo "⏱️ Tiempo total: ${DURATION_MIN}m ${DURATION_SEC}s"
echo "duration=${DURATION}" >> $GITHUB_OUTPUT
# Obtener tamaño del backup
BACKUP_SIZE=$(ls -lh backup/${{ steps.backup_info.outputs.filename }} 2>/dev/null | awk '{print $5}' || echo "N/A")
echo "backup_size=${BACKUP_SIZE}" >> $GITHUB_OUTPUT
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "📸 Tipo: Snapshot manual"
echo "☁️ Ubicación: ${{ env.AZURE_STORAGE_ACCOUNT }}/${{ env.CONTAINER_NAME }}/snapshots/"
else
echo "⏰ Tipo: Backup automático"
echo "☁️ Ubicación: ${{ env.AZURE_STORAGE_ACCOUNT }}/${{ env.CONTAINER_NAME }}/"
fi
echo "🗓️ Retención configurada: ${{ env.RETENTION_DAYS }} días"
# Notificación solo en caso de fallo
notificar-fallo:
needs: backup
runs-on: ubuntu-latest
if: failure()
permissions:
issues: write
steps:
- name: Crear notificación de fallo
uses: actions/github-script@v7
with:
script: |
const timestamp = new Date().toLocaleString('es-ES', {
timeZone: 'Europe/Madrid',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
const backupType = '${{ github.event_name }}' === 'workflow_dispatch' ? 'Snapshot Manual' : 'Backup Automático';
const title = `🚨 Error en ${backupType} - ${timestamp}`;
const body = `## 🚨 Error en el Sistema de Backups
**Fecha:** ${timestamp}
**Tipo:** ${backupType}
**Estado:** ❌ El backup ha fallado
### 📋 Detalles del Error
- **Workflow:** \`${{ github.workflow }}\`
- **Run ID:** \`${{ github.run_id }}\`
- **Commit:** \`${{ github.sha }}\`
- **Rama:** \`${{ github.ref_name }}\`
### 🔗 Enlaces Útiles
- [Ver logs del workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
- [Ejecutar backup manual](https://github.com/${{ github.repository }}/actions/workflows/backup-ghost-data.yml)
### 🛠️ Acciones Recomendadas
1. **Revisar los logs** del workflow para identificar el error específico
2. **Verificar conectividad** SSH al servidor
3. **Comprobar espacio** disponible en el servidor
4. **Validar credenciales** de Azure Storage
5. **Ejecutar backup manual** una vez solucionado el problema
### ⚠️ Importante
- Los datos de Ghost no están siendo respaldados automáticamente
- Se recomienda solucionar este problema lo antes posible
- Considera ejecutar un backup manual mientras se resuelve el issue
---
*Notificación automática del sistema de backups*
/cc @danieljsaldana`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: body,
assignees: ['danieljsaldana'],
labels: ['backup', 'error', 'urgent']
});

Beneficios de usar Azure Blob Storage para tus respaldos

  • Automatización: Gracias a herramientas como Azure CLI, puedes automatizar el proceso de creación y almacenamiento de respaldos, eliminando la intervención manual.
  • Escalabilidad: Puedes almacenar un número prácticamente ilimitado de respaldos sin preocuparte por quedarte sin espacio.
  • Seguridad: Con Azure, tus datos están protegidos por varias capas de seguridad, incluyendo autenticación avanzada y cifrado en reposo.
  • Retención eficiente: Azure te permite configurar políticas de retención para eliminar respaldos antiguos, lo que te ayuda a optimizar el uso del espacio.
Backups de ejemplo

Conclusión

El respaldo de tus datos no tiene por qué ser complicado. Usando Azure Blob Storage, puedes garantizar que tus datos estén siempre seguros y accesibles, al mismo tiempo que automatizas el proceso para que no tengas que preocuparte por hacerlo manualmente. No importa si trabajas con Ghost o cualquier otro sistema, Azure te ofrece la flexibilidad y escalabilidad que necesitas.

¡Suscríbete y recibe actualizaciones sobre tecnología, diseño, productividad, programación y mucho más!
0
0