7.6 KiB
NetBird (control plane)
NetBird is an open-source, self-hostable WireGuard-based overlay network. This cookbook deploys the control plane as separate containers, following the advanced (multi-container) self-hosted layout, so it can be pointed at an external OpenID Connect identity provider.
Components:
| Container | Image | Role |
|---|---|---|
netbird-management |
netbirdio/management |
Management API + gRPC (peer configuration, store) |
netbird-signal |
netbirdio/signal |
Signal exchange (peer connection setup) |
netbird-relay |
netbirdio/relay |
Relay for peers that cannot connect directly |
netbird-dashboard |
netbirdio/dashboard |
Web UI (SPA) |
netbird-coturn |
coturn/coturn |
STUN/TURN server (NAT traversal) |
Scope. This cookbook is the control plane only. The NetBird agent (peers, routing peers) is out of scope. It does not bundle an identity provider: the management service and the dashboard point at an external OIDC provider that the operator configures.
Prerequisites
Dependencies (installed automatically)
postgresql— the management store. Theother/postgresql/netbird.sqlhook creates thenetbirddatabase and user. PostgreSQL handles major upgrades and backups.traefik— TLS termination and HTTP/gRPC/WebSocket ingress on port 443. Theother/traefik/netbird.yamlhook wires the routing. NetBird must not obtain its own certificates (LETSENCRYPT_DOMAIN=none); Traefik owns TLS.
The keycloak cookbook of this repository is not a dependency (that would force it onto
every install). It is one valid way to provide the external OIDC provider — see below.
virtiofs mounts
/var/lib/virtiofs/data/netbird— precious data (the management data directory, which holds key material peers pin). The management store itself lives in PostgreSQL.
Resources (control plane, small deployment)
- CPU: 2 vCPU.
- RAM: ~1 GiB for the NetBird containers (PostgreSQL and Traefik are extra; budget ~2 GiB total on the VM).
- Disk: a few hundred MiB for images; the store grows with the number of peers (tens of MiB for hundreds of peers). coturn relays media through RAM/network, not disk.
Files the operator must provide
All live under /etc/quadlets/netbird/. Working-but-insecure examples ship under
config/examples/ (installed by make install and by the dev VM, not part of the
production package). netbird.target and each unit are gated with ConditionPathExists, so a
VM built from the ignition file before these are pushed stays idle instead of crash-looping.
| File | Mode / owner | Contents |
|---|---|---|
management.json |
0640 netbird:itix-svc |
Management config: OIDC, STUN/TURN, relay, signal, data-store encryption key, store engine. Keystone that gates the target. |
management.env |
0600 root:root |
NETBIRD_STORE_ENGINE_POSTGRES_DSN (PostgreSQL DSN incl. DB password). |
dashboard.env |
0600 root:root |
Dashboard OIDC client + NETBIRD_MGMT_API_ENDPOINT + LETSENCRYPT_DOMAIN=none. |
relay.env |
0600 root:root |
NB_LISTEN_ADDRESS, NB_EXPOSED_ADDRESS, NB_AUTH_SECRET. |
turnserver.conf |
0640 netbird:itix-svc |
coturn config: TURN user/password, realm, and (behind NAT) external-ip. |
Values that must stay consistent across files:
- DB password:
management.envDSN ↔other/postgresql/netbird.sql. - TURN password:
turnserver.confuser=self:<pw>↔management.jsonTURNConfig.Turns[].Password. - Relay secret:
relay.envNB_AUTH_SECRET↔management.jsonRelay.Secret. - Data-store encryption key:
management.jsonDataStoreEncryptionKey— 32 bytes, base64-encoded (openssl rand -base64 32). Losing or changing it makes encrypted store fields unreadable; treat it as precious.
OIDC settings the operator must fill in
The control plane needs an external OIDC provider. The keycloak cookbook of this repository
is one way to provide it (create a realm, a public PKCE client for the dashboard, and — if you
want NetBird to manage users in the IdP — a confidential client); or point at any provider you
run elsewhere. Fill in:
management.json→HttpConfig:AuthIssuer(issuer URL),AuthAudience,AuthKeysLocation(JWKS URL), andOIDCConfigEndpoint(the provider's.well-known/openid-configuration).management.json→PKCEAuthorizationFlow.ProviderConfig:ClientID,Audience,AuthorizationEndpoint,TokenEndpoint,Scope,RedirectURLs.dashboard.env:AUTH_AUTHORITY,AUTH_CLIENT_ID,AUTH_AUDIENCE,AUTH_SUPPORTED_SCOPES(andAUTH_CLIENT_SECRETif your client is confidential).
The shipped example leaves
OIDCConfigEndpointempty on purpose: management then boots without contacting a provider (JWKS is fetched lazily), which keepsmake installand the tests self-sufficient. In production setOIDCConfigEndpoint(or at least a correctAuthKeysLocation) to your provider, otherwise tokens cannot be validated.
IdpManagerConfig.ManagerTypeisnonein the example. Set it (e.g.keycloak) with IdP admin client credentials only if you want NetBird to manage users in the provider.
Ports
The consumer maintains the Internet-facing firewall by hand. Only these must be reachable from the Internet:
| Port | Proto | Where | Why |
|---|---|---|---|
| 443 | TCP | Traefik | Dashboard, management API + gRPC, signal gRPC, relay — all multiplexed on one host name (NetBird 0.29+ shares 443 via HTTP/2; Traefik dispatches by path prefix, gRPC over h2c). |
| 3478 | UDP (and TCP) | coturn | STUN/TURN. Cannot go through an HTTP reverse proxy. |
| 49152–65535 | UDP | coturn | TURN relayed-media port range. Cannot go through an HTTP reverse proxy. Keep in sync with min-port/max-port in turnserver.conf. |
Backend ports used behind Traefik / on the host only (must not be exposed to the Internet):
| Port | Proto | Service | Bind |
|---|---|---|---|
| 8080 | TCP | dashboard (nginx) | 127.0.0.1 only (published from the container namespace) |
| 33073 | TCP | management (HTTP API + gRPC, h2c) | host |
| 10000 | TCP | signal (gRPC + WebSocket proxy, h2c) | host |
| 33080 | TCP | relay (WebSocket) | host |
| 9090 / 9092 / 9093 | TCP | management / signal / relay Prometheus metrics | host |
| 9000 | TCP | relay health check | host |
UID and GID
Runs as UID 10035 (netbird) / GID 10000 (itix-svc).
management,signal,relayrun as10035:10000under host networking.coturnruns as10035:10000under host networking withCAP_NET_BIND_SERVICE(to bind the privileged port 3478), the same pattern as thetraefikcookbook.dashboarduses the upstream rootful image, which serves the SPA with nginx on port 80 and ships no configurable HTTP port. Rather than fork it, it runs in its own network namespace (not host networking) so nginx keeps port 80 inside the container, and the port is published only on127.0.0.1:8080for Traefik. The rootful image is mapped to the non-root host UID10035viaUIDMap/GIDMap(the same approach as thenextcloudcookbook), so no process runs as root on the host.
Notes
- The management schema is migrated by the management process itself on start, so there is no separate init/migration unit.
- coturn behind NAT: set
external-ipinturnserver.confso relayed addresses are correct. - TLS on coturn (5349) is disabled; NetBird uses STUN/TURN over 3478 with long-term credentials. Enabling it would require certificates this cookbook does not manage.