16 — Backup y restore¶
Qué hay que respaldar¶
| Componente | Crítico | Frecuencia recomendada |
|---|---|---|
| Base de datos PostgreSQL | ✅ | Diario |
backend_django/media/ (uploads) |
✅ | Diario |
.env (con secretos) |
✅ | Cada cambio |
Código fuente (apps/, backend_django/api/, etc.) |
⚠️ | Vive en git; backup git suficiente |
Builds (apps/*/dist/, static/v3/dist/) |
❌ | Se regeneran con npm run build |
node_modules/, venv/ |
❌ | Se regeneran con npm install / pip install |
Backup de PostgreSQL¶
Backup completo (custom format — recomendado)¶
sudo -u postgres pg_dump \
-d blimx_saas \
-F c \
-Z 9 \
-f /var/backups/blimx/db-$(date +%Y%m%d-%H%M%S).dump
-F c→ custom format (comprimido, restaurable selectivamente).-Z 9→ compresión máxima.- Tamaño típico: 5-50 MB según volumen de FAQs.
Backup en SQL plano (legible, restore en cualquier Postgres)¶
sudo -u postgres pg_dump \
-d blimx_saas \
--no-owner --no-privileges \
-f /var/backups/blimx/db-$(date +%Y%m%d-%H%M%S).sql
Cron diario¶
Añade:
# Backup diario a las 3:00 AM, retención 30 días
0 3 * * * pg_dump -d blimx_saas -F c -Z 9 -f /var/backups/blimx/db-$(date +\%Y\%m\%d).dump && find /var/backups/blimx/ -name 'db-*.dump' -mtime +30 -delete
Replica/streaming¶
Para mayor resiliencia, configura una replica streaming a otro servidor:
Detalle: https://www.postgresql.org/docs/current/warm-standby.html.
Restore de PostgreSQL¶
Desde custom format¶
# Restore en una BD existente (limpia primero las tablas que se restauran)
sudo -u postgres pg_restore \
-d blimx_saas \
--clean --if-exists \
/var/backups/blimx/db-20260524.dump
# Restore en una BD nueva
sudo -u postgres createdb blimx_saas_restore
sudo -u postgres pg_restore \
-d blimx_saas_restore \
/var/backups/blimx/db-20260524.dump
Desde SQL plano¶
Restore parcial (tabla específica)¶
# Lista las tablas del dump
pg_restore -l /var/backups/blimx/db-20260524.dump | head -40
# Restaura solo una tabla (id en el listing)
pg_restore -L <(pg_restore -l backup.dump | grep "TABLE DATA public bots") \
-d blimx_saas backup.dump
Backup de uploads (media/)¶
# rsync incremental — solo copia archivos cambiados
rsync -av --delete \
/home/blimxapp/saas/chatbot-v3-workspace/backend_django/media/ \
/var/backups/blimx/media/
Cron diario:
30 3 * * * rsync -av --delete /home/blimxapp/saas/chatbot-v3-workspace/backend_django/media/ /var/backups/blimx/media/
Backup de .env¶
sudo cp /home/blimxapp/saas/chatbot-v3-workspace/.env /var/backups/blimx/env-$(date +%Y%m%d).bak
sudo chmod 600 /var/backups/blimx/env-*.bak
Cuidado con dónde lo respaldas. Contiene
STRIPE_SECRET_KEY,SECRET_KEY, password de la BD. Si lo subes a S3 / Glacier, cifra con KMS o gpg.
Backup off-site (recomendado)¶
Sube los backups diarios a un bucket externo. Opciones:
Opción 1: AWS S3¶
aws s3 cp /var/backups/blimx/db-$(date +%Y%m%d).dump s3://my-blimx-backups/$(date +%Y/%m)/ \
--storage-class GLACIER_IR \
--sse aws:kms --sse-kms-key-id alias/blimx-backups
Opción 2: rclone (cualquier proveedor)¶
rclone copy /var/backups/blimx/ remote:blimx-backups/$(date +%Y/%m/%d)/ \
--transfers 4 --check-first
Drill de restore¶
Una vez al mes, restaura el backup más reciente en una BD de testing y corre:
sudo -u postgres pg_restore -d blimx_test /var/backups/blimx/db-latest.dump
# Verifica integridad
psql blimx_test -c "SELECT COUNT(*) FROM bots; SELECT COUNT(*) FROM knowledge_base;"
psql blimx_test -c "SELECT MAX(created_at) FROM accounts;"
# Verifica que las FKs no estén rotas
psql blimx_test -c "
SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';
"
Si todo OK, drop la BD de test:
RTO / RPO recomendados¶
| Métrica | Valor recomendado | Cómo lograrlo |
|---|---|---|
| RPO (Recovery Point Objective) | 24h | Backup diario |
| RPO con replica | < 1 min | Streaming replication |
| RTO (Recovery Time Objective) | < 30 min | Backup en mismo data center + script de restore probado |
| RTO con failover | < 5 min | Replica + DNS auto-failover |
Disaster recovery checklist¶
Si el servidor primary se pierde por completo:
- Provisiona un servidor nuevo con los prereqs de
03-prerrequisitos.md. - Restaura el código:
git cloneo sube el zip más reciente. - Restaura
.envdesde tu backup off-site. - Restaura la BD:
pg_restoredel último dump. - Restaura
media/:rsyncdesde tu backup off-site. - Sigue
04-instalacion.mddesde el paso 4 (venv + deps + builds). - Sigue
05-deploy-produccion.md(systemd + nginx + SSL). - Repunta los DNS del dominio al servidor nuevo.
- Reconfigura el webhook Stripe con la URL nueva (si cambió).
- Smoke test según
test/health_check.py.
Tiempo total con backups recientes: 30-60 minutos.
Lo que NO está cubierto por backups¶
- Stripe — Stripe es la fuente de verdad de pagos. Si tu BD se corrompe pero Stripe está ok, puedes re-sincronizar suscripciones e invoices con un script que llame a
stripe.Subscription.list(). - OAuth (Google) — los
id_tokenno se respaldan; los usuarios re-autorizan al loguearse. - JWT activos — al restaurar, los JWT siguen siendo válidos hasta su expiración (si guardas
SECRET_KEYigual). Si quieres invalidarlos a todos, cambiaSECRET_KEYy todos los tokens dejan de validar.