AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Netbird Server

skill-aidas-dev-k8s-agent-skills-netbird-server · by Aidas-dev

Self-hosted NetBird control plane on Kubernetes — the netbirdio/netbird-server combined management/signal/relay/STUN server + dashboard. Covers the helmforge/netbird chart, config.yaml, PostgreSQL store, embedded Dex IdP, gRPC/HTTP routing, STUN, and ESO secret delivery.

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add skill-aidas-dev-k8s-agent-skills-netbird-server

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-aidas-dev-k8s-agent-skills-netbird-server)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Netbird Server? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

netbird-server — Self-Hosted Control Plane

> Scope: This skill documents the helmforge/netbird chart (repo.helmforge.dev, helmforge/netbird) deployment of the self-hosted control plane — the pattern used by the production talos-stack deployment. NetBird also ships an official self-hosted setup (docker-compose with optional Traefik); if you're following that path, the components are the same but the chart-specific values (database subchart, existingSecret keys, GRPCRoute wiring) don't apply. For the docker-compose variant see docs.netbird.io/selfhosted.

For the client agent see netbird-cli. For Terraform provider see netbird-terraform. For the K8s operator see netbird-operator (the operator consumes an EXISTING control plane — it does not deploy one).

What You're Deploying

The control plane is a single combined server (netbirdio/netbird-server) running management API, gRPC, signal, relay, metrics, health, and embedded STUN in one container, plus a separate dashboard (netbirdio/dashboard).

| Component | Image | Role | |-----------|-------|------| | server | netbirdio/netbird-server | management + signal + relay + STUN + embedded IdP (Dex) | | dashboard | netbirdio/dashboard | web UI (embedded nginx) |

Configured by a single config.yaml (replaces old management.json + relay.env). All services listen on HTTP inside the cluster; TLS terminates at your ingress/gateway — the proxy MUST support HTTP/2 / gRPC (h2c).

Ports to Expose

| Port | Proto | Purpose | |------|-------|---------| | 443 | TCP | dashboard, API, gRPC, signal, relay (via ingress/gateway) | | 3478 | UDP | STUN (NAT traversal — CANNOT be proxied by HTTP reverse proxies) |

Install (helmforge/netbird chart)

helm repo add helmforge https://repo.helmforge.dev
helm upgrade --install netbird helmforge/netbird \
  --namespace netbird --create-namespace

Minimal production values:

server:
  publicUrl: https://netbird.example.com
  replicaCount: 1          # sqlite requires 1; postgres can scale
  auth:
    issuer: https://netbird.example.com/oauth2   # embedded IdP issuer
  config:
    existingSecret: netbird-config    # full config.yaml from secret
    existingSecretKey: config.yaml
  service:
    annotations:
      external-dns.alpha.kubernetes.io/hostname: netbird.example.com

dashboard:
  replicaCount: 1

database:
  mode: external            # auto | external | sqlite
  external:
    engine: postgres        # postgres | mysql
    host: netbird-db-rw.netbird.svc
    port: 5432
    name: netbird
    username: netbird
    existingSecret: netbird-db
    existingSecretPasswordKey: password   # CNPG uses `password`, chart default is `database-password`

postgresql:
  enabled: false            # disable bundled subchart when database.mode=external

Database modes

| Mode | Use | Notes | |------|-----|-------| | auto (default) | bundled HelmForge PostgreSQL subchart | production default | | external | your own DB (CNPG, managed PG) | engine: postgres or mysql | | sqlite | lab/disposable | MUST keep server.replicaCount: 1 |

Production = PostgreSQL (concurrent access, HA). The DSN lives in the config secret, not in values.

config.yaml delivery (ESO)

Chart renders a default config.yaml from values, OR use server.config.existingSecret for the full upstream config. Sensitive parts (authSecret, sessionCookieEncryptionKey, store DSN + encryptionKey, owner email/password, IdP seed) belong in an external secret store:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: netbird-config
  namespace: netbird
spec:
  secretStoreRef: { name: vault, kind: ClusterSecretStore }
  target: { name: netbird-config, creationPolicy: Owner }
  data:
    - secretKey: config.yaml
      remoteRef: { key: kv/apps/netbird/config, property: config.yaml }
    - secretKey: idp-seed
      remoteRef: { key: kv/apps/netbird/config, property: idp-seed }

Key config.yaml fields:

