Problema

En muchos homelabs y pequeñas infraestructuras el objetivo es mantener varios servicios críticos (proxy inverso, DNS interno, autenticación, monitorización) siempre disponibles sin complicar la arquitectura. La combinación de un clúster Proxmox, contenedores LXC y una red plana suele generar puntos únicos de falla: la pérdida del nodo que aloja Traefik corta el acceso externo, la caída del servidor DNS rompe la resolución interna y la falta de sincronización de configuraciones genera divergencias que obligan a intervenciones manuales. El patrón recurrente es “servicios críticos sin alta disponibilidad ni replicación de configuración”, lo que lleva a interrupciones inesperadas y a una carga operativa innecesaria.

Causa

  1. Dependencia de un único nodo – Cuando Traefik, Keepalived o la base de datos de Authelia se ejecutan en un solo host, cualquier caída del hardware o del hypervisor deja el servicio inoperativo.
  2. Sincronización manual – Copiar archivos de configuración entre nodos a mano o mediante scripts ad‑hoc genera versiones desalineadas y errores de sintaxis que solo se detectan en producción.
  3. Falta de VIP gestionado – Usar una IP estática sin un mecanismo de conmutación (VRRP) impide que otro nodo asuma la dirección en caso de fallo.
  4. Red plana sin segmentación – Sin VLANs o subredes dedicadas, el tráfico de gestión y el de datos compiten por ancho de banda y pueden saturar el switch, provocando latencias que afectan a los servicios de monitorización.
  5. Encriptación parcial – Conexiones SSH sin claves dedicadas o sin restricciones de host pueden ser vulnerables a ataques de replay, especialmente cuando se usan para replicar configuraciones sensibles.

Solución

Una arquitectura basada en VIP + Keepalived, replicación de archivos con Lsyncd y contenedores LXC gestionados por Portainer cubre la mayoría de los escenarios. El flujo es:

  1. Crear una IP virtual (VIP) gestionada por Keepalived en al menos dos nodos del clúster. Keepalived usa VRRP para anunciar la VIP y realiza un failover automático mediante Gratuitous ARP.
  2. Instalar Lsyncd en los mismos nodos y configurar una sincronización unidireccional o bidireccional de los directorios de configuración (por ejemplo /etc/traefik, /etc/technitium, /var/lib/authelia). Lsyncd utiliza rsync bajo el hood y mantiene la consistencia casi en tiempo real.
  3. Ejecutar los servicios críticos dentro de contenedores LXC y montar los volúmenes sincronizados. Al iniciar el contenedor en cualquier nodo, leerá la configuración más reciente sin intervención manual.
  4. Mantener la red segmentada: crear una VLAN para tráfico de gestión (ej. 192.168.10.0/24) y otra para tráfico de datos (192.168.20.0/24). El Netgear switch soporta 802.1Q, lo que permite aislar el tráfico de Keepalived/SSH del tráfico de NFS y streaming.
  5. Asegurar la replicación: generar pares de claves ed25519 sin passphrase, restringir el acceso en authorized_keys a la IP del nodo vecino y usar StrictModes yes.
  6. Monitorear con Prometheus + Loki: exponer métricas de Keepalived (keepalived_exporter) y de Lsyncd (lsyncd_exporter) para detectar retrasos de sincronización o fallos de VIP.

Keepalived (VIP y VRRP)

Mantener la VIP en los nodos que ejecutan los contenedores críticos. La prioridad determina cuál es el master. Un ejemplo mínimo:

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 150   # nodo master = 200, backup = 150
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass secret123
    }
    virtual_ipaddress {
        192.168.1.250/24 dev eth0 label eth0:vip
    }
}

Lsyncd (replicación de configuración)

Configurar Lsyncd para sincronizar /etc/traefik y /etc/technitium desde el nodo master al backup:

