Problema

En muchos homelabs el entusiasmo por añadir hardware, VLANs y contenedores lleva a una arquitectura que funciona, pero que rápidamente se vuelve difícil de mantener. El patrón típico es:

  • Un servidor Proxmox que aloja varios LXCs no privilegiados.
  • Múltiples VLANs creadas en un switch gestionado para separar tráfico de gestión, laboratorio, invitados y dispositivos de confianza.
  • Un firewall virtual (Debian o similar) que actúa como puerta de enlace entre esas VLANs y el ISP.
  • Herramientas de orquestación caseras que intentan automatizar la creación de redes y contenedores.

El síntoma más común es que, tras añadir una nueva VLAN o LXC, aparecen problemas de conectividad inter‑VLAN, reglas de firewall que no se aplican o contenedores que pierden acceso a recursos críticos (DNS, NTP, almacenamiento). La raíz del problema suele estar en la falta de una guía estructurada que una la configuración de Proxmox, el switch gestionado y la orquestación de contenedores.

Causa

  1. Diseño de VLAN sin documentación
    Cuando se crean VLANs “sobre la marcha”, los IDs pueden solaparse con los de la red doméstica o con los rangos usados por los contenedores. Además, la asignación de puertos trunk vs access a menudo se hace de forma ad‑hoc, lo que genera loops o tráfico no etiquetado.

  2. Puentes de red (vmbr) mal alineados
    Proxmox permite crear puentes que pueden ser “tagged” o “untagged”. Si un puente está configurado como no tagged pero se conecta a un puerto trunk, los paquetes llegan sin la etiqueta VLAN esperada y el firewall no los reconoce.

  3. Contenedores LXC no privilegiados sin permisos de red adecuados
    Los LXCs sin privilegios dependen de la capa de red del host. Si el host no expone la VLAN correcta al contenedor, éste queda aislado o, peor, comparte la misma subred que otro contenedor, provocando colisiones de IP.

  4. Reglas de firewall dispersas
    Tener una regla de NAT en la VM firewall y otra regla de filtrado en el propio Proxmox puede crear excepciones inesperadas. Cuando se añaden rutas estáticas (por ejemplo, para Tailnet o AirVPN), la tabla de rutas del host se vuelve inconsistente.

  5. Orquestador casero sin idempotencia
    Scripts que crean puentes, asignan VLANs y despliegan LXCs sin comprobar el estado actual pueden volver a crear recursos, dejando interfaces “dangling” y duplicando configuraciones.

Solución

Adoptar un flujo de trabajo basado en tres capas claramente definidas:

  1. Definir la topología de VLAN en un único archivo declarativo
    Utiliza un formato sencillo (YAML o JSON) que describa cada VLAN: ID, nombre, subred, puertos del switch y puentes de Proxmox. Mantén este archivo bajo control de versiones.

  2. Configurar los puentes de Proxmox a partir de la definición

    • Crea un puente “trunk” que reciba todas las VLANs del switch.
    • Para cada VLAN, crea un puente “tagged” que apunte al trunk y añada la etiqueta correspondiente.
    • Asocia los puentes “tagged” a las VMs o contenedores que necesiten acceso a esa VLAN.
  3. Desplegar LXC con una plantilla que reciba la VLAN como parámetro

    • Usa pct create con la opción -netX para especificar el puente y la etiqueta.
    • Marca el contenedor como unprivileged y asigna un uidmap/gidmap que permita acceso a los dispositivos de red necesarios.
  4. Centralizar el firewall en una única VM

    • Configura todas las reglas NAT, filtrado y rutas en esa VM.
    • Desactiva el firewall de Proxmox para evitar solapamientos.
    • Exporta la tabla de rutas del firewall a los hosts mediante un script de arranque que actualice ip route.
  5. Automatizar con Ansible (o tu orquestador) usando módulos idempotentes

    • Un playbook que lea la definición de VLAN, aplique la configuración de puentes y despliegue los contenedores.
    • Incluye tareas de verificación (assert) para confirmar que cada puente tiene la etiqueta correcta y que cada LXC tiene la IP esperada.

