Skip to content

Failover playbook

Last updated: 2026-07-12

Qué hacer cuando un host del cluster muere. Documento de emergencia: pensado para leer rápido cuando algo está mal, no para mantenimiento normal.

Antes de leer: si la VM o LXC solo está en estado raro (frozen, OOM, kernel panic), prueba primero qm reset / qm reboot / pct reboot desde el otro nodo. Esto aplica solo si el guest está irrecuperable o el host físico está caído.

Inventario crítico

Reparto real de guests (verificado 2026-07-12 con pct list/qm list):

  • proxmox-50 (el nodo "gordo", Ryzen): VM 171, VM 208, LXC 100, 102, 110, 172, 186 (PBS), 251, 270 (caddy-primary), 280 (rp-server), 281 (wp-pulse).
  • proxmox2-51 (el nodo pequeño, N5095): LXC 123 (cf), 155 (wg), 200 (n8n), 271 (caddy-secondary). Solo 4 guests.
Guest Host actual RAM Replica Backup PBS Notas
VM 171 ha (HomeAssistant) proxmox-50 6 GB → proxmox2 cada 15min 03:00 daily, keep 7 USB BT pinneado a proxmox-50 — failover a pmx2 requiere editar config para quitar usb0
VM 208 ubuntu-media-server proxmox-50 12 GB ❌ no replica 03:00 daily, keep 7 Heavy lifter — Loki, Grafana, Alertmanager, media stack, PocketID (Caddy NO está aquí — vive en 270/271)
LXC 100 Vera/clawdbot proxmox-50 4 GB ❌ no replica 04:30 daily, keep 7 Bot Telegram (Vera) + relay de alertas backup-freshness. Sin él no salen avisos a Telegram vía CT100
LXC 186 pbs proxmox-50 2 GB ❌ no replica (es el destino) ❌ N/A Backup destination. Ojo: si cae proxmox-50, cae PBS → no se puede restaurar de PBS hasta recuperarlo
LXC 270 caddy-primary proxmox-50 ❌ no replica 05:00 daily, keep 7 CRÍTICO — edge caddy primario (VIP keepalived .250)
LXC 280 rp-server proxmox-50 ❌ no replica 05:00 daily, keep 7 CRÍTICO — reverse proxy / remote-pulse
LXC 281 wp-pulse proxmox-50 ❌ no replica 02:00 daily, keep 7 wp-pulse control plane (/srv/wp-pulse/sites excluido, re-pullable)
LXC 102 iarq-bkp proxmox-50 ❌ no replica 05:30 daily, keep 7 misc CT (.113)
LXC 110 tv-gw proxmox-50 ❌ no replica 05:30 daily, keep 7 misc CT (.210)
LXC 172 ha-ml proxmox-50 ❌ no replica 05:30 daily, keep 7 misc CT
LXC 251 rag proxmox-50 ❌ no replica 05:30 daily, keep 7 misc CT (.188)
LXC 123 cf proxmox2-51 256 MB → proxmox cada 15min 01:00 daily, keep 7 Cloudflare tunnel daemon — sin él no hay ingress externo
LXC 155 wg proxmox2-51 256 MB → proxmox cada 15min 01:00 daily, keep 7 Wireguard
LXC 200 n8n proxmox2-51 1 GB ❌ no replica 02:00 daily, keep 7 Alert hub legacy (ver alerting-flow.md)
LXC 271 caddy-secondary proxmox2-51 ❌ no replica 05:00 daily, keep 7 CRÍTICO — edge caddy secundario (VIP failover)

Decomisados (ya no existen): LXC 253 (era clawdbot → migrado a CT100) y LXC 249 (beszel).

Escenario A — proxmox-50 down (el grave)

Impacto inmediato: cae CASI TODO — VM 171 (HA), VM 208 (Loki/Grafana/Alertmanager/media/PocketID), Vera/CT100 (relay de alertas), PBS/CT186, caddy-primary (270), rp-server (280), wp-pulse (281) y los misc CTs (102/110/172/251). Sobreviven solo en proxmox2-51: cf (123), wg (155), n8n (200) y caddy-secondary (271).