server:
  listenAddress: ":443"
  exposedAddress: "https://netbird.example.com:443"   # public address peers use
  stunPorts: [3478]                                    # or `stuns:` for external
  authSecret: "..."                                    # required for local relay
  dataDir: "/var/lib/netbird/"
  auth:
    issuer: "https://netbird.example.com/oauth2"
    sessionCookieEncryptionKey: ""      # 16/24/32 bytes or base64
    dashboardRedirectURIs: ["https://app.example.com/nb-auth"]
    cliRedirectURIs: ["http://localhost:53000/"]
    owner: { email: "...", password: "..." }   # initial admin
  store:
    engine: "postgres"                   # sqlite | postgres | mysql
    dsn: "..."                           # from secret
    encryptionKey: ""                    # from secret

Routing (Cilium Gateway / any Gateway API)

gRPC must be routed via GRPCRoute (or an h2c backend). Plain HTTPRoute forwarding 404s GetServerKey — match by RPC service name (see netbirdio/netbird#5542):

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: netbird-rpc
  namespace: netbird
spec:
  parentRefs:
    - name: cilium-gateway
      kind: Gateway
      namespace: cilium-gateway
      sectionName: https
  hostnames: [netbird.example.com]
  rules:
    - matches:
        - { method: { service: management.ManagementService } }
        - { method: { service: signalexchange.SignalExchange } }
      backendRefs: [{ name: netbird-server, port: 80 }]

HTTP paths on the server: /api, /oauth2, /relay, /ws-proxy. Dashboard UI is everything else:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: netbird
  namespace: netbird
spec:
  parentRefs:
    - name: cilium-gateway
      kind: Gateway
      namespace: cilium-gateway
      sectionName: https
  hostnames: [netbird.example.com]
  rules:
    - matches:
        - { path: { type: PathPrefix, value: /api } }
        - { path: { type: PathPrefix, value: /oauth2 } }
        - { path: { type: PathPrefix, value: /relay } }
        - { path: { type: PathPrefix, value: /ws-proxy } }
      backendRefs: [{ name: netbird-server, port: 80 }]
    - backendRefs: [{ name: netbird-dashboard, port: 80 }]

Identity (embedded Dex + external IdP)

  • Embedded IdP (Dex) is always enabled and serves /oauth2/* (OIDC discovery at {issuer}/.well-known/openid-configuration).
  • Default dashboard auth matches the embedded issuer: clientId: netbird-dashboard, audience: netbird-dashboard, scopes openid profile email groups, redirects /nb-auth + /nb-silent-auth.
  • External IdP (ZITADEL, Keycloak, Okta): register a web app with PKCE (public client, auth_method_type NONE), redirect URI = the Dex connector callback https:///oauth2/callback. Seed the Dex connector into the embedded IdP at startup via IDP_SEED_INFO env (base64 dex.Connector JSON from a secret).
  • Dashboard + server must share one public hostname when using the embedded provider. With an external OIDC provider, override ALL dashboard auth values (AUTH_AUTHORITY, AUTH_CLIENT_ID, AUTH_AUDIENCE, AUTH_SUPPORTED_SCOPES).

Terraform + Operator Integration

  • Manage account topology (groups, peers, routes, networks, policies, setup keys, DNS) via the netbird-terraform skill, pointing management_url at the in-cluster API (https://netbird.example.com or netbird-server.netbird.svc:80 from inside the cluster).
  • The netbird-operator (separate chart netbirdio/helm-charts) connects to this control plane via managementURL + a service-user PAT (NB_API_KEY). It does NOT deploy the server.

Common Mistakes

  • Plain HTTPRoute for gRPCGetServerKey 404s. Use GRPCRoute matched by RPC service name (management.ManagementService, signalexchange.SignalExchange) or an h2c backend.
  • Missing UDP 3478 — STUN can't be HTTP-proxied. P2P NAT traversal breaks → all peers relay.
  • existingSecretPasswordKey mismatch — CNPG initdb secret uses key password; chart default expects database-password. Set the override or the server can't reach the DB.
  • Forgetting postgresql.enabled: false — bundled subchart conflicts with database.mode: external (chart validate).
  • sqlite with replicaCount > 1 — SQLite store is single-instance; scale only on PostgreSQL.
  • IdP connector type immutability — after creation the NetBird API rejects type/schema changes on the identity provider. Delete+recreate (import afterwards in Terraform) rather than edit.
  • Dashboard external OIDC partial override — mixing embedded defaults with an external IdP breaks login. Override all AUTH_* values.
  • disableGeoliteUpdate: false + air-gapped — server pod downloads GeoLite DBs at boot; health endpoint can be slow to open. Leave disabled unless the cluster allows egress.
  • STUN deferred on Cilium — Cilium UDP listener support for the STUN Service may be unproven; verify UDP Service exposure before relying on P2P.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.