Saltar a contenido

Apéndice E — Backup y disaster recovery

E.1. Backups diarios

/opt/tienda/scripts/backup_daily.sh:

#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%F)
BACKUP_DIR=/opt/tienda/backups
S3_BUCKET=s3://erp-backups-tuempresa

mkdir -p "$BACKUP_DIR/$DATE"

sudo -u postgres pg_dump -Fc tienda_db | gzip -9 > "$BACKUP_DIR/$DATE/tienda_db.dump.gz"
tar czf "$BACKUP_DIR/$DATE/media.tar.gz" /opt/tienda/media/
tar cz /opt/tienda/secrets /opt/tienda/.env | \
    gpg --batch --yes --symmetric --cipher-algo AES256 \
        --passphrase-file /root/.gpg_backup_pass \
        --output "$BACKUP_DIR/$DATE/secrets.tar.gz.gpg"
aws s3 sync "$BACKUP_DIR/$DATE/" "$S3_BUCKET/$DATE/" --storage-class STANDARD_IA
find "$BACKUP_DIR" -maxdepth 1 -type d -name "20*" -mtime +30 -exec rm -rf {} +

Cron: 0 2 * * * /opt/tienda/scripts/backup_daily.sh >> /var/log/backup.log 2>&1

Buena práctica: probar el restore 1 vez al mes en VPS de staging.

E.2. Procedimiento de restore

aws s3 sync s3://erp-backups-tuempresa/2026-05-14/ /tmp/restore/
systemctl stop tienda tienda-daphne tienda-celery tienda-celerybeat
sudo -u postgres dropdb tienda_db
sudo -u postgres createdb tienda_db -O tienda
gunzip -c /tmp/restore/tienda_db.dump.gz | sudo -u postgres pg_restore -d tienda_db
tar xzf /tmp/restore/media.tar.gz -C /
gpg --decrypt --passphrase-file /root/.gpg_backup_pass /tmp/restore/secrets.tar.gz.gpg | tar xz -C /
cd /opt/tienda && set -a && . .env && set +a && .venv/bin/python manage.py check
systemctl start postgresql redis-server tienda tienda-daphne tienda-celery tienda-celerybeat

RTO objetivo: 30 min. RPO objetivo: 24h (bajar a 1h con WAL archiving).

E.3. Réplica caliente Postgres (opcional)

# postgresql.conf primario
wal_level = replica
max_wal_senders = 3
archive_mode = on
archive_command = 'aws s3 cp %p s3://erp-wal-archive/%f'

Failover con pg_promote() o automático con Patroni (ver Apéndice M).