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 throughactive.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
3478through Cloudflare or another CDN. - Allow inbound TCP
80, TCP443, and UDP3478through the host firewall and upstream router. - Ensure the external
proxyDocker network exists and Traefik uses itswebsecureentrypoint andletsencryptresolver.NETBIRD_PROXY_SUBNETmust describe that network; it is used to trust only forwarded client addresses from Traefik. - Ensure the internal names in
.envresolve where the local and development aliases are needed. - Keep Traefik's
websecureread timeout disabled for long-lived gRPC and WebSocket sessions. This repository configures--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0intraefik/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.
- Create a reusable or ephemeral setup key in the NetBird dashboard.
- Put
NB_SETUP_KEY=<key>in the ignorednetbird/.envfile. - Set
NETBIRD_CLIENT_HOSTNAMEto this machine's desired peer name. - 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
- Take and verify a backup.
- Review NetBird release notes for server, client, and dashboard compatibility.
- Update the pinned tags in
compose.yaml; updateclient.compose.yamlseparately when deploying the client. - 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.