Runbook: backup y restore de qrsgen¶
Procedimiento para backup completo del estado de qrsgen y restore en un nuevo VPS o tras corrupción del Postgres. Cubre las tablas propias del bridge + el sqlstore de whatsmeow (que contiene las sesiones — sin esto pierdes todas las sesiones paireadas y hay que re-escanear QR).
Este runbook NO cubre el backup de Chatwoot. Para eso ver la documentación de Chatwoot upstream. Estos dos servicios son independientes — restore de qrsgen no requiere que Chatwoot esté en el mismo estado, sólo que sea alcanzable y que los
inbox_idreferenciados sigan existiendo.
Qué hay que respaldar¶
1. Tablas Postgres del bridge¶
| Tabla | Contenido | Coste perderla |
|---|---|---|
bridge_instance |
Instancias + JIDs + owner_tags + webhook URLs | Alto — pierdes el mapeo |
bridge_msg_history |
Tracker de retroactive name update + replyto outgoing | Medio — retroactive update parará durante el TTL |
bridge_chat_anchor |
Anchors per-chat para on-demand history sync | Bajo — se reconstruye automáticamente |
bridge_outgoing_queue |
Outbox de mensajes en cola | Alto si hay msgs pendientes |
bridge_dedup |
Hashes per-instancia de los últimos N msgs | Bajo — ventana de minutos |
bridge_spamguard |
Estado del detector spam outgoing | Medio — historial perdido |
bridge_audit_log |
Log inmutable de ops admin | Alto si lo necesitas legal/forense |
bridge_tenant |
Config multi-tenant (downstream URLs/tokens) | Alto — pierdes mapeo cliente |
bridge_usage_* |
Counters per-instancia per-día para billing | Alto si facturas por uso |
2. Tablas Postgres de whatsmeow (sesiones)¶
whatsmeow guarda en el mismo Postgres su sqlstore.Container:
whatsmeow_device
whatsmeow_identity_keys
whatsmeow_pre_keys
whatsmeow_sessions
whatsmeow_sender_keys
whatsmeow_app_state_*
whatsmeow_contacts
whatsmeow_message_secrets
whatsmeow_chat_settings
whatsmeow_lid_map
Crítico: sin estas tablas hay que re-escanear QR de cada instancia y se pierde el historial de pushnames + LID mappings.
3. Imagen Docker¶
El binario qrsgen está versionado por tag. No necesitas backup; lo
re-pulls de ghcr.io/rricajos/qrsgen:X.Y.Z. Backup tu .env del
deploy (variables como QRSGEN_API_TOKEN, DOWNSTREAM_*,
CHATWOOT_DB_URL, etc.).
Backup procedure¶
Backup completo (recomendado)¶
pg_dump de la database bridge entera. Captura las tablas qrsgen +
whatsmeow en una transacción consistente:
# Variables
POSTGRES_HOST=postgres # alias overlay del swarm
POSTGRES_USER=postgres
POSTGRES_DB=bridge
PGPASSWORD=$(sudo docker service inspect qrsgen_qrsgen \
--format '{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' \
| grep POSTGRES_PASSWORD | cut -d= -f2-)
# Dump
sudo docker exec -e PGPASSWORD="$PGPASSWORD" \
$(sudo docker ps -q -f name=postgres | head -1) \
pg_dump -U $POSTGRES_USER -d $POSTGRES_DB \
--format=custom --no-owner --no-acl \
> /var/backups/qrsgen-$(date +%Y%m%d-%H%M%S).dump
# Verifica tamaño + integridad mínima
ls -lh /var/backups/qrsgen-*.dump | tail -1
sudo docker exec $(sudo docker ps -q -f name=postgres | head -1) \
pg_restore -l /var/backups/qrsgen-$(date +%Y%m%d-%H%M%S).dump | head -20
El formato custom (.dump) es comprimido + paralelizable en restore.
Para una DB de ~1GB el dump ronda 100-200MB.
Schedule recomendado¶
- Diario: dump completo, retención 7 días.
- Semanal: copia del dump del lunes a almacenamiento off-site (S3, B2, otro VPS).
- Pre-deploy: dump antes de cada
docker service updatede qrsgen — permite rollback total si una migración rompe schema.
Ejemplo de cron mínimo:
# /etc/cron.d/qrsgen-backup
0 3 * * * root /opt/qrsgen/ops/backup/run.sh daily
0 4 * * 1 root /opt/qrsgen/ops/backup/run.sh weekly-offsite
(El repo incluye ops/backup/ con scripts más completos — adapta
según tu storage.)
Restore procedure¶
Caso A: Restaurar en mismo Postgres tras corrupción de la DB qrsgen¶
- Para qrsgen para que no escriba durante el restore:
- Drop la database actual (cuidado):
PGPASSWORD=$PGPASS sudo docker exec -i $(sudo docker ps -q -f name=postgres | head -1) \
psql -U postgres -d postgres -c "DROP DATABASE bridge WITH (FORCE);"
PGPASSWORD=$PGPASS sudo docker exec -i $(sudo docker ps -q -f name=postgres | head -1) \
psql -U postgres -d postgres -c "CREATE DATABASE bridge;"
- Restore del dump:
PGPASSWORD=$PGPASS sudo docker exec -i $(sudo docker ps -q -f name=postgres | head -1) \
pg_restore -U postgres -d bridge --no-owner --no-acl \
< /var/backups/qrsgen-YYYYMMDD-HHMMSS.dump
- Re-arranca qrsgen:
- Verifica:
sudo docker service logs qrsgen_qrsgen --since 2m | grep -i "qrsgen ready"
curl -sS -H "Authorization: Bearer $TOK" http://qrsgen:3100/api/instances | jq '.[].state'
Cada instancia debería pasar connected en <30s sin necesidad
de re-escanear QR (porque las sesiones whatsmeow restauradas
incluyen los identity keys).
Caso B: Restaurar en VPS nuevo (DR completo)¶
- Provisiona el nuevo VPS con Docker Swarm + Postgres + Chatwoot.
- Carga el dump en el nuevo Postgres (mismo procedimiento que Caso A, paso 3).
- Despliega qrsgen con el mismo
.env(mismas credentials, webhook URLs, etc.). - Si Chatwoot cambia de host (nuevo dominio), actualiza:
DOWNSTREAM_BASE_URLen el.envdel stack qrsgenwebhook_urlen cadachannel_apirow de Chatwoot — SQL directo en la DB de Chatwoot
Caso C: Restaurar solo bridge_* (no whatsmeow)¶
Si quieres restaurar el estado del bridge pero forzar re-pareo de todas las instancias (porque sospechas que las sesiones están quemadas por algún incident), restaura selectivo:
# Restore solo las tablas bridge_*, excluye whatsmeow_*
pg_restore -l backup.dump | grep "bridge_" | pg_restore --use-list=- \
-U postgres -d bridge < backup.dump
Tras esto, cada instancia mostrará state=qr_pending al arrancar y
necesitarás escanear el QR (desde Chatwoot conv QR - X con el
flujo OMNIA_QR_CHAT qr command).
Verificación post-restore¶
Checklist:
- [ ]
/api/healthdevuelve 200 coninstances_connected == instances_total. - [ ] Cada conv
QR - Xen Chatwoot recibe el pill🟢 QRsGEN vX operativotrasbackend_started. - [ ] Un mensaje de prueba (curl outgoing o agente escribiendo desde Chatwoot) llega al móvil destino.
- [ ] Tablas
bridge_audit_logtiene rows recientes (verifica que los triggers anti-UPDATE/DELETE están reactivados):
-- Esto DEBE fallar tras el restore con un mensaje del trigger:
UPDATE bridge_audit_log SET actor='test' WHERE id=1;
Si el UPDATE pasa sin error, los triggers no se restauraron correctamente. Re-aplica el schema:
Notas de capacidad¶
- DB
bridgetípicamente crece ~10-50MB/día con 4-5 instancias activas (history import puntual añade picos). bridge_audit_loges append-only — sin retención automática crece linealmente. Considera particionado por mes si superas 100k rows (postpuesto a v0.65+ por ahora).bridge_outgoing_queuese auto-purga (TTL 5 min) — no debería bloatear nunca.whatsmeow_*crece con cada msg recibido y con cada nuevo contacto/grupo. ~1GB tras 6 meses de operación con 5 instancias conversando activamente.
Última actualización: v0.64.6 (2026-06-01)