# 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. ```bash 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: ```bash 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=` in the ignored `netbird/.env` file. 3. Set `NETBIRD_CLIENT_HOSTNAME` to this machine's desired peer name. 4. Start and inspect the client: ```bash 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: ```bash docker compose -f client.compose.yaml down ``` ## Operations Inspect status and logs: ```bash docker compose ps docker compose logs --tail=200 netbird-server dashboard ``` Stop or remove containers without deleting data: ```bash 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: ```bash 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: ```bash 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.