Buenas noticias: el VIP keepalived .250 debería hacer failover a caddy-secondary (271) + cloudflared (123) sigue vivo → el ingress externo probablemente sobrevive (aunque sin upstreams sanos hasta restaurar VM208). Mala noticia clave: PBS (CT186) está caído → no se puede restaurar de PBS hasta recuperarlo. Y sin Vera/CT100 no salen avisos a Telegram.

Pasos (SSH a [email protected]):

  1. Recuperar PBS primero (bloquea todo lo demás): PBS (CT186) vivía en proxmox-50. Sus chunks están en el terramaster NFS (intacto). Redesplegar un PBS temporal en proxmox2 (o arrancar una réplica) apuntando al datastore existente en el NAS, o montar el NAS y restaurar con proxmox-backup-client. Sin PBS accesible, los pct restore/qmrestore de abajo no funcionan.
  2. HA — failover manual (USB no estará disponible):
    sed -i '/^usb0:/d' /etc/pve/qemu-server/171.conf   # quitar USB
    # (la replicación ya dejó el disco en proxmox2; mover ownership)
    mv /etc/pve/nodes/proxmox/qemu-server/171.conf /etc/pve/nodes/proxmox2/qemu-server/171.conf 2>/dev/null
    qm start 171
    
    HA arranca sin Bluetooth (presence detection fallará). Aceptable temporalmente. (171 SÍ tiene réplica → arranque rápido, no necesita PBS.)
  3. VM 208 (sin réplica) — restore desde PBS a proxmox2 (requiere PBS del paso 1):
    qmrestore pbs:backup/vm/208/<TIMESTAMP> 208 --storage zfs-ha
    qm start 208
    
    Tarda ~20-25 min con 140 GB. Recupera Loki/Grafana/Alertmanager/media/PocketID.
  4. Vera/CT100, wp-pulse/281, misc CTs — restore desde PBS según prioridad (CT100 primero para recuperar alertas Telegram):
    for ID in 100 281 102 110 172 251; do
      pct restore $ID pbs:backup/ct/$ID/<TIMESTAMP> --storage zfs-ha
      pct start $ID
    done
    
  5. Edge caddy-primary (270) + rp-server (280) — el 271 ya cubre el routing vía VIP; restaurar 270/280 para volver a HA:
    for ID in 270 280; do
      pct restore $ID pbs:backup/ct/$ID/<TIMESTAMP> --storage zfs-ha
      pct start $ID
    done
    
  6. Verificar rutas: pct exec 271 -- caddy validate --config /etc/caddy/Caddyfile y re-correr homelab-ctl.py sync --yes (desde VM208, ya restaurada) para reapuntar ingress.

Tiempo estimado: HA en <5 min (réplica); pero VM208/servicios dependen de recuperar PBS primero → realistamente 30-60 min hasta tener el grueso operativo.

Escenario B — proxmox2-51 down (el leve)

Impacto inmediato: caen solo cf (123), wg (155), n8n (200) y caddy-secondary (271). Todo lo demás sigue vivo en proxmox-50: HA, media, Vera/CT100, PBS, caddy-primary (270), rp-server (280), wp-pulse (281) y los misc CTs.

  • Routing entrante: caddy-primary (270) sigue vivo → el VIP .250 se mantiene/failover a 270. Pero cloudflared (123) cae → sin él no hay ingress externo hasta restaurarlo. El acceso LAN directo sigue.
  • PBS sigue vivo → los restores son fáciles.

Pasos (SSH a [email protected]):

  1. Prioridad — cloudflared (123): sin él no entra tráfico de Cloudflare. Está replicado → arranque rápido:
    for ID in 123 155; do   # cf + wg (ambos replicados)
      mv /etc/pve/nodes/proxmox2/lxc/$ID.conf /etc/pve/nodes/proxmox/lxc/$ID.conf
      pct start $ID
    done
    
  2. caddy-secondary (271) — restore desde PBS (sin réplica). No urge si 270 ya sirve el VIP, pero recupera la HA del edge:
    pct restore 271 pbs:backup/ct/271/<TIMESTAMP> --storage zfs-ha
    pct start 271
    
  3. n8n (200) — restore desde PBS (flujo de alertas legacy, baja prioridad):
    pct restore 200 pbs:backup/ct/200/<TIMESTAMP> --storage zfs-ha
    pct start 200
    
  4. Verificar: curl -sI https://finanzas.monxas.casa/ (a través de CF → cloudflared → VIP → caddy) y homelab-ctl.py sync --yes si cambió alguna IP.