Paso a paso resumido

  1. Archivo de topología (vlan.yml)

    vlans:
      - id: 10
        name: LAB_TRANSIT
        subnet: 10.10.0.0/16
        switch_ports: [1,2]   # trunk ports
      - id: 20
        name: LAB_INFRA
        subnet: 10.20.0.0/24
        switch_ports: [3]     # access
    
  2. Playbook Ansible (setup.yml)

    • Lee vlan.yml.
    • Crea vmbr0 (trunk) si no existe.
    • Por cada VLAN, crea vmbr{id} con bridge_ports: vmbr0 y bridge_vlan_aware: yes.
    • Usa pct create para lanzar contenedores con -net0 name=eth0,bridge=vmbr{id},tag={id}.
  3. Reglas de firewall en la VM

    • Permitir tráfico intra‑VLAN.
    • NAT para salida a internet desde VLANs de laboratorio.
    • Rutas estáticas para redes externas (Tailnet, AirVPN).

Cuándo aplicar esta solución

  • Escenarios con más de dos VLANs y la necesidad de aislar tráfico de gestión, laboratorio y dispositivos de invitados.
  • Entornos donde se usan contenedores LXC no privilegiados y se requiere que cada uno pertenezca a una subred distinta.
  • Cuando la configuración actual está dispersa entre scripts manuales, la UI de Proxmox y la consola del switch, y los cambios frecuentes generan inconsistencias.

No es necesario si tu homelab consta de un solo segmento de red y pocos contenedores; la sobre‑ingeniería puede ser más costosa que útil.

Código

# Crear puente trunk (vmbr0) si no existe
pvesh create /nodes/$(hostname)/network/interfaces -iface vmbr0 -type bridge -bridge_ports eth0 -autostart 1

# Crear puente VLAN 10 (vmbr10) sobre el trunk
pvesh create /nodes/$(hostname)/network/interfaces -iface vmbr10 -type bridge -bridge_ports vmbr0 -bridge_vlan_aware 1 -autostart 1

# Lanzar un LXC no privilegiado en VLAN 10
pct create 101 local:vztmpl/debian-12-standard_12.0-1_amd64.tar.gz \
  -hostname infra01 -net0 name=eth0,bridge=vmbr10,tag=10,ip=10.10.1.10/16 \
  -unprivileged 1 -features nesting=1 \
  -memory 2048 -swap 512 -rootfs local-lvm:8

Verificación

  1. Puentes

    ip -d link show vmbr10 | grep vlan
    

    Debería mostrar vlan protocol 802.1Q id 10.

  2. Conectividad LXC

    pct exec 101 -- ping -c 3 10.10.1.1   # ping a la puerta del firewall
    pct exec 101 -- ip a
    

    La interfaz eth0 debe tener la IP 10.10.1.10/16 y la etiqueta VLAN 10.

  3. Reglas de firewall
    En la VM firewall:

    iptables -L -n -v | grep 10.10
    

    Verifica que exista la regla de NAT para la subred 10.10.0.0/16.

  4. Rutas

    ip route show table main | grep 10.10
    

    Debe aparecer la ruta predeterminada hacia la VM firewall.

Notas adicionales

  • Persistencia en el switch: guarda la configuración del puerto trunk y los puertos access antes de aplicar cambios desde Proxmox. Un error típico es que el switch siga etiquetando paquetes que el host espera sin etiqueta.
  • UID/GID mapping: en contenedores no privilegiados, asigna rangos de UID/GID que no colisionen con los del host para evitar problemas con volúmenes compartidos.
  • Backup de la topología: exporta vlan.yml y el playbook a un repositorio Git; así cualquier reinstalación de Proxmox puede recrear la red automáticamente.
  • Monitoreo: usa una herramienta ligera (por ejemplo, Gatus o Prometheus Node Exporter) para alertar cuando una VLAN pierde conectividad o cuando un LXC no responde al ping de salud.
  • Escalabilidad: al añadir nuevas VLANs, solo necesitas actualizar vlan.yml y volver a ejecutar el playbook; no hay cambios manuales en Proxmox ni en el switch.