settings {
    logfile    = "/var/log/lsyncd/lsyncd.log",
    statusFile = "/var/log/lsyncd/lsyncd.status",
    nodaemon   = false,
}
sync {
    default.rsync,
    source = "/etc/traefik",
    target = "[email protected]::/etc/traefik",
    rsync = {
        archive = true,
        compress = true,
        verbose = true,
        _extra = {"--delete"},
    }
}
sync {
    default.rsync,
    source = "/etc/technitium",
    target = "[email protected]::/etc/technitium",
    rsync = {
        archive = true,
        compress = true,
        verbose = true,
        _extra = {"--delete"},
    }
}

Contenedores LXC

Definir los contenedores con pct y montar los directorios sincronizados como volúmenes bind:

pct create 101 local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.gz \
    -hostname traefik -net0 name=eth0,bridge=vmbr0,ip=dhcp \
    -mp0 /etc/traefik,mp=/etc/traefik,backup=0 \
    -features nesting=1
pct start 101

Repetir para DNS, Authelia y demás servicios. Al iniciar en el nodo backup, la configuración ya está presente gracias a Lsyncd.

Cuándo aplicar esta solución

  • Síntomas: pérdida de acceso externo tras reinicio de un nodo, errores de resolución DNS internos, divergencia de archivos de configuración entre nodos.
  • Entorno: clúster Proxmox con al menos dos nodos, servicios críticos en contenedores LXC, red con capacidad VLAN.
  • No aplica: infraestructuras que ya usan soluciones de orquestación completas (Kubernetes, Docker Swarm) con controladores de ingreso nativos, o entornos donde la carga es tan baja que la complejidad de HA no justifica el esfuerzo.

Código

# Instalar Keepalived y Lsyncd en ambos nodos (Debian/Ubuntu)
apt update && apt install -y keepalived lsyncd

# Copiar configuraciones
scp keepalived.conf root@backup:/etc/keepalived/keepalived.conf
scp lsyncd.conf.lua root@backup:/etc/lsyncd/lsyncd.conf.lua

# Habilitar y arrancar servicios
systemctl enable keepalived && systemctl start keepalived
systemctl enable lsyncd && systemctl start lsyncd

# Verificar que la VIP está asignada
ip addr show dev eth0 | grep 192.168.1.250

Verificación

  1. VIP activa: ip a show dev eth0 debe listar 192.168.1.250.
  2. Failover: apagar el nodo master (systemctl stop keepalived o apagar la máquina). Después de 2‑3 segundos, la VIP debe aparecer en el backup (ip a en el nodo secundario).
  3. Sincronización: modificar /etc/traefik/traefik.yml en el master, esperar <5 s y comprobar que el mismo archivo se actualiza en el backup (diff entre los dos).
  4. Contenedores: reiniciar el contenedor LXC en el nodo backup y confirmar que Traefik arranca sin errores de configuración.
  5. Métricas: en Prometheus, buscar keepalived_state y lsyncd_sync_status. Los valores deben ser master/backup y up respectivamente.

Notas adicionales

  • Tiempo de convergencia: Keepalived usa advert_int 1, lo que permite un failover en ~2 s. Ajustar según tolerancia a interrupciones.
  • Rsync sobre SSH: la configuración anterior usa el modo daemon (::). Si prefieres SSH, cambia target a [email protected]:/etc/traefik y añade la opción ssh = { port = 22 } dentro del bloque rsync.
  • Backup de la VIP: en entornos con más de dos nodos, asigna prioridades diferentes para crear un árbol de failover (master → secondary → tertiary).
  • Seguridad de claves: revoca y regenera las claves ed25519 cada 6‑12 meses; usa ssh-keygen -t ed25519 -C "keepalived-sync" y distribúyelas con ssh-copy-id.
  • Logs: Lsyncd escribe en /var/log/lsyncd/lsyncd.log. Configura Loki para recoger este archivo y crear alertas cuando el número de errores supere un umbral.
  • Escalabilidad: si añades más nodos al clúster, simplemente replica la configuración de Keepalived y Lsyncd; no es necesario reescribir los contenedores LXC.

Con este patrón, la mayoría de los servicios críticos de un homelab quedan protegidos contra fallos de nodo, la configuración se mantiene coherente y la conmutación es prácticamente transparente para los usuarios.