💾 Backup System - Gunei ERP (Enterprise Architecture)
Sistema automatizado de backups para PostgreSQL Shared con verificación de integridad, políticas de retención y soporte multi-ambiente.
Versión 2.1 - PostgreSQL Shared + Multi-Ambiente
📋 Tabla de Contenidos
- Quick Reference
- Descripción General
- Arquitectura de Backups
- Schedule Automático
- Scripts de Backup
- Proceso de Restore
- Gestión por Ambiente
- Políticas de Retención
- Monitoreo de Backups
- Troubleshooting
⚡ Quick Reference
Comandos Más Usados
# === BACKUP ===
# Backup de ambos ambientes
/root/scripts/backup-postgres.sh
# Backup solo staging
docker exec postgres-shared pg_dump -U gunei_staging_user gunei_erp_staging | gzip > backup_staging_$(date +%Y%m%d).sql.gz
# Backup solo production
docker exec postgres-shared pg_dump -U gunei_prod_user gunei_erp_production | gzip > backup_production_$(date +%Y%m%d).sql.gz
# === RESTORE ===
# Restore staging
gunzip -c backup_staging.sql.gz | docker exec -i postgres-shared psql -U gunei_staging_user -d gunei_erp_staging
# Restore production
gunzip -c backup_production.sql.gz | docker exec -i postgres-shared psql -U gunei_prod_user -d gunei_erp_production
# === VERIFICACIÓN ===
# Ver backups recientes
ls -lht /opt/infrastructure/postgres/backups/staging/ | head -5
ls -lht /opt/infrastructure/postgres/backups/production/ | head -5
# Ver logs
tail -50 /var/log/gunei-backups.log
# Dashboard
/root/scripts/backup-dashboard.sh
Conexión a PostgreSQL
# Desde el host (puerto 5433)
psql -h localhost -p 5433 -U postgres
psql -h localhost -p 5433 -U gunei_staging_user -d gunei_erp_staging
psql -h localhost -p 5433 -U gunei_prod_user -d gunei_erp_production
# Desde container (puerto 5432 interno)
docker exec -it postgres-shared psql -U postgres
docker exec -it postgres-shared psql -U gunei_staging_user -d gunei_erp_staging
docker exec -it postgres-shared psql -U gunei_prod_user -d gunei_erp_production
Ubicaciones Clave
| Recurso | Path |
|---|---|
| Scripts | /root/scripts/ |
| Backups Staging | /opt/infrastructure/postgres/backups/staging/ |
| Backups Production | /opt/infrastructure/postgres/backups/production/ |
| Logs | /var/log/gunei-backups.log |
| PostgreSQL Data | /opt/infrastructure/postgres/data/ |
Timezone
Todos los timestamps de backups usan hora Argentina:
TZ=America/Argentina/Buenos_Aires
Nomenclatura de archivos:
backup_staging_YYYYMMDD_HHMMSS.sql.gz # Hora Argentina
backup_production_YYYYMMDD_HHMMSS.sql.gz # Hora Argentina
Verificación:
# Ver timezone del contenedor PostgreSQL
docker exec postgres-shared date
# Ver timestamp de últimos backups
ls -lh /opt/infrastructure/postgres/backups/staging/ | head -5
ls -lh /opt/infrastructure/postgres/backups/production/ | head -5
Implicaciones:
- Cron ejecuta en hora local del servidor (Argentina)
- Nombres de archivos reflejan hora Argentina
- Logs de backup con timestamps en hora local
🎯 Descripción General
Objetivos
- Backups diarios automáticos de PostgreSQL Shared (ambos ambientes)
- Verificación de integridad de cada backup
- Retención inteligente (diarios, semanales, mensuales)
- Alertas de espacio en disco
- Proceso de restore documentado y probado por ambiente
- Disaster recovery para staging y production
Características
- Multi-Ambiente: Backup separado de
gunei_erp_stagingygunei_erp_production - Compresión: gzip para ahorrar espacio (~70% reducción)
- Metadata: Timestamp, tamaño, checksum, ambiente
- Verificación: Test de restore automático post-backup
- Cleanup: Retención de 30 días con semanales/mensuales extra
- Logs: Registro completo de todas las operaciones
Ubicación
# Directorio de backups (organizado por ambiente)
/opt/infrastructure/postgres/backups/
├── staging/
│ ├── backup_staging_20260112_020000.sql.gz
│ ├── backup_staging_20260112_020000.sql.gz.meta
│ └── ...
└── production/
├── backup_production_20260112_020000.sql.gz
├── backup_production_20260112_020000.sql.gz.meta
└── ...
# Scripts de backup
/root/scripts/
├── backup-postgres.sh # Backup principal (ambos ambientes)
├── backup-db-staging.sh # Backup solo staging
├── backup-db-production.sh # Backup solo production
├── restore-backup.sh # Restore (especificar ambiente)
├── verify-backup.sh # Verificación de integridad
├── cleanup-backups.sh # Limpieza con retención
└── check-disk-space.sh # Monitoreo de disco
🏗️ Arquitectura de Backups
PostgreSQL Shared
PostgreSQL Shared (postgres-shared:5433)
├── gunei_erp_staging
│ ├── Usuario: gunei_staging_user
│ └── Backup: /opt/infrastructure/postgres/backups/staging/
└── gunei_erp_production
├── Usuario: gunei_prod_user
└── Backup: /opt/infrastructure/postgres/backups/production/
Flujo de Backup
Cron (02:00 AM)
↓
backup-postgres.sh
↓
├─→ backup-db-staging.sh
│ ↓
│ pg_dump gunei_erp_staging
│ ↓
│ Compress → Metadata → Log
│
└─→ backup-db-production.sh
↓
pg_dump gunei_erp_production
↓
Compress → Metadata → Log
Databases y Usuarios
| Ambiente | Database | Usuario | Password Location |
|---|---|---|---|
| Staging | gunei_erp_staging | gunei_staging_user | /opt/apps/gunei-erp/backend/staging/.env |
| Production | gunei_erp_production | gunei_prod_user | /opt/apps/gunei-erp/backend/production/.env |
⏰ Schedule Automático
Cron Configuration
# Ver configuración actual
crontab -l
# Editar si es necesario
crontab -e
Schedule Completo (Actualizado)
# 1. Backup diario a las 2 AM (ambos ambientes)
0 2 * * * /root/scripts/backup-postgres.sh >> /var/log/gunei-backups.log 2>&1
# 2. Verificación a las 3 AM (1 hora después del backup)
0 3 * * * /root/scripts/verify-backup.sh >> /var/log/gunei-backups.log 2>&1
# 3. Cleanup semanal (domingos a las 4 AM)
0 4 * * 0 /root/scripts/cleanup-backups.sh >> /var/log/gunei-backups.log 2>&1
# 4. Check de espacio diario a las 5 AM
0 5 * * * /root/scripts/check-disk-space.sh >> /var/log/gunei-health.log 2>&1
Timeline Típico
02:00 → Backup inicia (staging + production)
02:05 → Backup staging completo (5 minutos)
02:10 → Backup production completo (5 minutos)
02:10 → Compresión y metadata
03:00 → Verificación de integridad inicia
03:05 → Verificación staging completa
03:10 → Verificación production completa
04:00 → Cleanup (solo domingos)
05:00 → Check de espacio en disco
Duración total: ~15-20 minutos para ambos ambientes
🔧 Scripts de Backup
1. backup-postgres.sh (Principal)
Propósito: Backup automático de ambos ambientes
#!/bin/bash
# Backup completo de PostgreSQL Shared (staging + production)
# Uso
/root/scripts/backup-postgres.sh
# O especificar solo un ambiente
/root/scripts/backup-postgres.sh staging
/root/scripts/backup-postgres.sh production
Proceso:
- Detecta ambientes disponibles
- Para cada ambiente:
- Genera timestamp único
- Ejecuta
pg_dumpcon usuario correcto - Comprime con gzip (nivel 9)
- Guarda metadata (tamaño, checksum, ambiente)
- Registra en log
- Reporta resultado consolidado
Ejemplo de implementación:
#!/bin/bash
# /root/scripts/backup-postgres.sh
BACKUP_DIR="/opt/infrastructure/postgres/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/gunei-backups.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
backup_environment() {
ENV=$1
if [ "$ENV" = "staging" ]; then
DB_NAME="gunei_erp_staging"
DB_USER="gunei_staging_user"
elif [ "$ENV" = "production" ]; then
DB_NAME="gunei_erp_production"
DB_USER="gunei_prod_user"
else
log "[ERROR] Invalid environment: $ENV"
return 1
fi
BACKUP_FILE="${BACKUP_DIR}/${ENV}/backup_${ENV}_${TIMESTAMP}.sql"
BACKUP_GZ="${BACKUP_FILE}.gz"
META_FILE="${BACKUP_GZ}.meta"
log "[INFO] Starting backup of $DB_NAME ($ENV)"
# Backup
docker exec postgres-shared pg_dump -U "$DB_USER" "$DB_NAME" > "$BACKUP_FILE"
if [ $? -ne 0 ]; then
log "[ERROR] Backup failed for $ENV"
return 1
fi
# Compress
gzip -9 "$BACKUP_FILE"
# Metadata
SIZE=$(stat -f%z "$BACKUP_GZ" 2>/dev/null || stat -c%s "$BACKUP_GZ")
CHECKSUM=$(sha256sum "$BACKUP_GZ" | awk '{print $1}')
cat > "$META_FILE" << EOF
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
ENVIRONMENT=$ENV
DATABASE=$DB_NAME
SIZE=$SIZE
CHECKSUM=$CHECKSUM
EOF
log "[INFO] Backup completed: $ENV ($(du -h $BACKUP_GZ | cut -f1))"
return 0
}
# Main
log "[INFO] === PostgreSQL Shared Backup Started ==="
if [ -n "$1" ]; then
# Backup solo el ambiente especificado
backup_environment "$1"
else
# Backup ambos ambientes
backup_environment "staging"
backup_environment "production"
fi
log "[INFO] === PostgreSQL Shared Backup Completed ==="
Output:
# Nombres de archivos
/opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz
/opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz.meta
/opt/infrastructure/postgres/backups/production/backup_production_20260112_020000.sql.gz
/opt/infrastructure/postgres/backups/production/backup_production_20260112_020000.sql.gz.meta
Metadata incluida:
TIMESTAMP=2026-01-12 02:00:00
ENVIRONMENT=staging
DATABASE=gunei_erp_staging
SIZE=45123456 # bytes
CHECKSUM=a1b2c3d4e5f6...
Logs:
[2026-01-12 02:00:00] [INFO] === PostgreSQL Shared Backup Started ===
[2026-01-12 02:00:00] [INFO] Starting backup of gunei_erp_staging (staging)
[2026-01-12 02:04:45] [INFO] Backup completed: staging (12.3 MB)
[2026-01-12 02:05:00] [INFO] Starting backup of gunei_erp_production (production)
[2026-01-12 02:09:30] [INFO] Backup completed: production (8.7 MB)
[2026-01-12 02:09:30] [INFO] === PostgreSQL Shared Backup Completed ===
2. backup-db-staging.sh / backup-db-production.sh
Propósito: Backup de un ambiente específico
#!/bin/bash
# Backup solo staging
/root/scripts/backup-db-staging.sh
# Backup solo production
/root/scripts/backup-db-production.sh
Estos scripts son wrappers que llaman a backup-postgres.sh con el ambiente específico:
#!/bin/bash
# /root/scripts/backup-db-staging.sh
/root/scripts/backup-postgres.sh staging
#!/bin/bash
# /root/scripts/backup-db-production.sh
/root/scripts/backup-postgres.sh production
3. restore-backup.sh (Actualizado)
Propósito: Restaurar base de datos desde backup especificando ambiente
#!/bin/bash
# Restore de backup con validación
# Uso
/root/scripts/restore-backup.sh <backup-file> <environment> [target-db]
# Ejemplos:
# Restore staging a su database original
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging
# Restore production a su database original (CUIDADO!)
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/production/backup_production_20260112_020000.sql.gz production
# Restore staging a database temporal para testing
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging gunei_erp_test
Proceso:
- Valida que el archivo existe
- Verifica checksum contra metadata
- Determina usuario correcto según ambiente
- Descomprime backup
- Crea base de datos destino (si no existe)
- Ejecuta
psqlcon usuario correcto - Valida integridad post-restore
- Registra resultado
Ejemplo de implementación:
#!/bin/bash
# /root/scripts/restore-backup.sh
BACKUP_FILE=$1
ENVIRONMENT=$2
TARGET_DB=$3
if [ -z "$BACKUP_FILE" ] || [ -z "$ENVIRONMENT" ]; then
echo "Usage: $0 <backup-file> <environment> [target-db]"
exit 1
fi
# Determinar database y usuario según ambiente
if [ "$ENVIRONMENT" = "staging" ]; then
DEFAULT_DB="gunei_erp_staging"
DB_USER="gunei_staging_user"
elif [ "$ENVIRONMENT" = "production" ]; then
DEFAULT_DB="gunei_erp_production"
DB_USER="gunei_prod_user"
else
echo "[ERROR] Invalid environment: $ENVIRONMENT"
exit 1
fi
# Usar target-db si se especificó, sino usar default
DB_NAME=${TARGET_DB:-$DEFAULT_DB}
echo "[INFO] Restoring to $DB_NAME ($ENVIRONMENT)"
# Descomprimir
TMP_FILE="/tmp/restore_${ENVIRONMENT}_$$.sql"
gunzip -c "$BACKUP_FILE" > "$TMP_FILE"
# Restore
docker exec -i postgres-shared psql -U "$DB_USER" -d "$DB_NAME" < "$TMP_FILE"
if [ $? -eq 0 ]; then
echo "[INFO] Restore completed successfully"
else
echo "[ERROR] Restore failed"
rm "$TMP_FILE"
exit 1
fi
# Cleanup
rm "$TMP_FILE"
Logs:
[2026-01-12 10:30:00] [INFO] Starting restore from backup_staging_20260112_020000.sql.gz
[2026-01-12 10:30:01] [INFO] Environment: staging
[2026-01-12 10:30:01] [INFO] Target database: gunei_erp_staging
[2026-01-12 10:30:01] [INFO] Checksum verified: OK
[2026-01-12 10:30:05] [INFO] Decompressing backup...
[2026-01-12 10:30:15] [INFO] Restoring data...
[2026-01-12 10:35:42] [INFO] Restore completed successfully
4. verify-backup.sh (Actualizado)
Propósito: Verificar integridad de los backups de ambos ambientes
#!/bin/bash
# Verificación automática post-backup
# Uso (automático vía cron)
/root/scripts/verify-backup.sh
# O manual para backup específico
/root/scripts/verify-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging
Proceso:
- Identifica últimos backups de cada ambiente
- Para cada backup:
- Verifica checksum
- Intenta descomprimir (test)
- Crea DB temporal de prueba
- Ejecuta restore completo
- Valida integridad de datos
- Limpia DB temporal
- Registra resultado consolidado
Validaciones por ambiente:
- Checksum coincide con metadata
- Archivo descomprime correctamente
psqlcompleta sin errores- Tablas esperadas existen
- Conteo de filas razonable
- Índices y constraints presentes
Logs exitosos:
[2026-01-12 03:00:00] [INFO] === Backup Verification Started ===
[2026-01-12 03:00:00] [INFO] Verifying staging: backup_staging_20260112_020000.sql.gz
[2026-01-12 03:00:01] [INFO] Checksum: OK
[2026-01-12 03:00:05] [INFO] Decompression test: OK
[2026-01-12 03:00:10] [INFO] Restore test started
[2026-01-12 03:04:32] [INFO] Restore test: OK
[2026-01-12 03:04:33] [INFO] ✅ Staging backup verified successfully
[2026-01-12 03:04:40] [INFO] Verifying production: backup_production_20260112_020000.sql.gz
[2026-01-12 03:04:41] [INFO] Checksum: OK
[2026-01-12 03:04:45] [INFO] Decompression test: OK
[2026-01-12 03:04:50] [INFO] Restore test started
[2026-01-12 03:08:12] [INFO] Restore test: OK
[2026-01-12 03:08:13] [INFO] ✅ Production backup verified successfully
[2026-01-12 03:08:13] [INFO] === All Backups Verified ===
5. cleanup-backups.sh (Actualizado)
Propósito: Aplicar política de retención por ambiente
#!/bin/bash
# Limpieza con retención inteligente
# Uso (automático domingos vía cron)
/root/scripts/cleanup-backups.sh
# O forzar limpieza manual
/root/scripts/cleanup-backups.sh --force
# Limpiar solo un ambiente
/root/scripts/cleanup-backups.sh staging
/root/scripts/cleanup-backups.sh production
Política de retención (por ambiente):
Últimos 7 días: TODOS los backups diarios
Últimas 4 semanas: 1 backup por semana (domingo)
Últimos 12 meses: 1 backup por mes (primer domingo)
Más antiguos: ELIMINAR
Logs:
[2026-01-12 04:00:00] [INFO] === Backup Cleanup Started ===
[2026-01-12 04:00:00] [INFO] Policy: 7 daily, 4 weekly, 12 monthly
[2026-01-12 04:00:01] [INFO] Cleaning staging backups...
[2026-01-12 04:00:01] [INFO] Found 45 staging backups
[2026-01-12 04:00:02] [INFO] Keeping 23 staging backups
[2026-01-12 04:00:02] [INFO] Removing 22 old staging backups
[2026-01-12 04:00:03] [INFO] Freed 180 MB from staging
[2026-01-12 04:00:03] [INFO] Cleaning production backups...
[2026-01-12 04:00:03] [INFO] Found 38 production backups
[2026-01-12 04:00:04] [INFO] Keeping 20 production backups
[2026-01-12 04:00:04] [INFO] Removing 18 old production backups
[2026-01-12 04:00:05] [INFO] Freed 160 MB from production
[2026-01-12 04:00:05] [INFO] Total freed: 340 MB
[2026-01-12 04:00:05] [INFO] === Cleanup Completed ===
6. check-disk-space.sh
Propósito: Monitorear espacio en disco (sin cambios del original)
#!/bin/bash
# Alertar si disco está cerca de llenarse
# Uso (automático diario vía cron)
/root/scripts/check-disk-space.sh
Umbrales:
WARNING: 80% usado → Log warning
CRITICAL: 90% usado → Log critical + alerta Discord
EMERGENCY: 95% usado → Log emergency + múltiples alertas
🔄 Proceso de Restore
Escenario 1: Restore de Staging (Emergencia)
Situación: Database staging corrupta, necesitamos restore inmediato
# 1. Verificar último backup de staging
ls -lh /opt/infrastructure/postgres/backups/staging/ | head -n 5
# 2. Verificar integridad
/root/scripts/verify-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging
# 3. Detener backend staging
cd /opt/apps/gunei-erp/backend/staging
docker compose stop backend
# 4. Backup de seguridad de DB actual (por si acaso)
docker exec postgres-shared pg_dump -U gunei_staging_user gunei_erp_staging > /tmp/gunei_erp_staging-before-restore.sql
# 5. Eliminar DB actual
docker exec postgres-shared psql -U postgres -c "DROP DATABASE IF EXISTS gunei_erp_staging;"
# 6. Recrear DB vacía
docker exec postgres-shared psql -U postgres -c "CREATE DATABASE gunei_erp_staging;"
docker exec postgres-shared psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gunei_erp_staging TO gunei_staging_user;"
# 7. Restore
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging
# 8. Verificar restore
docker exec postgres-shared psql -U gunei_staging_user -d gunei_erp_staging -c "SELECT COUNT(*) FROM users;"
# 9. Reiniciar backend staging
cd /opt/apps/gunei-erp/backend/staging
docker compose start backend
# 10. Verificar aplicación
curl https://staging-erpback.gunei.xyz/status
docker logs gunei-backend-staging --tail 20
Tiempo estimado: 10-15 minutos
Escenario 2: Restore de Production (Crítico)
Situación: Database production corrupta, disaster recovery
# PROCESO SIMILAR A STAGING pero con paths de production:
# 1. Verificar backup
ls -lh /opt/infrastructure/postgres/backups/production/ | head -n 5
# 2. Detener backend production
cd /opt/apps/gunei-erp/backend/production
docker compose stop backend
# 3. Backup de seguridad
docker exec postgres-shared pg_dump -U gunei_prod_user gunei_erp_production > /tmp/gunei_erp_production-before-restore.sql
# 4. Drop y recrear DB
docker exec postgres-shared psql -U postgres -c "DROP DATABASE IF EXISTS gunei_erp_production;"
docker exec postgres-shared psql -U postgres -c "CREATE DATABASE gunei_erp_production;"
docker exec postgres-shared psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gunei_erp_production TO gunei_prod_user;"
# 5. Restore
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/production/backup_production_20260112_020000.sql.gz production
# 6. Verificar
docker exec postgres-shared psql -U gunei_prod_user -d gunei_erp_production -c "SELECT COUNT(*) FROM users;"
# 7. Reiniciar backend production
cd /opt/apps/gunei-erp/backend/production
docker compose start backend
# 8. Verificar aplicación (URL de production cuando esté configurada)
curl http://localhost:3100/status
docker logs gunei-backend-production --tail 20
Tiempo estimado: 10-15 minutos
Escenario 3: Copiar Staging a Testing
Situación: Necesitamos copia de staging para pruebas
# 1. Crear DB de testing
docker exec postgres-shared psql -U postgres -c "CREATE DATABASE gunei_erp_test;"
# 2. Restore backup de staging en DB de testing
/root/scripts/restore-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz staging gunei_erp_test
# 3. Ajustar permisos
docker exec postgres-shared psql -U postgres -c "GRANT ALL ON DATABASE gunei_erp_test TO gunei_staging_user;"
# 4. Usar en testing
# Actualizar DATABASE_URL temporalmente o crear nuevo .env para testing
Escenario 4: Promover Staging a Production
Situación: Migrar datos de staging a production
# ⚠️ CUIDADO: Esto SOBRESCRIBE production con datos de staging
# 1. Crear backup de production actual (safety)
/root/scripts/backup-db-production.sh
# 2. Usar backup más reciente de staging
STAGING_BACKUP="/opt/infrastructure/postgres/backups/staging/backup_staging_20260112_020000.sql.gz"
# 3. Detener backend production
cd /opt/apps/gunei-erp/backend/production
docker compose stop backend
# 4. Drop y recrear production DB
docker exec postgres-shared psql -U postgres -c "DROP DATABASE IF EXISTS gunei_erp_production;"
docker exec postgres-shared psql -U postgres -c "CREATE DATABASE gunei_erp_production;"
docker exec postgres-shared psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gunei_erp_production TO gunei_prod_user;"
# 5. Restore staging backup a production
# Nota: Usamos el backup de staging pero lo restauramos a production
gunzip -c "$STAGING_BACKUP" > /tmp/staging_to_prod.sql
docker exec -i postgres-shared psql -U gunei_prod_user -d gunei_erp_production < /tmp/staging_to_prod.sql
rm /tmp/staging_to_prod.sql
# 6. Verificar
docker exec postgres-shared psql -U gunei_prod_user -d gunei_erp_production -c "SELECT COUNT(*) FROM users;"
# 7. Reiniciar backend production
docker compose start backend
# 8. Crear nuevo backup de production (con datos migrados)
/root/scripts/backup-db-production.sh
Escenario 5: Restore Selectivo (Solo Ciertas Tablas)
Situación: Necesitamos recuperar solo tabla clientes de staging
# 1. Descomprimir backup
cd /opt/infrastructure/postgres/backups/staging
gunzip -c backup_staging_20260112_020000.sql.gz > /tmp/backup_staging.sql
# 2. Extraer solo tabla clientes (usando grep/sed o pg_restore si está en formato custom)
# Si es SQL plano:
grep -A 1000 "CREATE TABLE clientes" /tmp/backup_staging.sql > /tmp/clientes_only.sql
# 3. Restore solo esa tabla
docker exec -i postgres-shared psql -U gunei_staging_user -d gunei_erp_staging < /tmp/clientes_only.sql
# 4. Verificar
docker exec postgres-shared psql -U gunei_staging_user -d gunei_erp_staging -c "SELECT COUNT(*) FROM clientes;"
# 5. Limpiar
rm /tmp/backup_staging.sql /tmp/clientes_only.sql
🌍 Gestión por Ambiente
Estructura de Backups
/opt/infrastructure/postgres/backups/
├── staging/
│ ├── backup_staging_20260112_020000.sql.gz
│ ├── backup_staging_20260112_020000.sql.gz.meta
│ ├── backup_staging_20260111_020000.sql.gz
│ ├── backup_staging_20260111_020000.sql.gz.meta
│ └── ...
└── production/
├── backup_production_20260112_020000.sql.gz
├── backup_production_20260112_020000.sql.gz.meta
├── backup_production_20260111_020000.sql.gz
├── backup_production_20260111_020000.sql.gz.meta
└── ...
Tamaños Típicos por Ambiente
Staging (ambiente de desarrollo):
- Base de datos: ~150 MB
- Backup sin comprimir: ~150 MB
- Backup comprimido: ~40 MB (73% compresión)
- 30 backups diarios ≈ 1.2 GB
Production (cuando esté operativo):
- Base de datos: ~200 MB (esperado)
- Backup sin comprimir: ~200 MB
- Backup comprimido: ~55 MB (73% compresión)
- 30 backups diarios ≈ 1.7 GB
Total ambos ambientes: ~3 GB para 30 días de retención completa
Comandos por Ambiente
Listar backups de staging:
ls -lh /opt/infrastructure/postgres/backups/staging/
Listar backups de production:
ls -lh /opt/infrastructure/postgres/backups/production/
Ver último backup de cada ambiente:
# Staging
ls -t /opt/infrastructure/postgres/backups/staging/backup_staging_*.sql.gz | head -n 1
# Production
ls -t /opt/infrastructure/postgres/backups/production/backup_production_*.sql.gz | head -n 1
Estadísticas por ambiente:
# Script de stats
cat > /root/scripts/backup-stats.sh << 'EOF'
#!/bin/bash
echo "=== Backup Statistics by Environment ==="
echo ""
for ENV in staging production; do
BACKUP_DIR="/opt/infrastructure/postgres/backups/$ENV"
echo "--- $ENV ---"
echo "Total backups: $(ls $BACKUP_DIR/backup_${ENV}_*.sql.gz 2>/dev/null | wc -l)"
echo "Total size: $(du -sh $BACKUP_DIR 2>/dev/null | cut -f1)"
echo "Oldest: $(ls -t $BACKUP_DIR/backup_${ENV}_*.sql.gz 2>/dev/null | tail -n 1 | xargs basename)"
echo "Newest: $(ls -t $BACKUP_DIR/backup_${ENV}_*.sql.gz 2>/dev/null | head -n 1 | xargs basename)"
echo ""
done
EOF
chmod +x /root/scripts/backup-stats.sh
# Ejecutar
/root/scripts/backup-stats.sh
📊 Políticas de Retención
Política por Ambiente
Cada ambiente mantiene su propia política de retención independiente:
Últimos 7 días: TODOS los backups diarios
Últimas 4 semanas: 1 backup por semana (domingo)
Últimos 12 meses: 1 backup por mes (primer domingo)
Más antiguos: ELIMINAR
Ejemplo Práctico - Staging
Hoy: 2026-01-12 (domingo)
/opt/infrastructure/postgres/backups/staging/
CONSERVAR:
✓ backup_staging_20260112_020000.sql.gz (hoy - diario)
✓ backup_staging_20260111_020000.sql.gz (ayer - diario)
✓ backup_staging_20260110_020000.sql.gz (hace 2 días - diario)
✓ backup_staging_20260109_020000.sql.gz (hace 3 días - diario)
✓ backup_staging_20260108_020000.sql.gz (hace 4 días - diario)
✓ backup_staging_20260107_020000.sql.gz (hace 5 días - diario)
✓ backup_staging_20260106_020000.sql.gz (hace 6 días - diario)
✓ backup_staging_20260105_020000.sql.gz (hace 7 días - diario + semanal)
✓ backup_staging_20251229_020000.sql.gz (semanal - hace 2 semanas)
✓ backup_staging_20251222_020000.sql.gz (semanal - hace 3 semanas)
✓ backup_staging_20251215_020000.sql.gz (semanal - hace 4 semanas)
✓ backup_staging_20251207_020000.sql.gz (mensual - diciembre)
✓ backup_staging_20251103_020000.sql.gz (mensual - noviembre)
ELIMINAR:
✗ backup_staging_20260104_020000.sql.gz (más de 7 días, no es domingo)
✗ backup_staging_20251015_020000.sql.gz (más de 1 mes, no es primer domingo)
Ejemplo Práctico - Production
Similar proceso pero independiente de staging:
/opt/infrastructure/postgres/backups/production/
CONSERVAR y ELIMINAR según la misma política, pero aplicada independientemente
📈 Monitoreo de Backups
Verificar Último Backup por Ambiente
Staging:
# Listar backups recientes
ls -lht /opt/infrastructure/postgres/backups/staging/ | head -n 5
# Ver metadata del último
cat /opt/infrastructure/postgres/backups/staging/backup_staging_$(date +%Y%m%d)_020000.sql.gz.meta
# Verificar que el backup de hoy existe
BACKUP_TODAY="/opt/infrastructure/postgres/backups/staging/backup_staging_$(date +%Y%m%d)_020000.sql.gz"
if [ -f "$BACKUP_TODAY" ]; then
echo "✅ Backup staging de hoy existe"
else
echo "❌ Backup staging de hoy NO existe"
fi
Production:
# Listar backups recientes
ls -lht /opt/infrastructure/postgres/backups/production/ | head -n 5
# Ver metadata del último
cat /opt/infrastructure/postgres/backups/production/backup_production_$(date +%Y%m%d)_020000.sql.gz.meta
# Verificar que el backup de hoy existe
BACKUP_TODAY="/opt/infrastructure/postgres/backups/production/backup_production_$(date +%Y%m%d)_020000.sql.gz"
if [ -f "$BACKUP_TODAY" ]; then
echo "✅ Backup production de hoy existe"
else
echo "❌ Backup production de hoy NO existe"
fi
Verificar Logs de Backup
# Ver backups exitosos de ambos ambientes (últimos 7 días)
grep "Backup completed" /var/log/gunei-backups.log | tail -n 14
# Ver si hubo errores en cualquier ambiente
grep ERROR /var/log/gunei-backups.log | tail -n 20
# Ver último proceso completo
tail -n 100 /var/log/gunei-backups.log
# Filtrar por ambiente específico
grep "staging" /var/log/gunei-backups.log | tail -n 50
grep "production" /var/log/gunei-backups.log | tail -n 50
Dashboard Consolidado
# Script de monitoreo consolidado
cat > /root/scripts/backup-dashboard.sh << 'EOF'
#!/bin/bash
echo "╔═══════════════════════════════════════╗"
echo "║ Gunei ERP - Backup Dashboard ║"
echo "╚═══════════════════════════════════════╝"
echo ""
for ENV in staging production; do
BACKUP_DIR="/opt/infrastructure/postgres/backups/$ENV"
echo "┌─ $ENV Environment ─────────────────────"
# Total backups
TOTAL=$(ls $BACKUP_DIR/backup_${ENV}_*.sql.gz 2>/dev/null | wc -l)
echo "│ Total backups: $TOTAL"
# Total size
SIZE=$(du -sh $BACKUP_DIR 2>/dev/null | cut -f1)
echo "│ Total size: $SIZE"
# Último backup
LATEST=$(ls -t $BACKUP_DIR/backup_${ENV}_*.sql.gz 2>/dev/null | head -n 1)
if [ -n "$LATEST" ]; then
LATEST_NAME=$(basename "$LATEST")
LATEST_SIZE=$(du -h "$LATEST" | cut -f1)
LATEST_DATE=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M" "$LATEST" 2>/dev/null || stat -c "%y" "$LATEST" | cut -d'.' -f1)
echo "│ Latest: $LATEST_NAME"
echo "│ Size: $LATEST_SIZE"
echo "│ Date: $LATEST_DATE"
# Verificar metadata
META_FILE="${LATEST}.meta"
if [ -f "$META_FILE" ]; then
echo "│ ✅ Metadata exists"
else
echo "│ ⚠️ Metadata missing"
fi
else
echo "│ ⚠️ No backups found"
fi
# Último backup exitoso en logs
LAST_SUCCESS=$(grep "Backup completed: $ENV" /var/log/gunei-backups.log | tail -n 1)
if [ -n "$LAST_SUCCESS" ]; then
echo "│ Last success: $(echo $LAST_SUCCESS | cut -d']' -f1 | tr -d '[')"
fi
# Errores recientes
ERROR_COUNT=$(grep "$ENV" /var/log/gunei-backups.log | grep ERROR | tail -n 10 | wc -l)
if [ "$ERROR_COUNT" -gt 0 ]; then
echo "│ ⚠️ Recent errors: $ERROR_COUNT"
else
echo "│ ✅ No recent errors"
fi
echo "└────────────────────────────────────────"
echo ""
done
# Disk space
echo "┌─ Disk Space ───────────────────────────"
DISK_USAGE=$(df -h /opt/infrastructure/postgres/backups | awk 'NR==2 {print $5}' | tr -d '%')
echo "│ Backup partition: $DISK_USAGE% used"
if [ "$DISK_USAGE" -gt 80 ]; then
echo "│ ⚠️ WARNING: High disk usage"
elif [ "$DISK_USAGE" -gt 90 ]; then
echo "│ 🚨 CRITICAL: Disk almost full"
else
echo "│ ✅ Disk usage healthy"
fi
echo "└────────────────────────────────────────"
EOF
chmod +x /root/scripts/backup-dashboard.sh
# Ejecutar
/root/scripts/backup-dashboard.sh
🚨 Troubleshooting
Backup Falla en un Ambiente Específico
Síntoma: Backup de staging OK pero production falla (o viceversa)
Diagnóstico:
# Ver logs filtrados por ambiente
grep "production" /var/log/gunei-backups.log | tail -n 20
grep "staging" /var/log/gunei-backups.log | tail -n 20
# Verificar que la database existe
docker exec postgres-shared psql -U postgres -c "\l" | grep gunei_erp
# Test de conexión por ambiente
docker exec postgres-shared pg_isready -U gunei_staging_user -d gunei_erp_staging
docker exec postgres-shared pg_isready -U gunei_prod_user -d gunei_erp_production
Solución:
# Si el usuario no tiene permisos
docker exec postgres-shared psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gunei_erp_staging TO gunei_staging_user;"
docker exec postgres-shared psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE gunei_erp_production TO gunei_prod_user;"
# Re-intentar backup del ambiente específico
/root/scripts/backup-db-staging.sh
# o
/root/scripts/backup-db-production.sh
Disco Lleno por Backups de Ambos Ambientes
Síntoma:
[ERROR] pg_dump failed: No space left on device
Diagnóstico:
# Ver uso de disco por ambiente
du -sh /opt/infrastructure/postgres/backups/staging/
du -sh /opt/infrastructure/postgres/backups/production/
# Ver uso total
du -sh /opt/infrastructure/postgres/backups/
Solución:
# 1. Limpiar backups de ambos ambientes
/root/scripts/cleanup-backups.sh --force
# 2. Si sigue sin espacio, limpiar más agresivamente (solo staging por ejemplo)
find /opt/infrastructure/postgres/backups/staging/ -name "*.gz" -mtime +45 -delete
# 3. Re-intentar backup
/root/scripts/backup-postgres.sh
Backup de Staging OK pero Verificación Falla
Síntoma:
[ERROR] Backup verification FAILED
[ERROR] Checksum mismatch (staging)
Solución:
# 1. Re-ejecutar backup de staging inmediatamente
/root/scripts/backup-db-staging.sh
# 2. Verificar nuevo backup
/root/scripts/verify-backup.sh /opt/infrastructure/postgres/backups/staging/backup_staging_$(date +%Y%m%d)_*.sql.gz staging
# 3. Si persiste, puede ser problema de disco
smartctl -a /dev/sda
Restore Falla - "role does not exist"
Síntoma:
[ERROR] role "gunei_staging_user" does not exist
Solución:
# Recrear usuario
docker exec postgres-shared psql -U postgres << EOF
CREATE USER gunei_staging_user WITH PASSWORD 'staging_password';
GRANT ALL PRIVILEGES ON DATABASE gunei_erp_staging TO gunei_staging_user;
EOF
# O para production
docker exec postgres-shared psql -U postgres << EOF
CREATE USER gunei_prod_user WITH PASSWORD 'production_password';
GRANT ALL PRIVILEGES ON DATABASE gunei_erp_production TO gunei_prod_user;
EOF
# Re-intentar restore
/root/scripts/restore-backup.sh <backup-file> <environment>
Cron No Ejecuta Backups
Síntoma: No hay backups nuevos de ningún ambiente en días
Diagnóstico:
# 1. Cron está corriendo?
systemctl status cron
# 2. Crontab está configurado?
crontab -l | grep backup
# 3. Scripts son ejecutables?
ls -la /root/scripts/backup-postgres.sh
# 4. Logs de cron
grep CRON /var/log/syslog | grep backup
# 5. Permisos del directorio de backups
ls -la /opt/infrastructure/postgres/backups/
Solución:
# Asegurar permisos de scripts
chmod +x /root/scripts/*.sh
# Asegurar permisos de directorios
chmod 755 /opt/infrastructure/postgres/backups/
chmod 755 /opt/infrastructure/postgres/backups/staging/
chmod 755 /opt/infrastructure/postgres/backups/production/
# Test manual
/root/scripts/backup-postgres.sh
# Si funciona manual pero no en cron, agregar PATH
crontab -e
# Primera línea:
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
Backup Muy Lento en Production
Síntoma: Backup de production tarda > 30 minutos, staging OK
Causas posibles:
- Database production más grande
- Más tráfico durante backup
- Locks en tablas
Solución:
# 1. Ver tamaño de cada database
docker exec postgres-shared psql -U postgres -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database ORDER BY pg_database_size(pg_database.datname) DESC;"
# 2. Ajustar horario si hay tráfico
# Mover backup de production a horario de menor carga
crontab -e
# Cambiar a 3 AM solo para production:
0 2 * * * /root/scripts/backup-db-staging.sh >> /var/log/gunei-backups.log 2>&1
0 3 * * * /root/scripts/backup-db-production.sh >> /var/log/gunei-backups.log 2>&1
# 3. Excluir tablas grandes de logs (si existen)
# Editar backup-postgres.sh para agregar --exclude-table
🔐 Seguridad de Backups
Permisos por Ambiente
# Directorio raíz
chmod 755 /opt/infrastructure/postgres/backups/
# Directorios de ambientes
chmod 755 /opt/infrastructure/postgres/backups/staging/
chmod 755 /opt/infrastructure/postgres/backups/production/
# Backups (solo root puede leer/escribir)
find /opt/infrastructure/postgres/backups/ -name "*.gz" -exec chmod 640 {} \;
find /opt/infrastructure/postgres/backups/ -name "*.meta" -exec chmod 640 {} \;
chown -R root:postgres /opt/infrastructure/postgres/backups/
📈 Mejoras Futuras
Corto Plazo
- [ ] Notificaciones Discord para backups fallidos por ambiente
- [ ] Dashboard web de backups (listar por ambiente, descargar)
- [ ] Backup diferencial por ambiente
Mediano Plazo
- [ ] Encriptación GPG de backups
- [ ] Replicación a S3 o storage remoto (por ambiente)
- [ ] Point-in-time recovery (PITR) por ambiente
- [ ] Backup de archivos (uploads, certs)
Largo Plazo
- [ ] Replicación continua (streaming replication) de production
- [ ] Multi-site backups (geo-redundancia)
- [ ] Automated DR testing por ambiente
- [ ] Backup compliance reporting
📚 Referencias
Scripts Relacionados
/root/scripts/backup-postgres.sh: Backup principal/root/scripts/backup-db-staging.sh: Backup staging/root/scripts/backup-db-production.sh: Backup production/root/scripts/restore-backup.sh: Restore por ambiente/root/scripts/verify-backup.sh: Verificación/root/scripts/cleanup-backups.sh: Limpieza con retención/root/scripts/check-disk-space.sh: Monitoreo de disco/root/scripts/backup-stats.sh: Estadísticas por ambiente/root/scripts/backup-dashboard.sh: Dashboard consolidado
PostgreSQL Shared
- Contenedor:
postgres-shared - Puerto: 5433 (externo), 5432 (interno)
- Ubicación:
/opt/infrastructure/postgres/ - Databases:
gunei_erp_staging(usuario:gunei_staging_user)gunei_erp_production(usuario:gunei_prod_user)
Última actualización: 14 Enero 2026 Versión: 2.1.0 Responsable: Mikle SLA: RPO 24 horas, RTO 15 minutos (por ambiente)
Cambios en v2.1:
- ✅ Quick Reference con comandos más usados
- ✅ Conexión a PostgreSQL desde host (puerto 5433) y container (5432)
- ✅ Tabla de ubicaciones clave
- ✅ Timezone Argentina (TZ=America/Argentina/Buenos_Aires)
- ✅ Nomenclatura de backups con hora Argentina
Cambios en v2.0:
- ✅ Soporte para PostgreSQL Shared
- ✅ Backup separado por ambiente (staging + production)
- ✅ Scripts actualizados para multi-ambiente
- ✅ Restore con especificación de ambiente
- ✅ Políticas de retención independientes por ambiente
- ✅ Monitoreo y troubleshooting por ambiente
- ✅ Dashboard consolidado de ambos ambientes