Soy un Maniático del Control. Mi VPN Mesh También Debería Serlo.
Una VPN mesh privada conecta toda tu infraestructura de desarrollo en la misma red. Servidores VPS, tu Mac, tu teléfono. Todo se comunica a través de túneles cifrados en lugar del internet abierto. Dejas de exponer puertos SSH, APIs internas, conexiones de base de datos al mundo exterior. Menos superficie de ataque, menos riesgo, reglas de firewall más simples.
Tailscale hace esto bien. El túnel es P2P, la configuración toma 30 segundos, y te olvidas de que está ahí. Excepto que el servidor de coordinación, lo que permite que tus máquinas se encuentren entre sí, vive en la infraestructura de Tailscale. Y cuando ejecutas cargas de trabajo de producción multi-VPS, "en su infraestructura" es una dependencia que no pediste.
Ya me quemé una vez antes con un proveedor que cambió las reglas a mitad de juego en mi setup de producción. Una vez es suficiente. Así que migré a NetBird, completamente auto-hospedado, detrás del reverse proxy Traefik que ya ejecuto en mi VPS. La mesh en sí tomó 2 minutos por cliente. El trabajo real fue todo lo demás.
TLDR: NetBird reemplaza Tailscale con una mesh P2P WireGuard completamente auto-hospedada. La red está funcionando en 2 minutos. Pero si ya tienes Traefik en producción, el script oficial de instalación es inutilizable. Este artículo te da el docker-compose personalizado, el enrutamiento basado en rutas con prioridades, y las 7 trampas que ni la documentación ni ningún tutorial mencionan.

Tailscale Funciona. Ese No Es el Punto.
Tailscale es genuinamente buen software. Lo ejecuté durante meses. Conexiones P2P vía WireGuard, traversal de NAT que realmente funciona, un panel de administración limpio. Sin quejas del lado del túnel.
El problema es estructural. El servidor de coordinación (Tailscale lo llama su "plano de control") es un SaaS que no posees. Cada vez que tus máquinas necesitan descubrirse entre sí, llaman a casa. Si Tailscale cambia su precio, mata el tier gratuito, o tiene un mal día, tu red privada no puede establecer nuevas conexiones. Los túneles existentes sobreviven (son WireGuard por debajo), pero no hay nuevos peers, no hay re-keying, no hay cambios.
Para un proyecto de homelab de fin de semana, está bien. Para infraestructura VPS de producción donde ejecuto servicios de clientes, es una apuesta que no quiero seguir haciendo. No es paranoia. Solo el mismo reflejo que cualquier dev obtiene después de ver a un proveedor quitar la alfombra.
La solución era obvia. Auto-hospedar la capa de coordinación.
Revisé Todas las Alternativas. La Mayoría Son Callejones Sin Salida.
La lista corta se elimina sola rápido:
Headscale hace ingeniería inversa del protocolo de Tailscale. Eso significa que aún dependes de las aplicaciones cliente de Tailscale, y Tailscale no tiene obligación de mantener su protocolo estable para un servidor de terceros. La comunidad ha documentado este riesgo extensivamente. Tu servidor de coordinación auto-hospedado podría romperse en cualquier actualización del cliente Tailscale que no controlas.
ZeroTier usa un protocolo personalizado, no WireGuard. Eso es un dealbreaker para cualquiera que quiera criptografía estándar y auditada por debajo.
Nebula (la herramienta mesh de Slack) no tiene cliente iOS nativo. Mi teléfono es un peer. No negociable.
Netmaker tiene problemas de inestabilidad documentados en reportes de la comunidad. No quiero debuggear mi capa de networking.
WireGuard puro es sólido como una roca pero no tiene descubrimiento automático de mesh. Estás manejando manualmente configs de peers en cada nodo. Con 3 peers es molesto. Con 10 es un trabajo. Con 30 es un cambio de carrera que no solicitaste.
NetBird es el que sobrevivió la eliminación. Completamente open-source (cliente Y servidor de coordinación, no solo un lado). App iOS nativa en la App Store. WireGuard por debajo con capa post-cuántica Rosenpass encima. Gestión de usuarios local desde v0.62 (no se necesita proveedor de identidad externo para setups pequeños). Imagen Docker combinada que agrupa management, signal, relay, y STUN en un contenedor.
Y sí, NetBird es más joven que Tailscale. El ecosistema de integración de terceros es más delgado. No encontrarás los mismos conectores plug-and-play para cada herramienta SaaS. Pero no busco un ecosistema. Busco una mesh que posea.
El Script Oficial Está Hecho para Gente Sin Nada en Producción.
Fui a los docs de NetBird. Clickeé "Self-hosting." Descargué getting-started.sh.
Entonces veo que el script despliega su propia instancia de Traefik.
Ya tengo Traefik v3 ejecutándose en este VPS. Maneja el enrutamiento para sitios de clientes, apps, dashboards. Tiene certificados Let's Encrypt, cadenas de middleware, todo. No voy a derribar eso y no voy a ejecutar dos instancias de Traefik en la misma máquina peleando por el puerto 443.
Así que el script oficial va a la basura y te escribes un docker-compose personalizado que conecta los servicios de NetBird a tu Traefik existente. Y ahí es donde empieza el trabajo real.
El enrutamiento es la parte complicada. Todo vive en un solo dominio (netbird.tudominio.com), separado por ruta y prioridad. La lógica:
Prioridad 100 (más alta): servicios gRPC (signal y management API). Estos necesitan el esquema h2c porque Traefik termina TLS y reenvía HTTP/2 plano a los contenedores. Si usas http en lugar de h2c, gRPC falla silenciosamente y el cliente se queda atascado en "Disconnected."
Prioridad 50: conexiones WebSocket para el servicio relay, endpoints REST API, y rutas de callback OAuth2. HTTP estándar, nada exótico.
Prioridad 1 (catch-all): la UI del dashboard. Todo lo que no coincida con una regla de mayor prioridad llega aquí.
Coturn (servidor TURN/STUN): ejecuta en network_mode: host, completamente fuera de Traefik. Necesita UDP crudo en puertos 3478 y 49152-65535. Ningún reverse proxy puede ayudarte aquí.

