WireGuard estático, Headscale, Netbird: tres enfoques distintos
WireGuard estático (vea nuestra guía de WireGuard) se basa en una configuración fija: cada par debe conocer la clave pública y la dirección accesible de sus homólogos, y cada nuevo par obliga a editar /etc/wireguard/wg0.conf en los demás. Eficaz para dos o tres servidores fijos. Inmanejable en cuanto un parque de agencia mezcla VPS, portátiles itinerantes y oficinas detrás de un CGNAT: añadir una máquina significa retocar todas las demás; retirar a un técnico significa revocar su clave en todas partes.
Headscale reimplementa el servidor de coordinación de Tailscale: conserva los clientes oficiales de Tailscale y obtiene una red plana sin tope de usuarios. El aislamiento entre clientes pasa por un único archivo de política ACL para toda la red. Es la opción adecuada para un solo equipo que quiere seguir en el ecosistema Tailscale.
Netbird toma el camino contrario: aporta su propio cliente, su propio plano de control y una noción de grupos unidos por reglas de acceso granulares, gestionadas desde la interfaz web incluida de serie. Una sola instancia puede alojar varios parques completamente separados, lo que la convierte en la herramienta natural para una agencia que gestiona clientes distintos.
Arquitectura de Netbird: un servidor combinado
El script de instalación actual ya no despliega cuatro contenedores separados. Genera una pila Docker Compose de tres servicios:
netbird-server — el servidor combinado. Reúne en un solo contenedor el Management (distribución de claves WireGuard, aplicación de las reglas de acceso, API REST), el Signal (intercambio de descriptores de conexión ICE entre pares), el Relay y un servidor STUN integrado. Se configura con un único config.yaml, que sustituye a los antiguos management.json y relay.env. Almacenamiento SQLite por defecto, PostgreSQL como opción.
netbird-dashboard — la interfaz web que consume la API de Management: grupos, pares, reglas de acceso y claves de alta, sin línea de comandos.
netbird-traefik — el proxy inverso que termina el TLS (certificado Let's Encrypt automático) y enruta el gRPC, el WebSocket y la API hacia el servidor.
Dos cambios importan respecto a los tutoriales antiguos. El relé ya no es COTURN: desde la v0.29, Netbird usa su propia implementación, que hace pasar las conexiones retransmitidas por el puerto 443 (WebSocket sobre TCP, o QUIC sobre UDP) en lugar de asignar un rango de puertos UDP. Y la autenticación está integrada: desde la v0.62, un proveedor de identidad embebido (basado en Dex) gestiona los usuarios locales, sin Zitadel ni Keycloak que desplegar. Un quinto servicio, el proxy de Netbird, se añade si lo activa para publicar servicios en Internet (vea más abajo).
Todos los flujos entre clientes pasan por WireGuard cifrado de extremo a extremo: el servidor solo ve los metadatos de alta, nunca el tráfico de las aplicaciones. En cuanto a licencias, el cliente está bajo BSD-3-Clause; los directorios management/, signal/, relay/ y combined/ del repositorio están bajo AGPLv3.
Requisitos previos
Un VPS dedicado al plano de control. Prevea un VPS distinto de sus nodos de cliente. La documentación de Netbird pide al menos 1 vCPU y 2 GB de RAM; un VPS Solo sirve para empezar.
Puertos que abrir en el VPS de control:
80 (TCP) — validación de Let's Encrypt y redirección a HTTPS.443 (TCP) — Dashboard, API y gRPC de Management, Signal, Relay por WebSocket.3478 (UDP) — STUN integrado en el servidor.
El rango UDP 49152-65535 ya no es necesario: solo servía al relé COTURN de las versiones anteriores a la v0.29, y solo se mantiene abierto si clientes antiguos aún dependen de él. Los nodos de cliente no tienen ningún puerto entrante que abrir: el cliente Netbird establece conexiones salientes hacia el plano de control.
Software necesario en el VPS de control: Docker con el plugin Compose, jq y curl, y un nombre de dominio cuyo registro A apunte a la IP del VPS (netbird.su-dominio.com). Si activa el proxy de Netbird, añada un CNAME *.netbird hacia ese mismo nombre. Detrás de Cloudflare, la documentación de Netbird deja ambos registros en modo solo DNS.
En cada nodo de cliente: el cliente netbird (paquete Debian/RPM o binario estático), acceso root o sudo.
Desplegar el plano de control y conectar tres nodos
Ejecutar el script de instalación
En el VPS de control, descargue el script oficial y ejecútelo:
curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bashEl script le pide su dominio (
netbird.su-dominio.com) y luego el proxy inverso que usar: conserve la opción[0](Traefik integrado, TLS automático). Después propone activar el proxy de Netbird, que también puede añadir más adelante. Generadocker-compose.yml,config.yamlydashboard.env(másproxy.envsi el proxy está activado) y arranca la pila. Para una instalación sin preguntas (cloud-init, CI), exporte antesNETBIRD_DOMAIN,NETBIRD_LETSENCRYPT_EMAILyNETBIRD_NON_INTERACTIVE=true.Comprobar que los servicios responden
Desde el directorio de instalación, confirme que los contenedores están en marcha:
docker compose psDebe ver
netbird-traefik,netbird-dashboardynetbird-serveren estadorunning(más el proxy si está activado). En caso de duda, la mayoría de los errores de configuración aparecen en el registro del servidor:docker compose logs -f netbird-serverCrear la cuenta de administrador y una Setup Key
Abra
https://netbird.su-dominio.comen un navegador. El script despliega Netbird sin ningún usuario: se le redirige a la página/setup, donde crea la cuenta de administrador (correo, nombre, contraseña). Esta página solo está disponible mientras no exista ningún usuario.En el menú Setup Keys, haga clic en Create Setup Key. Póngale un nombre (
vps-client-a), elija el tipoReusable(para dar de alta varias máquinas con la misma clave) y una duración de caducidad. Copie la clave: la necesitará en cada nodo.Dar de alta el nodo 1
En el primer VPS de cliente, instale el cliente Netbird:
curl -fsSL https://pkgs.netbird.io/install.sh | shDespués conéctelo al plano de control apuntando a su instancia (sin
--management-url, el cliente apunta al servicio en la nube de Netbird):netbird up \ --management-url https://netbird.su-dominio.com \ --setup-key <SU_SETUP_KEY>Confirme la conexión:
netbird statusDebe leer
Management: ConnectedySignal: Connected, además de una IP de malla en el rango100.64.x.xasignada por su servidor.Dar de alta los nodos 2 y 3
Repita exactamente el mismo procedimiento en los otros dos VPS de cliente. Mismo comando
curlpara instalar el cliente, mismo comandonetbird upcon el mismo--management-urly la misma--setup-key(si es de tipoReusable).Una vez dados de alta los tres nodos, compruebe desde el nodo 1 que los pares son visibles:
netbird status --detailLa salida enumera cada par con su IP de malla, su estado y su latencia. Desde la v0.82,
netbird status --tablepresenta la misma información en forma de tabla.Probar la conectividad de la malla sin puerto público abierto
Antes de probar, compruebe el estado del cortafuegos en el nodo 1 — no debe haber ningún puerto entrante abierto hacia los demás nodos:
sudo ufw status numberedSolo debe aparecer el puerto SSH (22). Ahora haga ping al nodo 2 por su IP de malla (visible en
netbird status --detail, por ejemplo100.64.0.2):ping -c 3 100.64.0.2El ping atraviesa el túnel WireGuard establecido entre los pares. Si una conexión directa es imposible, el relé integrado en el servidor toma el relevo en el puerto 443: el ping funciona en ambos casos, sin ninguna regla UFW adicional.
Distinguir conexiones directas y retransmitidas
Para saber si un par conecta en directo o a través del relé:
netbird status --detailEl campo
Connection typemuestraP2Ppara una conexión directa oRelayedcuando el tráfico pasa por el relé. Para listar solo los pares retransmitidos:netbird status -d --filter-by-connection-type RelayedP2Pes el estado esperado entre dos VPS con IPv4 pública.Relayedsigue cifrado de extremo a extremo, pero añade latencia: la sección de resolución de problemas indica qué comprobar.
Aislamiento multicliente: grupos y reglas de acceso
La fuerza de Netbird frente a Headscale es la separación por grupo, gestionada desde la interfaz. Un punto que conocer primero: al crear la cuenta, Netbird establece una política Default que permite a todos los pares del grupo especial All comunicarse entre sí. Mientras exista, ningún aislamiento se sostiene. Para separar los servidores del cliente A de los del cliente B:
1. Cree un grupo por cliente en la interfaz web (client-a, client-b) y un grupo agencia-ops para los equipos del personal.
2. Asigne cada par a su grupo. En la ficha del par, en la sección de grupos, añada el grupo correspondiente. Una Setup Key también puede asignar un grupo automáticamente a cada máquina dada de alta con ella: prevea una clave por cliente.
3. Defina las reglas de acceso (Access Control → Policies). Una política client-a-interna autoriza el tráfico entre pares del grupo client-a. Una política ops-ssh autoriza solo al grupo agencia-ops a alcanzar el puerto 22 de los grupos de clientes: un equipo comprometido dentro de la malla ya no puede servir de trampolín hacia todos los VPS. No se crea ninguna regla entre client-a y client-b.
4. Elimine la política Default una vez que las políticas de sustitución estén en su sitio. En el orden inverso, toda comunicación entre pares se detiene.
Resultado: el equipo de un administrador del cliente A alcanza el VPS A por su IP de malla pero no ve el VPS B, y el grupo agencia-ops sigue siendo el único miembro transversal. También puede definir Network Routes: un par actúa como router de una subred privada (p. ej. 192.168.10.0/24) y la expone a los demás pares del grupo, sin cliente Netbird en cada máquina de esa subred.
Cerrar el SSH público: escuchar solo en la interfaz de malla
Una vez dados de alta los nodos, la administración ya no necesita un puerto SSH abierto a Internet: ssh [email protected] transita por completo dentro del túnel cifrado. El cliente Netbird crea la interfaz WireGuard wt0 (nombre por defecto); autorice SSH en esa interfaz y luego rechácelo en el resto:
sudo ufw allow in on wt0 to any port 22 proto tcp
sudo ufw deny 22/tcp
sudo ufw status numberedUFW aplica la primera regla que coincide: la regla wt0 debe aparecer antes del rechazo. Mantenga abierta su sesión SSH actual y pruebe una nueva conexión por la IP de malla antes de cerrarla: si el túnel no se levanta, conserva una vía de salida. La política ops-ssh vista antes limita después quién, dentro de la malla, puede alcanzar ese puerto.
`netbird expose`: publicar un servicio local sin abrir puertos
La malla une sus máquinas entre sí. Para hacer accesible un servicio desde Internet sin tocar el cortafuegos del VPS, Netbird ofrece desde la v0.66 (febrero de 2026) el comando netbird expose. Se apoya en el proxy de Netbird: en una instancia autoalojada, debe estar activado (en la instalación o más tarde), con un registro DNS comodín hacia el servidor.
Un administrador debe autorizar primero la función: Settings → Clients → Peer Expose, limitando si es necesario los grupos de pares autorizados. Después, en el VPS donde funciona por ejemplo un Grafana en 127.0.0.1:3000:
netbird expose 3000 --with-name-prefix grafana --with-user-groups supervision-adminsEl comando muestra una URL pública en el dominio del proxy y mantiene la sesión abierta; Ctrl+C la retira. El servicio es efímero: el cliente lo renueva cada 30 segundos, y el servidor lo elimina entre 90 y 120 segundos después de la última renovación si el cliente desaparece. El puerto 3000 sigue cerrado en el cortafuegos del VPS.
Sin opción de autenticación, la URL es pública para cualquiera que la conozca. Existen tres protecciones para HTTP: --with-user-groups (inicio de sesión SSO, reservado a los grupos indicados), --with-password <contraseña> (una contraseña que usted elige) y --with-pin <6 cifras>. --protocol tcp publica un servicio no HTTP, sin autenticación posible. Cada par puede mantener 10 sesiones simultáneas, y el comando falla si la opción Block Inbound Connections está activa en el par. Para un servicio permanente, créelo mejor en el Dashboard.
Netbird, Headscale y WireGuard manual de un vistazo
Desplace la tabla
| Criterio | Netbird autoalojado | Headscale | WireGuard manual |
|---|---|---|---|
| Gestión de pares | Automática, plano de control e interfaz web | Automática, clientes oficiales de Tailscale | Manual, un archivo `wg0.conf` en cada nodo |
| NAT y CGNAT | STUN integrado, relé en el puerto 443 | Servidores DERP (servidor DERP integrado opcional) | Exige una IP pública o una redirección de puertos |
| Separación | Grupos y políticas en la interfaz | Archivo de política ACL (HuJSON) | Ninguna, aparte de los `AllowedIPs` de cada par |
| Publicación de un servicio | `netbird expose` o proxy de Netbird | No integrada | Túnel SSH o proxy inverso que montar uno mismo |
| Licencia | BSD-3 (cliente), AGPLv3 (servidor) | BSD-3-Clause | Software libre (núcleo Linux y herramientas) |
Operaciones habituales
Renovar o revocar una Setup Key. En la interfaz web, Setup Keys → su clave → Revoke. Los pares ya dados de alta conservan su conexión; los nuevos intentos de alta con esa clave se rechazan. Cree una clave nueva para las próximas altas.
Revocar un par. Peers → seleccione el par → Delete. El nodo sale de la malla; en el lado del cliente, la conexión con Management cae y se destruyen los túneles WireGuard hacia ese par.
Acceso a la API para la automatización. Netbird expone una API REST documentada. Cree un usuario de servicio (Team → Service Users) y su token de acceso, y gestione todo desde sus scripts de Ansible o sus pipelines de CI:
curl -s https://netbird.su-dominio.com/api/peers \
-H 'Authorization: Token <PAT>'Monitorización. El servidor combinado expone sus métricas Prometheus en /metrics, puerto 9090 por defecto (clave server.metricsPort de config.yaml): un único objetivo de scrape cubre Management, Signal y Relay. Conecte Grafana para seguir los pares conectados y la actividad del relé.
Actualizar. Desde el directorio de instalación, descargue las nuevas imágenes y recree los contenedores:
docker compose pull netbird-server dashboard
docker compose up -d --force-recreate netbird-server dashboardAñada proxy a ambos comandos si está activado. Si docker-compose.yml fija versiones, cambie primero las etiquetas image:: docker compose pull no las modifica. Los pares pierden el plano de control unos segundos durante el reinicio; programe la operación fuera de las horas de más uso. Traefik se actualiza por separado de Netbird.
Refuerzo: MFA en el Dashboard y copias de seguridad de los datos
El proveedor de identidad embebido gestiona la doble autenticación de las cuentas locales: Settings → Authentication → Local MFA exige un código TOTP a cada usuario. También puede conectar un proveedor OIDC (Keycloak, Authentik, Microsoft Entra ID…) y aplicar allí su propia política de MFA.
El estado persistente de su red (base de datos de Management, claves de cifrado) vive en el volumen Docker netbird_data, montado en /var/lib/netbird: perderlo significa volver a dar de alta todos sus pares. Detenga la pila para obtener una copia coherente, guarde los archivos de configuración y el volumen, y vuelva a arrancar:
docker compose stop
mkdir -p backup
cp docker-compose.yml config.yaml dashboard.env backup/
docker compose cp -a netbird-server:/var/lib/netbird/ backup/
docker compose startSi el proxy está activado, añada proxy.env y traefik-dynamic.yaml. Conserve al menos siete días de copias, fuera del VPS.
Resolución de problemas
Pares bloqueados en Relayed.
El relé funciona, pero la conexión directa falla. Compruebe primero que el puerto UDP 3478 del VPS de control está abierto (ufw status) y después que los nodos pueden emitir UDP saliente. En un VPS con IP pública y sin NAT, un cortafuegos de host que rechaza el UDP entrante no solicitado basta para que falle la conexión directa; la documentación de Netbird propone entonces sudo ufw allow in proto udp to any port 49152:65535, a costa de una apertura más amplia, o aceptar el relé. Un par Connected que no responde al ping suele señalar el mismo bloqueo, en el lado de la interfaz wt0.
UDP bloqueado por una red corporativa.
Nada que configurar: desde la v0.36, el cliente prueba el relé por QUIC (UDP/443) y por WebSocket (TCP/443), y se queda con el primero que responde. Si solo pasa el TCP/443 saliente, el par sigue conectado, en modo retransmitido.
Par bloqueado en Connecting.
El alta ha funcionado pero el túnel no se forma. Compruebe que netbird status muestra Signal: Connected, que el servidor anuncia una dirección pública correcta (campo exposedAddress de config.yaml) y que los puertos 443/TCP y 3478/UDP son accesibles desde el nodo.
Leer los registros.
En el servidor, docker compose logs -f netbird-server. En el cliente, el registro se escribe por defecto en /var/log/netbird/client.log. Para un diagnóstico completo, netbird debug for 5m pone el cliente en registro detallado durante cinco minutos y luego genera un archivo de diagnóstico (netbird debug bundle lo genera de inmediato).
Resolución DNS entre pares.
Netbird asigna un nombre DNS a cada par, visible en el campo Domain Name de su ficha. Si el ping por nombre falla mientras que ping 100.64.0.2 funciona, compruebe que el servicio está en marcha en el par (sudo netbird service status) y que el DNS del sistema pasa realmente por Netbird (resolvectl status).
Proxy de Netbird bloqueado en «Issuing certificate…»
Cuando el proxy está activado, un síntoma se repite: el servicio creado se queda en «Issuing certificate…» (estado certificate_pending, o certificate_failed) y nunca es accesible. La incidencia netbird #5517, abierta el 5 de marzo de 2026, sigue abierta a 7 de noviembre de 2026 con 36 comentarios y la etiqueta triage-needed. El mensaje más frecuente en el registro del proxy, no viable challenge type found, solo dice que la autoridad de certificación no obtuvo ninguna respuesta válida a sus desafíos: no hay una causa única. Empiece por leer el registro (docker compose logs proxy | grep -i acme) y anote el error exacto; después descarte las causas una a una.
Orden de diagnóstico de un certificado que no se emite
- Permisos de la carpeta de certificados — un error
permission denieden/certses el único caso zanjado por un colaborador del repositorio: ninguna actualización lo corregirá, la causa son los permisos del sistema de archivos. Compruebe con qué usuario se ejecuta el contenedor antes de cambiar los permisos. - DNS, CAA y puertos — el nombre debe resolver hacia el proxy y ningún registro CAA debe excluir a Let's Encrypt. El puerto 443 debe ser accesible desde Internet para el desafío
tls-alpn-01(el predeterminado), el puerto 80 parahttp-01. Let's Encrypt valida desde varias regiones del mundo: un filtrado geográfico hace fallar la validación. - CDN o proxy previo — Cloudflare y algunos equipos no transmiten el protocolo ALPN
acme-tls/1. En ese caso, pase aNB_PROXY_ACME_CHALLENGE_TYPE=http-01enproxy.env. - Traefik delante — si Traefik retransmite el TLS hacia el proxy en passthrough, su punto de entrada HTTPS debe llevar
allowACMEByPass=true, que el script de instalación establece enwebsecure. En un Traefik existente, use el nombre de su propio punto de entrada; algunos participantes de la incidencia tuvieron que añadirlo también al punto de entrada HTTP, otros ya lo tenían sin resultado. - Lo que no está demostrado — ninguna de estas pistas es «la» causa: pruébelas en su instalación antes de concluir.
Origen cerrado: el certificado por DNS-01 con lego
Los desafíos tls-alpn-01 y http-01 exigen que la autoridad alcance su proxy desde Internet, lo que choca con un origen cerrado a los rangos de una CDN o con un filtrado geográfico. El desafío dns-01 elimina esa restricción: demuestra el control del dominio con un registro TXT temporal, creado mediante la API de su proveedor DNS. El proxy de Netbird no lo integra de forma nativa: la petición (incidencia #5450, abierta el 25 de febrero de 2026) sigue abierta. La documentación de Netbird describe en cambio un montaje «private proxy with lego»: lego obtiene y renueva un certificado comodín por DNS-01, un hook de despliegue lo deposita en un volumen compartido y el proxy lo recarga en caliente tras cada renovación.
Puntos de atención del montaje con lego
- Variables del proxy —
NB_PROXY_WILDCARD_CERT_DIRindica la carpeta de los certificados comodín,NB_PROXY_PRIVATE=trueactiva las escuchas privadas en la malla. Fije una imagen del proxy que conozca ambas variables. - El modo privado no cierra nada por sí solo —
NB_PROXY_PRIVATEno desactiva la escucha ordinaria del proxy. Retire usted mismo los puertos publicados y las rutas de entrada (etiquetas de Traefik, Caddy, nginx) que llevan al contenedor. - Alcance del comodín —
*.proxy.su-dominio.comsolo cubre un nivel de subdominio. Un nombre fuera de esa cobertura vuelve al ACME integrado, que no puede funcionar si el proxy es completamente privado. - Exposición del certificado — el comodín y el dominio base aparecen en los registros de Certificate Transparency, no los nombres de los servicios que cubre; en cambio, una clave comodín comprometida afecta a todos esos nombres.
- Explotación — use el servidor ACME de producción de Let's Encrypt (sus certificados de prueba no son de confianza), guarde los volúmenes de lego y de los certificados, y fije versiones probadas de lego y del proxy.
Lo que la malla cambia en la gestión del parque
Una malla Netbird autoalojada sustituye tres capas que usted mantiene a mano: la distribución de claves WireGuard, las reglas UFW entre servidores y la documentación de los accesos cruzados. Cada nuevo VPS de cliente se da de alta con un comando; cada revocación es inmediata y centralizada. El SSH público puede cerrarse, y netbird expose publica bajo demanda un servicio sin abrir ningún puerto.
El aislamiento por grupo le permite crecer sin riesgo de colisión: los servidores de dos clientes distintos no pueden verse, aunque funcionen en la misma infraestructura. Y el plano de control sigue bajo su control: ninguna dependencia de un SaaS de terceros, ningún límite de puestos, ninguna suscripción por nodo.
Para ir más lejos, describa el aprovisionamiento de los nodos en Ansible (vea nuestra guía de Ansible): la instalación del cliente, el comando netbird up y las reglas UFW en wt0 se convierten en tareas idempotentes dentro de un rol reutilizable.