boma/roles/netbird_coordinator
2026-06-19 17:15:33 +02:00
..
defaults feat(netbird_coordinator): disable geolocation so no-egress startup can't FATAL the control plane 2026-06-19 17:15:33 +02:00
handlers feat(netbird): coordinator service role (combined server + dashboard, v0.72.4) 2026-06-15 17:49:57 +02:00
meta docs(netbird): service-role standard files (SECURITY/VERIFY/ACCESS/BACKUP) 2026-06-15 18:01:29 +02:00
molecule/default feat(netbird_coordinator): disable geolocation so no-egress startup can't FATAL the control plane 2026-06-19 17:15:33 +02:00
tasks fix(reverse_proxy,netbird_coordinator): create scaffold dirs in check mode 2026-06-17 17:49:47 +02:00
templates feat(netbird_coordinator): disable geolocation so no-egress startup can't FATAL the control plane 2026-06-19 17:15:33 +02:00
ACCESS.md docs(netbird): service-role standard files (SECURITY/VERIFY/ACCESS/BACKUP) 2026-06-15 18:01:29 +02:00
BACKUP.md docs(netbird): service-role standard files (SECURITY/VERIFY/ACCESS/BACKUP) 2026-06-15 18:01:29 +02:00
README.md feat(netbird_coordinator): disable geolocation so no-egress startup can't FATAL the control plane 2026-06-19 17:15:33 +02:00
SECURITY.md docs(netbird): describe gRPC routing as the deployed Content-Type matcher 2026-06-16 07:54:09 +02:00
VERIFY.md docs(netbird): service-role standard files (SECURITY/VERIFY/ACCESS/BACKUP) 2026-06-15 18:01:29 +02:00

netbird_coordinator

Self-hosted NetBird coordinator — the mesh-VPN control plane (ADR-016). Runs on askari (the off-site Hetzner host) and is the rendezvous point every NetBird peer talks to. Deployed via Docker Compose (ADR-004), behind the Caddy reverse proxy.

Architecture — combined server

NetBird's self-hosted stack is now a single combined server image plus a separate dashboard UI — there is no longer a separate signal / relay / coturn / dex container, and no turnserver.conf / management.json / openid-configuration.json.

Container Image Role
netbird-server netbirdio/netbird-server Management API + Signal + Relay + STUN + embedded Dex IdP (/oauth2), all on one process. Config at /etc/netbird/config.yaml. State in the netbird_data volume (SQLite).
netbird-dashboard netbirdio/dashboard Web UI. Configured purely by environment (dashboard.env); a public PKCE OIDC client, so its client secret is intentionally empty.

Both containers join the existing external boma Docker network (created by the reverse_proxy role's compose) so Caddy reaches them by container name. The only host-exposed port is 3478/udp (STUN); HTTP/gRPC/WS traffic enters via Caddy over the boma network, not via host ports.

Reverse-proxy routing (added separately — M4a Caddy)

This role does not add the Caddy route. The route is a separate task and must front several upstreams on netbird-server over the boma network, all to the same backend:

  • Native gRPC (signal + management) — matched by Content-Type: application/grpc* (not by path) → h2c://netbird-server:80
  • HTTP + WebSocket — paths /relay*, /ws-proxy/*, /api/*, /oauth2/*netbird-server:80
  • Dashboard catch-all — /*netbird-dashboard:80

This matches NetBird's own external-proxy Caddy example: gRPC (the /management.ManagementService/* + /signalexchange.SignalExchange/* services) is selected by content-type rather than enumerated by path. gRPC needs HTTP/2 (h2c) upstream support; WS/gRPC need long timeouts (Caddy sets none by default).

Variables — netbird_coordinator__*

Variable Default Description
netbird_coordinator__server_image netbirdio/netbird-server:0.72.4 Combined server image (pinned; never latest)
netbird_coordinator__dashboard_image netbirdio/dashboard:v2.39.0 Dashboard image (versioned independently of the server)
netbird_coordinator__base_dir /opt/services/netbird Working directory for the Compose project
netbird_coordinator__domain netbird.askari.wingu.me Public hostname; feeds exposedAddress, the OIDC issuer, redirect URIs, and the dashboard endpoints
netbird_coordinator__trusted_proxies ["172.16.0.0/12"] Source ranges NetBird trusts X-Forwarded-* from (server.reverseProxy.trustedHTTPProxies). Must cover Caddy's source IP on the boma network — verify the actual bridge subnet at deploy
netbird_coordinator__manage true Set false in Molecule to render templates without a Docker daemon
netbird_coordinator__disable_geolocation true sets NB_DISABLE_GEOLOCATION so a no-egress startup can't FATAL the server on the GeoLite2 download (FRICTION 2026-06-17 #4)

Production overrides live in inventories/production/group_vars/.

Secrets

Two secrets come from the vault and are rendered into the host-side config.yaml (mode 0640, no_log); they never touch the work tree or the dashboard:

  • vault.netbird.auth_secretserver.authSecret
  • vault.netbird.datastore_keyserver.store.encryptionKey (base64; keep the padding)

The dashboard's OIDC client is a public PKCE client, so AUTH_CLIENT_SECRET is intentionally empty — dashboard.env carries no secrets.

netbird_coordinator__manage toggle

Docker operations (docker compose up, the restart handler) are gated on netbird_coordinator__manage | bool. Molecule sets it false so the role can be tested (template rendering, directory creation) without a Docker daemon.