Si has hecho enrutamiento Traefik antes, esto es territorio familiar. Si no, cada uno de esos niveles de prioridad me tomó una ronda de "por qué esto da 502" para hacerlo bien.
Las 7 Trampas de las que Nadie Me Advirtió
Envejecí visiblemente durante la configuración del dashboard Dex. Cuatro iteraciones en la pantalla de login antes de que funcionara. La mayoría de mis 2 horas de setup se gastaron en esta lista, no en la mesh.
1. exposedAddress necesita el puerto.
En config.yaml, el campo exposedAddress para el servidor signal debe incluir :443. El parser del cliente gRPC falla con "missing port in address" si lo omites. El servicio signal se queda "Disconnected" con cero mensajes de error útiles.
2. Pantalla en blanco después del login. Sin error.
El JavaScript del dashboard hace window.location.origin + redirectURI. Si tu AUTH_REDIRECT_URI es una URL completa en lugar de una ruta relativa, se duplica: https://netbird.tudominio.com/https://netbird.tudominio.com/nb-auth. Ponlo como /nb-auth, no el dominio completo. Buena suerte debuggeando eso sin leer el código fuente.
3. AUTH_CLIENT_ID es "netbird-dashboard", no "dashboard".
La configuración del cliente estático Dex espera netbird-dashboard. Si usas dashboard, la autenticación falla silenciosamente. Sin error en la UI, sin línea de log útil. Simplemente no puedes loguearte.
4. Los scopes de Dex no son scopes de Auth0.
Los scopes correctos para Dex son openid profile email groups. Si copias y pegas de ejemplos de Auth0 (y muchos posts de blog usan Auth0), agregarás api y email_verified. Esos scopes no existen en Dex. La solicitud de token falla.
5. El contenedor inicia, los logs se ven limpios, el login no funciona.
El script init del dashboard asume Auth0 por defecto. Necesitas AUTH_SUPPORTED_SCOPES=openid profile email groups y USE_AUTH0=false en las variables de entorno del dashboard. Sin ambas, el script crashea silenciosamente. Sin error, sin advertencia. Solo una página de login que rechaza cada intento.
6. store.encryptionKey debe estar en config.yaml.
Sin ella, el servidor de management genera una clave aleatoria al iniciar. Reinicia el contenedor y no puede descifrar sus propios datos. Ponla una vez, mantenla para siempre. (O disfruta re-registrando cada peer después de un reinicio.)
7. Rutas que están claramente definidas devuelven 404.
Verificas la sintaxis del label. Correcta. Verificas la ruta. Correcta. Verificas la prioridad. Correcta. Resulta que Docker Compose YAML y shell heredocs manejan backticks de manera diferente. Si tus labels de Traefik usan matchers de ruta delimitados por backticks, envuélvelos en comillas dobles en el YAML. Los heredocs producen reglas que se ven válidas pero están sutilmente rotas. Enloquecedor.
El patrón en las siete: el modo de falla es silencio. Sin crash, sin stack trace. Las cosas simplemente no funcionan, y los logs dicen que todo está bien.
2 Horas, 3 Peers, 2ms.
La arquitectura final: servidor de management en un VPS Contabo (Ubuntu 24.04, 8GB RAM). Un contenedor netbird-server combinado ejecutando management, signal, relay, y STUN. Contenedores separados para netbird-dashboard y netbird-coturn. Todo detrás de la instancia Traefik existente excepto Coturn.
Tres peers en la mesh: el VPS Contabo (Linux), un VPS Hostinger (Linux), y mi MacBook (arm64). Ping entre los dos nodos VPS: ~2ms P2P. Eso es WireGuard directo, sin salto de relay.
La primera vez que ejecuté ese ping y vi el número volver, me quedé ahí sentado por un segundo. Dos milisegundos a través de dos datacenters en una red que construí yo mismo. Sin relay de terceros, sin servidor de coordinación en la nube de alguien más. Solo mis cajas hablándose entre sí.
Casi le mando un mensaje a alguien sobre eso. Entonces recordé que la gente normal no se emociona por los tiempos de ping.
El cliente macOS se instala vía Homebrew. Se reconecta limpiamente después del sleep. El cliente iOS es una descarga nativa de la App Store, funciona en 4G/5G con traversal NAT a través de Coturn cuando P2P directo no es posible.
La autenticación ejecuta a través de Dex embebido con usuarios locales. Sin proveedor de identidad externo, sin Okta, sin Auth0. Para un setup de una persona (o tres personas), los usuarios locales son todo lo que necesitas.
La migración desde Tailscale fue la parte más simple. Desinstalar el cliente Tailscale, nada se rompe del lado del servicio (tus apps no saben qué VPN ejecuta por debajo). Actualizar configs SSH para apuntar a las nuevas IPs de mesh. Listo.
El mismo VPS ya ejecuta otros servicios auto-hospedados detrás del mismo proxy Traefik. Mismo patrón, mismo stack, misma filosofía: si es crítico para tu flujo de trabajo, poséelo.
Dos horas total. Noventa minutos de esas en el circo de autenticación del dashboard.
Cuando el Servidor de Management Muere, los Túneles No.
Esta es la parte que hace que toda la migración valga la pena.
Si mi servidor de management se cae, los túneles P2P existentes siguen funcionando. Las sesiones WireGuard entre peers que ya se conocen sobreviven. No pueden unirse nuevos peers, no se propagan cambios de configuración, pero el tráfico de producción entre máquinas conectadas continúa sin interrupción. Esa es una propiedad de WireGuard, no una característica de NetBird. Pero es una propiedad que solo obtienes cuando la capa de coordinación ejecuta junto a tus otros servicios, no en la nube de alguien más.
El procedimiento de backup es corto: archivos de configuración y volúmenes Docker replicados al segundo VPS. La recuperación son cinco pasos. Instalar Docker, copiar la configuración, restaurar el volumen, apuntar DNS al servidor de backup, docker compose up. Reconectar los clientes. Lo probé. Funciona.
También moví el dominio de management a algo menos obvio. La seguridad por oscuridad no es una estrategia, pero tampoco hay razón para hacer tu endpoint de coordinación fácilmente descubrible.
La industria seguirá vendiéndote "cero configuración" y "simplemente funciona." Y tienen razón, funciona. Hasta que el proveedor cambia el precio, mata un tier, o se cae. Entonces descubres que tu red privada dependía de un servidor que nunca poseíste.
La mesh VPN no es la parte complicada. Son 2 minutos y 2ms. La parte complicada es conectarla a un stack que ya ejecuta. Pero una vez que está hecho, es tuyo. El servidor de management muere, los túneles sobreviven. Quieres migrar, migras.
Poseer tu infra es ser libre y seguro. 🤷
Fuentes
(*) La portada es generada por IA. Los túneles WireGuard, sin embargo, son muy reales y están enrutando paquetes mientras hablamos.
Este artículo contiene enlaces de afiliado. Puedo ganar una pequeña comisión si compras a través de ellos.