Files

NetBird

Self-hosted NetBird with the combined management, signal, relay, and STUN server. The dashboard and server run behind the repository's existing external Traefik instance on the Docker proxy network. Only STUN UDP 3478 is published directly.

The deployment uses SQLite for a single-instance homelab server. The persistent netbird_data volume and the datastore encryption key are both required to recover the installation.

Files

  • compose.yaml: dashboard and combined server; selected by the marker-driven deploy workflow through active.
  • config.template.yaml: non-secret server configuration rendered at startup.
  • entrypoint.sh: injects Docker secrets into an in-memory runtime configuration.
  • client.compose.yaml: optional host-network peer using a dashboard-generated setup key.
  • .env: ignored local hostnames, the detected Traefik Docker-network subnet, and optional client setup key.
  • secrets/: ignored relay secret and datastore encryption key.

First deployment

Run these commands on the Docker host before merging the activating branch. The deploy preflight resets tracked files but preserves ignored local state.

cd /srv/homelab/netbird
./setup.sh
$EDITOR .env
docker compose config --quiet
docker compose up -d

Review the values in .env before starting. The example public hostname is netbird.forust.xyz; change it if a different public domain was selected. setup.sh replaces NETBIRD_PROXY_SUBNET=auto with the first IPv4 subnet of the external Docker proxy network. Keep that value synchronized with the network; set an explicit CIDR instead if the network is managed elsewhere.

setup.sh is idempotent and never replaces existing secrets. Do not delete or regenerate secrets/datastore-encryption-key after the first successful start unless all encrypted setup keys and API tokens are intentionally being invalidated.

Network prerequisites

  • Point the public hostname directly to the Docker host. Do not proxy UDP 3478 through Cloudflare or another CDN.
  • Allow inbound TCP 80, TCP 443, and UDP 3478 through the host firewall and upstream router.
  • Ensure the external proxy Docker network exists and Traefik uses its websecure entrypoint and letsencrypt resolver. NETBIRD_PROXY_SUBNET must describe that network; it is used to trust only forwarded client addresses from Traefik.
  • Ensure the internal names in .env resolve where the local and development aliases are needed.
  • Keep Traefik's websecure read timeout disabled for long-lived gRPC and WebSocket sessions. This repository configures --entrypoints.websecure.transport.respondingTimeouts.readTimeout=0 in traefik/compose.yaml.

After startup, verify OIDC discovery through the public TLS endpoint:

curl -fsS "https://${NETBIRD_DOMAIN}/oauth2/.well-known/openid-configuration"

Open https://${NETBIRD_DOMAIN} immediately and complete the initial owner setup. Treat the initial setup flow as public until the owner exists.

Optional host client

The client intentionally lives in a separate Compose project. Normal server deploys use --remove-orphans, so keeping the client in the server project would cause it to be removed.

  1. Create a reusable or ephemeral setup key in the NetBird dashboard.
  2. Put NB_SETUP_KEY=<key> in the ignored netbird/.env file.
  3. Set NETBIRD_CLIENT_HOSTNAME to this machine's desired peer name.
  4. Start and inspect the client:
cd /srv/homelab/netbird
docker compose -f client.compose.yaml config --quiet
docker compose -f client.compose.yaml up -d
docker compose -f client.compose.yaml exec netbird-client netbird status

The client uses host networking and requires NET_ADMIN, SYS_ADMIN, SYS_RESOURCE, and /dev/net/tun. Remove it without affecting the server stack:

docker compose -f client.compose.yaml down

Operations

Inspect status and logs:

docker compose ps
docker compose logs --tail=200 netbird-server dashboard

Stop or remove containers without deleting data:

docker compose down

Do not add -v to docker compose down; it would delete the NetBird datastore.

Backup and restore

Back up both the persistent volume and the ignored secret files. For a consistent SQLite backup, briefly stop the server first and store the resulting archive and datastore-encryption-key in an encrypted backup:

cd /srv/homelab/netbird
mkdir -p backups
docker compose stop netbird-server
docker run --rm \
  -v netbird_data:/data:ro \
  -v "$PWD/backups:/backup" \
  busybox:1.37.0 \
  tar -C /data -czf "/backup/netbird-data-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" .
docker compose start netbird-server

Also securely back up:

  • secrets/datastore-encryption-key — required to decrypt stored secrets.
  • secrets/relay-auth-secret — keeps issued relay credentials valid across restoration.
  • netbird/.env — optional, but it records the public and internal hostnames.

Test a restore in an isolated Docker host before relying on a backup.

Upgrade

  1. Take and verify a backup.
  2. Review NetBird release notes for server, client, and dashboard compatibility.
  3. Update the pinned tags in compose.yaml; update client.compose.yaml separately when deploying the client.
  4. Pull and recreate the selected services:
docker compose pull
docker compose up -d

The image tags are intentionally pinned instead of using latest, matching this repository's pull-on-deploy policy.