Nota: Vera/CT100, caddy-primary (270), rp-server (280), wp-pulse (281) y los misc CTs no se caen en este escenario (viven en proxmox-50) — no requieren acción.

Tiempo estimado: <5 min para cf/wg (replicados), +10 min cada restore desde PBS (271, 200).

Drill no destructivo (validado 2026-04-26)

Para verificar que un backup PBS es restaurable sin tocar producción:

# Pick free VMID (verifica con qm/pct status NNN)
DRILL=999

# Listar backups disponibles (LXCs)
pvesm list pbs | grep ct/200

# Restore a VMID temporal con red aislada (IP libre, NO el VIP .250)
pct restore $DRILL pbs:backup/ct/200/<TIMESTAMP> \
  --storage zfs-ha --hostname drill-temp --unprivileged 1 \
  --memory 1024 --cores 2 --onboot 0 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.0.249/24,gw=192.168.0.1 \
  --tags drill-temp

pct start $DRILL
sleep 5

# Verificar servicio + DB integrity
pct exec $DRILL -- systemctl is-active n8n
curl -s http://192.168.0.249:5678/healthz
pct exec $DRILL -- sqlite3 /root/.n8n/database.sqlite \
  'SELECT count(*) FROM workflow_entity;'

# Cleanup
pct stop $DRILL && pct destroy $DRILL

Para VMs (qmrestore en lugar de pct restore — usa el volid pbs:backup/..., no una ruta de filesystem):

qmrestore pbs:backup/vm/171/<TIMESTAMP> $DRILL --storage zfs-ha
qm set $DRILL --net0 virtio=...,bridge=vmbr0   # IP no conflict
qm set $DRILL --delete usb0                    # quitar USB pinning
qm start $DRILL
# verificar luego destroy

Frecuencia automatizada: cron en proxmox-50 (/etc/cron.d/pbs-drill). LXC random mensual día 1 a 05:05, VM random trimestral día 15 a 05:05 (Ene/Abr/Jul/Oct). Detalles en scripts/pbs-drill.md. Drill manual también disponible (procedimiento arriba).

Recovery time objectives (RTO actuales)

Pérdida RTO realista Notas
LXC replicado (123/155/171) <5 min mv config + start, no necesita PBS
LXC no replicado 10-15 min restore PBS
Edge caddy (270 o 271) El otro cubre vía VIP; restore 10-15 min keepalived VIP .250 failover automático
VM 171 (HA) sin USB <5 min failover, perdiendo BT
VM 208 entera 25-30 min restore 140 GB desde PBS
proxmox-50 down (Escenario A) 30-60 min PBS cae con él → recuperar PBS primero
Cluster entero (ambos hosts) horas redesplegar PBS desde cero + secuencial

Lo que SÍ duele si pasa

  1. Si terramaster (PBS storage) muere: pierdes TODOS los backups históricos. PBS solo guarda metadata, los chunks viven en el NAS. Mitigación: copia mensual offline del NAS a disco externo. Esta copia mensual NO está implementada hoy — añadir al backlog si se vuelve crítico.

  2. Si Movistar router muere: sin DNS local, sin internet. Wireguard sigue para acceso interno-a-interno solo si el WG endpoint está en otra red. Mitigación: tener un spare router pre-configurado.

  3. Si proxmox-50 muere (Escenario A): cae PBS con él. Los chunks en el terramaster NFS sobreviven, pero hay que redesplegar/arrancar PBS (temporal en proxmox2 o restaurar CT186) apuntando al datastore existente antes de poder restaurar nada más. Es la dependencia crítica del escenario.