WireGuard P2P, Headscale, Netbird : trois angles différents
WireGuard statique (voir notre guide WireGuard) repose sur une configuration figée : chaque pair doit connaître la clé publique et l'adresse joignable de ses homologues, et chaque nouvel homologue impose d'éditer /etc/wireguard/wg0.conf sur les autres. Efficace pour deux ou trois serveurs fixes. Ingérable dès qu'un parc d'agence mêle des VPS, des postes nomades et des bureaux derrière un CGNAT : ajouter une machine, c'est retoucher toutes les autres ; retirer un technicien, c'est révoquer sa clé partout.
Headscale réimplémente le serveur de coordination Tailscale : vous gardez les clients Tailscale officiels et obtenez un réseau plat sans plafond d'utilisateurs. L'isolation entre clients passe par un fichier de politique ACL unique pour tout le réseau. C'est le bon choix pour une équipe unique qui veut rester dans l'écosystème Tailscale.
Netbird prend le parti inverse : il apporte son propre client, son propre plan de contrôle et une notion de groupes reliés par des règles d'accès granulaires, gérées depuis l'interface web incluse d'emblée. Une instance unique peut héberger plusieurs parcs entièrement cloisonnés, ce qui en fait l'outil naturel pour une agence qui gère des clients séparés.
Architecture Netbird : un serveur combiné
Le script d'installation actuel ne déploie plus quatre conteneurs séparés. Il génère une stack Docker Compose de trois services :
netbird-server — le serveur combiné. Il réunit dans un seul conteneur le Management (distribution des clés WireGuard, application des règles d'accès, API REST), le Signal (échange des descripteurs de connexion ICE entre pairs), le Relay et un serveur STUN intégré. Il se configure par un unique config.yaml, qui remplace les anciens management.json et relay.env. Stockage SQLite par défaut, PostgreSQL en option.
netbird-dashboard — l'interface web qui consomme l'API Management : groupes, pairs, règles d'accès, clés d'enrôlement, sans ligne de commande.
netbird-traefik — le reverse proxy qui termine le TLS (certificat Let's Encrypt automatique) et route le gRPC, le WebSocket et l'API vers le serveur.
Deux changements comptent par rapport aux anciens tutoriels. Le relais n'est plus COTURN : depuis la v0.29, Netbird utilise sa propre implémentation, qui fait passer les connexions relayées par le port 443 (WebSocket sur TCP, ou QUIC sur UDP) au lieu d'allouer une plage de ports UDP. Et l'authentification est intégrée : depuis la v0.62, un fournisseur d'identité embarqué (basé sur Dex) gère les utilisateurs locaux, sans Zitadel ni Keycloak à déployer. Un cinquième service, le proxy Netbird, s'ajoute si vous l'activez pour publier des services sur Internet (voir plus bas).
Tous les flux entre clients passent en WireGuard chiffré de bout en bout : le serveur ne voit que les métadonnées d'enrôlement, jamais le trafic applicatif. Côté licences, le client est sous BSD-3-Clause ; les répertoires management/, signal/, relay/ et combined/ du dépôt sont sous AGPLv3.
Prérequis
VPS dédié au plan de contrôle. Prévoyez un VPS distinct de vos nœuds clients. La documentation Netbird demande au moins 1 vCPU et 2 Go de RAM ; un VPS Solo convient pour démarrer.
Ports à ouvrir sur le VPS de contrôle :
80 (TCP) — validation Let's Encrypt et redirection vers HTTPS.443 (TCP) — Dashboard, API et gRPC Management, Signal, Relay en WebSocket.3478 (UDP) — STUN intégré au serveur.
La plage 49152-65535 en UDP n'est plus nécessaire : elle ne servait qu'au relais COTURN des versions antérieures à la v0.29, et ne reste ouverte que si d'anciens clients en dépendent encore. Les nœuds clients n'ont aucun port entrant à ouvrir : le client Netbird établit des connexions sortantes vers le plan de contrôle.
Logiciels requis sur le VPS de contrôle : Docker avec le plugin Compose, jq et curl, et un nom de domaine dont l'enregistrement A pointe vers l'IP du VPS (netbird.votre-domaine.com). Si vous activez le proxy Netbird, ajoutez un CNAME *.netbird vers ce même nom. Derrière Cloudflare, la documentation Netbird prévoit ces deux enregistrements en DNS seul.
Sur chaque nœud client : le client netbird (paquet Debian/RPM ou binaire statique), accès root ou sudo.
Déployer le plan de contrôle et raccorder trois nœuds
Lancer le script d'installation
Sur le VPS de contrôle, téléchargez le script officiel et exécutez-le :
curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bashLe script demande votre domaine (
netbird.votre-domaine.com), puis le reverse proxy à utiliser : gardez l'option[0](Traefik intégré, TLS automatique). Il propose ensuite d'activer le proxy Netbird, que vous pourrez ajouter plus tard. Il génèredocker-compose.yml,config.yamletdashboard.env(plusproxy.envsi le proxy est activé), puis démarre la stack. Pour une installation sans questions (cloud-init, CI), exportez au préalableNETBIRD_DOMAIN,NETBIRD_LETSENCRYPT_EMAILetNETBIRD_NON_INTERACTIVE=true.Vérifier que les services répondent
Depuis le répertoire d'installation, confirmez que les conteneurs sont debout :
docker compose psVous devez voir
netbird-traefik,netbird-dashboardetnetbird-serveren étatrunning(plus le proxy s'il est activé). En cas de doute, la plupart des erreurs de configuration apparaissent dans le journal du serveur :docker compose logs -f netbird-serverCréer le compte administrateur et une Setup Key
Ouvrez
https://netbird.votre-domaine.comdans un navigateur. Le script déploie Netbird sans aucun utilisateur : vous êtes redirigé vers la page/setup, où vous créez le compte administrateur (e-mail, nom, mot de passe). Cette page n'est accessible que tant qu'aucun utilisateur n'existe.Dans le menu Setup Keys, cliquez Create Setup Key. Donnez-lui un nom (
vps-client-a), choisissez le typeReusable(pour enrôler plusieurs machines avec la même clé) et une durée d'expiration. Copiez la clé : vous en aurez besoin sur chaque nœud.Enrôler le nœud 1
Sur le premier VPS client, installez le client Netbird :
curl -fsSL https://pkgs.netbird.io/install.sh | shPuis raccordez-le au plan de contrôle en pointant vers votre instance (sans
--management-url, le client vise le service cloud de Netbird) :netbird up \ --management-url https://netbird.votre-domaine.com \ --setup-key <VOTRE_SETUP_KEY>Confirmez la connexion :
netbird statusVous devez lire
Management: ConnectedetSignal: Connected, ainsi qu'une IP mesh dans la plage100.64.x.xattribuée par votre serveur.Enrôler les nœuds 2 et 3
Répétez exactement la même procédure sur les deux autres VPS clients. Même commande
curlpour installer le client, même commandenetbird upavec le même--management-urlet la même--setup-key(si elle est de typeReusable).Une fois les trois nœuds enrôlés, vérifiez depuis le nœud 1 que les pairs sont visibles :
netbird status --detailLa sortie liste chaque pair avec son IP mesh, son état et sa latence. Depuis la v0.82,
netbird status --tableprésente la même information sous forme de tableau.Tester la connectivité mesh sans port public ouvert
Avant de tester, vérifiez l'état du pare-feu sur le nœud 1 — aucun port entrant ne doit être ouvert vers les autres nœuds :
sudo ufw status numberedSeul le port SSH (22) doit apparaître. Pinguez maintenant le nœud 2 via son IP mesh (visible dans
netbird status --detail, par exemple100.64.0.2) :ping -c 3 100.64.0.2Le ping traverse le tunnel WireGuard établi entre les pairs. Si une connexion directe est impossible, le relais intégré au serveur prend le relais sur le port 443 : le ping fonctionne dans les deux cas, sans aucune règle UFW supplémentaire.
Distinguer connexions directes et relayées
Pour savoir si un pair passe en direct ou par le relais :
netbird status --detailLe champ
Connection typeafficheP2Ppour une connexion directe ouRelayedlorsque le trafic transite par le relais. Pour ne lister que les pairs relayés :netbird status -d --filter-by-connection-type RelayedP2Pest l'état attendu entre deux VPS dotés d'une IPv4 publique.Relayedreste chiffré de bout en bout, mais ajoute de la latence : la section Dépannage indique quoi vérifier.
Isolation multi-clients : groupes et règles d'accès
La force de Netbird face à Headscale est le cloisonnement par groupe, piloté depuis l'interface. Un point à connaître d'abord : à la création du compte, Netbird pose une politique Default qui autorise tous les pairs du groupe spécial All à communiquer entre eux. Tant qu'elle existe, aucune isolation ne tient. Pour séparer les serveurs du client A de ceux du client B :
1. Créez un groupe par client dans l'interface web (client-a, client-b) et un groupe agence-ops pour les postes de l'équipe.
2. Assignez chaque pair à son groupe. Dans la fiche du pair, section des groupes, ajoutez le groupe correspondant. Une Setup Key peut aussi attribuer un groupe automatiquement à chaque machine enrôlée avec elle : prévoyez une clé par client.
3. Définissez les règles d'accès (Access Control → Policies). Une politique client-a-interne autorise le trafic entre pairs du groupe client-a. Une politique ops-ssh autorise le seul groupe agence-ops à joindre le port 22 des groupes clients : un poste compromis dans le mesh ne peut alors pas servir de rebond vers tous les VPS. Aucune règle n'est créée entre client-a et client-b.
4. Supprimez la politique Default, une fois les politiques de remplacement en place. Dans l'autre ordre, toute communication entre pairs s'arrête.
Résultat : le poste d'un administrateur du client A joint le VPS A par son IP mesh mais ne voit pas le VPS B, et le groupe agence-ops reste le seul membre transversal. Vous pouvez aussi définir des Network Routes : un pair joue le rôle de routeur pour un sous-réseau privé (ex. 192.168.10.0/24) et l'expose aux autres pairs du groupe, sans client Netbird sur chaque machine de ce sous-réseau.
Fermer le SSH public : n'écouter que sur l'interface mesh
Une fois les nœuds enrôlés, l'administration n'a plus besoin d'un port SSH ouvert sur Internet : ssh [email protected] transite entièrement dans le tunnel chiffré. Le client Netbird crée l'interface WireGuard wt0 (nom par défaut) ; autorisez SSH sur cette interface, puis refusez-le ailleurs :
sudo ufw allow in on wt0 to any port 22 proto tcp
sudo ufw deny 22/tcp
sudo ufw status numberedUFW applique la première règle qui correspond : la règle wt0 doit apparaître avant le refus. Gardez votre session SSH en cours ouverte et testez une nouvelle connexion par l'IP mesh avant de la fermer : si le tunnel ne monte pas, vous gardez une porte de sortie. La politique ops-ssh vue plus haut limite ensuite qui, dans le mesh, peut atteindre ce port.
`netbird expose` : publier un service local sans ouvrir de port
Le mesh relie vos machines entre elles. Pour rendre un service accessible depuis Internet sans toucher au pare-feu du VPS, Netbird propose depuis la v0.66 (février 2026) la commande netbird expose. Elle s'appuie sur le proxy Netbird : sur une instance auto-hébergée, il doit être activé (à l'installation ou plus tard), avec un enregistrement DNS wildcard vers le serveur.
Un administrateur doit d'abord autoriser la fonction : Settings → Clients → Peer Expose, en limitant si besoin les groupes de pairs autorisés. Ensuite, sur le VPS où tourne par exemple un Grafana en 127.0.0.1:3000 :
netbird expose 3000 --with-name-prefix grafana --with-user-groups supervision-adminsLa commande affiche une URL publique sur le domaine du proxy et garde la session ouverte ; Ctrl+C la retire. Le service est éphémère : le client le renouvelle toutes les 30 secondes, et le serveur le supprime environ 90 à 120 secondes après le dernier renouvellement si le client disparaît. Le port 3000 reste fermé sur le pare-feu du VPS.
Sans option d'authentification, l'URL est publique pour quiconque la connaît. Trois protections existent pour le HTTP : --with-user-groups (connexion SSO, réservée aux groupes cités), --with-password <mot-de-passe> (mot de passe que vous choisissez) et --with-pin <6 chiffres>. --protocol tcp publie un service non HTTP, sans authentification possible. Chaque pair peut tenir 10 sessions simultanées, et la commande échoue si l'option Block Inbound Connections est active sur le pair. Pour un service permanent, créez-le plutôt dans le Dashboard.
Netbird, Headscale et WireGuard manuel en un coup d'œil
Faites défiler le tableau
| Critère | Netbird auto-hébergé | Headscale | WireGuard manuel |
|---|---|---|---|
| Gestion des pairs | Automatique, plan de contrôle et interface web | Automatique, clients Tailscale officiels | Manuelle, fichier `wg0.conf` sur chaque nœud |
| NAT et CGNAT | STUN intégré, relais sur le port 443 | Serveurs DERP (serveur DERP intégré en option) | Exige une IP publique ou une redirection de port |
| Cloisonnement | Groupes et politiques dans l'interface | Fichier de politique ACL (HuJSON) | Aucun, en dehors des `AllowedIPs` de chaque pair |
| Publication d'un service | `netbird expose` ou proxy Netbird | Non intégré | Tunnel SSH ou reverse proxy à monter soi-même |
| Licence | BSD-3 (client), AGPLv3 (serveur) | BSD-3-Clause | Libre (noyau Linux et outils) |
Opérations courantes
Renouveler ou révoquer une Setup Key. Dans l'interface web, Setup Keys → votre clé → Revoke. Les pairs déjà enrôlés conservent leur connexion ; les nouvelles tentatives d'enrôlement avec cette clé sont refusées. Créez une nouvelle clé pour les prochains enrôlements.
Révoquer un pair. Peers → sélectionnez le pair → Delete. Le nœud est retiré du mesh ; côté client, la connexion au Management tombe et les tunnels WireGuard vers ce pair sont détruits.
Accès API pour l'automatisation. Netbird expose une API REST documentée. Créez un utilisateur de service (Team → Service Users) et son jeton d'accès, puis pilotez l'ensemble depuis vos scripts Ansible ou vos pipelines CI :
curl -s https://netbird.votre-domaine.com/api/peers \
-H 'Authorization: Token <PAT>'Monitoring. Le serveur combiné expose ses métriques Prometheus sur /metrics, port 9090 par défaut (clé server.metricsPort de config.yaml) : une seule cible de scrape couvre Management, Signal et Relay. Branchez Grafana dessus pour suivre les pairs connectés et l'activité du relais.
Mettre à jour. Depuis le répertoire d'installation, tirez les nouvelles images puis recréez les conteneurs :
docker compose pull netbird-server dashboard
docker compose up -d --force-recreate netbird-server dashboardAjoutez proxy aux deux commandes s'il est activé. Si docker-compose.yml épingle des versions, modifiez d'abord les étiquettes image: : docker compose pull ne les change pas. Les pairs perdent le plan de contrôle quelques secondes pendant le redémarrage ; planifiez l'opération hors des heures chargées. Traefik se met à jour séparément de Netbird.
Durcissement : MFA sur le Dashboard et sauvegarde des données
Le fournisseur d'identité embarqué gère la double authentification des comptes locaux : Settings → Authentication → Local MFA impose un code TOTP à chaque utilisateur. Vous pouvez aussi brancher un fournisseur OIDC (Keycloak, Authentik, Microsoft Entra ID…) et y appliquer votre propre politique MFA.
L'état persistant de votre réseau (base Management, clés de chiffrement) vit dans le volume Docker netbird_data, monté sur /var/lib/netbird : le perdre signifie réenrôler tous vos pairs. Arrêtez la stack pour obtenir une copie cohérente, sauvegardez les fichiers de configuration et le volume, puis redémarrez :
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 le proxy est activé, ajoutez proxy.env et traefik-dynamic.yaml. Conservez au moins sept jours de sauvegardes, hors du VPS.
Dépannage
Pairs bloqués en Relayed.
Le relais fonctionne, mais la connexion directe échoue. Vérifiez d'abord que le port UDP 3478 du VPS de contrôle est ouvert (ufw status), puis que les nœuds peuvent émettre de l'UDP sortant. Sur un VPS doté d'une IP publique, sans NAT, un pare-feu d'hôte qui refuse l'UDP entrant non sollicité suffit à faire échouer la connexion directe ; la documentation Netbird propose alors sudo ufw allow in proto udp to any port 49152:65535, au prix d'une ouverture plus large, ou d'accepter le relais. Un pair Connected qui ne répond pas au ping signale souvent le même blocage, côté interface wt0.
UDP bloqué par un réseau d'entreprise.
Rien à configurer : depuis la v0.36, le client essaie le relais en QUIC (UDP/443) et en WebSocket (TCP/443), et garde le premier qui répond. Si seul le TCP/443 sortant passe, le pair reste connecté, en mode relayé.
Pair bloqué en Connecting.
L'enrôlement a réussi mais le tunnel ne se forme pas. Contrôlez que netbird status affiche bien Signal: Connected, que le serveur annonce une adresse publique correcte (champ exposedAddress de config.yaml) et que les ports 443/TCP et 3478/UDP sont joignables depuis le nœud.
Lire les journaux.
Côté serveur, docker compose logs -f netbird-server. Côté client, le journal est écrit par défaut dans /var/log/netbird/client.log. Pour un diagnostic complet, netbird debug for 5m passe le client en journalisation détaillée pendant cinq minutes puis produit une archive de diagnostic (netbird debug bundle la produit immédiatement).
Résolution DNS entre pairs.
Netbird attribue un nom DNS à chaque pair, visible dans le champ Domain Name de sa fiche. Si le ping par nom échoue alors que ping 100.64.0.2 fonctionne, vérifiez que le service tourne sur le pair (sudo netbird service status) et que le DNS du système passe bien par Netbird (resolvectl status).
Proxy Netbird bloqué sur « Issuing certificate… »
Quand le proxy est activé, un symptôme revient : le service créé reste sur « Issuing certificate… » (statut certificate_pending, ou certificate_failed) et n'est jamais joignable. Le ticket netbird #5517, ouvert le 5 mars 2026, est toujours ouvert au 7 novembre 2026 avec 36 commentaires et l'étiquette triage-needed. Le message le plus fréquent dans le journal du proxy, no viable challenge type found, dit seulement que l'autorité de certification n'a obtenu aucune réponse valide à ses défis : il n'existe pas de cause unique. Commencez par lire le journal (docker compose logs proxy | grep -i acme) et notez l'erreur exacte, puis éliminez les causes une à une.
Ordre de diagnostic d'un certificat qui ne sort pas
- Droits du dossier des certificats — une erreur
permission deniedsur/certsest le seul cas tranché par un collaborateur du dépôt : aucune mise à jour ne la corrigera, ce sont les permissions du système de fichiers. Vérifiez l'utilisateur sous lequel tourne le conteneur avant de changer les droits. - DNS, CAA et ports — le nom doit résoudre vers le proxy et aucun enregistrement CAA ne doit exclure Let's Encrypt. Le port 443 doit être joignable depuis Internet pour le défi
tls-alpn-01(par défaut), le port 80 pourhttp-01. Let's Encrypt valide depuis plusieurs régions du monde : un filtrage géographique fait échouer la validation. - CDN ou proxy en amont — Cloudflare et certains équipements ne transmettent pas le protocole ALPN
acme-tls/1. Passez alors àNB_PROXY_ACME_CHALLENGE_TYPE=http-01dansproxy.env. - Traefik en tête — si Traefik relaie le TLS vers le proxy en passthrough, son point d'entrée HTTPS doit porter
allowACMEByPass=true, que le script d'installation pose surwebsecure. Sur un Traefik existant, utilisez le nom de votre propre point d'entrée ; des participants du ticket ont dû l'ajouter aussi au point d'entrée HTTP, d'autres l'avaient déjà sans résultat. - Ce qui n'est pas établi — aucune de ces pistes n'est « la » cause : testez-les sur votre installation avant de conclure.
Origine fermée : le certificat par DNS-01 avec lego
Les défis tls-alpn-01 et http-01 exigent que l'autorité joigne votre proxy depuis Internet, ce qui s'oppose à une origine fermée aux plages d'un CDN ou à un filtrage géographique. Le défi dns-01 lève cette contrainte : il prouve la maîtrise du domaine par un enregistrement TXT temporaire, créé via l'API de votre fournisseur DNS. Le proxy Netbird ne l'intègre pas nativement : la demande (ticket #5450, ouvert le 25 février 2026) est toujours ouverte. La documentation Netbird décrit en revanche un montage « private proxy with lego » : lego obtient et renouvelle un certificat wildcard par DNS-01, un hook de déploiement le dépose dans un volume partagé, et le proxy le recharge à chaud après chaque renouvellement.
Points d'attention du montage lego
- Variables du proxy —
NB_PROXY_WILDCARD_CERT_DIRdésigne le dossier des certificats wildcard,NB_PROXY_PRIVATE=trueactive les écoutes privées sur le mesh. Épinglez une image du proxy qui connaît ces deux variables. - Le mode privé ne ferme rien seul —
NB_PROXY_PRIVATEne désactive pas l'écoute ordinaire du proxy. Retirez vous-même les ports publiés et les routes d'entrée (labels Traefik, Caddy, nginx) qui mènent au conteneur. - Portée du wildcard —
*.proxy.votre-domaine.comne couvre qu'un niveau de sous-domaine. Un nom hors couverture retombe sur l'ACME intégré, qui ne peut pas aboutir si le proxy est entièrement privé. - Exposition du certificat — le wildcard et le domaine de base apparaissent dans les journaux Certificate Transparency, pas les noms des services qu'il couvre ; une clé wildcard compromise touche en revanche tous ces noms.
- Exploitation — utilisez le serveur ACME de production de Let's Encrypt (ses certificats de test ne sont pas reconnus), sauvegardez les volumes de lego et des certificats, et épinglez des versions testées de lego et du proxy.
Ce que le mesh change pour la gestion de parc
Un réseau mesh Netbird auto-hébergé remplace trois couches que vous maintenez à la main : la distribution de clés WireGuard, les règles UFW entre serveurs et la documentation des accès croisés. Chaque nouveau VPS client s'enrôle en une commande ; chaque révocation est immédiate et centralisée. Le SSH public peut se fermer, et netbird expose publie à la demande un service sans ouvrir de port.
L'isolation par groupe vous permet de grandir sans risque de collision : les serveurs de deux clients différents ne peuvent pas se voir, même s'ils tournent sur la même infrastructure. Et le plan de contrôle reste sous votre maîtrise : aucune dépendance à un SaaS tiers, aucune limite de sièges, aucun abonnement par nœud.
Pour aller plus loin, couchez le provisionnement des nœuds dans Ansible (voir notre guide Ansible) : l'installation du client, la commande netbird up et les règles UFW sur wt0 deviennent des tâches idempotentes dans un rôle réutilisable.