NetBird mesh

Mesh networking for the homelab. This page reached you through it.

Built with
NetBird, WireGuard, NetBird Kubernetes operator, Let's Encrypt, Traefik
  • A self-hosted NetBird mesh connects everything, behind a reverse proxy.
  • A place to play with network policy and mesh networking.

How this page reached you

  1. Public DNS points at the house.
  2. The NetBird edge ends TLS, with a Let’s Encrypt certificate issued automatically for each hostname.
  3. The request crosses the WireGuard mesh into the Kubernetes cluster.
  4. Traefik routes it by hostname to the service for this site.

The cluster isn’t exposed to the LAN or the internet directly. Its only way in is through the mesh.

In the cluster

  • The NetBird Kubernetes operator creates setup keys, network resources and DNS records from Kubernetes objects.
  • Two routing peers run on different workers as a failover group. I added the second after a node-failure test where one router took Gitea down for six and a half minutes.
  • All HTTP traffic goes through one mesh resource in front of Traefik, so a new app only needs a route and a proxy entry. Non-HTTP services like Postgres get their own resource and policy.
  • I moved services onto the mesh one hostname at a time with no downtime, then removed the old LAN ingress.

Policy

  • Access is granted per resource group through NetBird policies, not by being on the right network.
  • About 25 hostnames across three domains go through the proxy, some public like this one and some behind single sign-on.

Gotchas worth knowing

  • The operator fails silently if the NetBird DNS zone it needs doesn’t exist, or if its namespace doesn’t allow privileged pods.
  • As shipped, the chart had no CPU request and one-second probe timeouts, and it crash-looped for over a week. Setting a CPU request and five-second timeouts fixed it.
  • The reverse-proxy API has no PATCH. Update by reading the whole object, editing it and writing it back, or you lose settings you didn’t touch.
  • Let’s Encrypt allows only five failed validations per hostname per hour, so a proxy entry goes in only once the backend already serves.