NetBird mit Docker installieren: Zero-Trust-Overlay-Netzwerk selbst hosten (WireGuard + SSO)
NetBird 0.82.0 mit dem kombinierten netbird-server per Docker Compose selbst hosten: config.yaml, Dashboard und Traefik verstehen, erstes Konto sicher anlegen, Clients verbinden, Backup und Rücksicherung.
Geprüft am 30.10.2026 · für NetBird 0.82.0, NetBird Dashboard 2.96.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

NetBird verbindet Rechner, Server und Netze über WireGuard zu einem privaten Overlay-Netz, gesteuert über einen zentralen Management-Dienst mit Weboberfläche, Benutzerverwaltung und Zugriffsrichtlinien. Seit Version 0.65 hat sich der selbst gehostete Aufbau grundlegend vereinfacht: Management, Signal, Relay und STUN laufen in einem einzigen Container netbirdio/netbird-server mit einer gemeinsamen config.yaml. Coturn, management.json und turnserver.conf entfallen, die Anmeldung übernimmt ein eingebauter Identitätsanbieter. Diese Anleitung baut den Server mit NetBird 0.82.0 so auf, wie ihn das offizielle Schnellstart-Skript erzeugt, aber Datei für Datei und damit nachvollziehbar.
Voraussetzungen
- Server: Linux-VM mit mindestens 1 CPU und 2 GB RAM (Herstellerangabe), x86_64 oder ARM64. Für Updates, Logs und Reserven sind 2 Kerne und 4 GB ein komfortabler Rahmen.
- Netz: öffentlich erreichbar auf TCP 80 und 443 sowie UDP 3478 (STUN). Ein Anbieter mit zustandsloser Firewall braucht laut Doku zusätzlich den lokalen Portbereich für UDP.
- Domain: ein A-Record, etwa
netbird.firma.de, der auf die öffentliche IP zeigt, bevor Sie starten. - Software: Docker mit Compose-Plugin v2,
curl,openssl. - Eigener Server oder freie Ports: Der Stack bringt Traefik mit und belegt 80 und 443. Läuft dort schon ein Reverse Proxy, nutzen Sie die Variante aus Schritt 7.
Docker installieren Sie nach Docker und Docker Compose auf Linux installieren.
Schritt 1: Aufbau und Dateien verstehen
Der Hersteller empfiehlt für neue Installationen das Skript getting-started.sh, das die Dateien interaktiv erzeugt. Es tut nichts, was Sie nicht auch selbst tun können, und schreibt drei Dateien. Wer sie kennt, kann Konfiguration und Updates gezielt steuern.
| Dienst | Image (Stand 28.10.2026) | Ports | Aufgabe |
|---|---|---|---|
| traefik | traefik:v3.6 | 80, 443 | TLS per Let’s Encrypt, Routing für gRPC, WebSocket, HTTP |
| dashboard | netbirdio/dashboard:v2.96.0 | intern 80 | Weboberfläche |
| netbird-server | netbirdio/netbird-server:0.82.0 | intern 80, 3478/udp | Management, Signal, Relay, STUN, eingebauter IdP |
Die Daten (SQLite-Datenbank, Schlüssel) liegen im Volume netbird_data unter /var/lib/netbird. Das Skript verwendet für Dashboard und Server latest; hier sind die aktuellen Versionen festgeschrieben, damit ein Neustart nicht ungeplant aktualisiert.
mkdir -p /opt/netbird && cd /opt/netbird
Verifizieren: dig +short netbird.firma.de liefert die öffentliche IP des Servers, und ss -tlnp | grep -E ':(80|443) ' zeigt keinen Dienst auf diesen Ports.
Schritt 2: config.yaml erstellen
Erzeugen Sie drei Geheimnisse. Die Doku weist darauf hin, dass der Datenbankschlüssel sein Base64-Padding (=) behalten muss; der Relay-Schlüssel darf es verlieren.
openssl rand -base64 32 | sed 's/=//g' # authSecret
openssl rand -base64 32 # sessionCookieEncryptionKey
openssl rand -base64 32 # store.encryptionKey
Die config.yaml entspricht dem, was das Skript für den Betrieb mit eingebautem Traefik schreibt. Ersetzen Sie Domain und Platzhalter:
server:
listenAddress: ":80"
exposedAddress: "https://netbird.firma.de:443"
stunPorts:
- 3478
metricsPort: 9090
healthcheckAddress: ":9000"
logLevel: "info"
logFile: "console"
authSecret: "RELAY_SECRET"
dataDir: "/var/lib/netbird"
auth:
issuer: "https://netbird.firma.de/oauth2"
signKeyRefreshEnabled: true
sessionCookieEncryptionKey: "COOKIE_KEY"
dashboardRedirectURIs:
- "https://netbird.firma.de/nb-auth"
- "https://netbird.firma.de/nb-silent-auth"
cliRedirectURIs:
- "http://localhost:53000/"
reverseProxy:
trustedHTTPProxies:
- "172.30.0.10/32"
trustedPeers:
- "172.30.0.10/32"
store:
engine: "sqlite"
encryptionKey: "STORE_KEY"
172.30.0.10 ist die feste Adresse, die Traefik im Compose-Netz erhält. Nur von dort übernimmt der Server die weitergereichte Client-IP. Die Datei enthält Geheimnisse; das Skript legt sie mit Rechten 600 an.
chmod 600 config.yaml
Verifizieren: grep -c 'firma.de' config.yaml liefert 4, grep -c -E 'RELAY_SECRET|COOKIE_KEY|STORE_KEY' config.yaml liefert 0.
Schritt 3: dashboard.env und docker-compose.yml
NETBIRD_MGMT_API_ENDPOINT=https://netbird.firma.de
NETBIRD_MGMT_GRPC_API_ENDPOINT=https://netbird.firma.de
AUTH_AUDIENCE=netbird-dashboard
AUTH_CLIENT_ID=netbird-dashboard
AUTH_CLIENT_SECRET=
AUTH_AUTHORITY=https://netbird.firma.de/oauth2
USE_AUTH0=false
AUTH_SUPPORTED_SCOPES=openid profile email groups
AUTH_REDIRECT_URI=/nb-auth
AUTH_SILENT_REDIRECT_URI=/nb-silent-auth
NGINX_SSL_PORT=443
LETSENCRYPT_DOMAIN=none
Die Compose-Datei folgt der Vorlage des Skripts für die Option „Traefik (recommended)“, gekürzt um Protokolloptionen. Traefik liest die Docker-Labels über den Socket nur lesend:
services:
traefik:
image: traefik:v3.6
restart: unless-stopped
networks:
netbird:
ipv4_address: 172.30.0.10
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=netbird"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.allowACMEByPass=true"
- "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0"
- "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=0"
- "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=0"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.email=admin@firma.de"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--serverstransport.forwardingtimeouts.responseheadertimeout=0s"
- "--serverstransport.forwardingtimeouts.idleconntimeout=0s"
ports:
- "443:443"
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- netbird_traefik_letsencrypt:/letsencrypt
dashboard:
image: netbirdio/dashboard:v2.96.0
restart: unless-stopped
networks: [netbird]
env_file: ./dashboard.env
labels:
- traefik.enable=true
- traefik.http.routers.netbird-dashboard.rule=Host(`netbird.firma.de`)
- traefik.http.routers.netbird-dashboard.entrypoints=websecure
- traefik.http.routers.netbird-dashboard.tls.certresolver=letsencrypt
- traefik.http.routers.netbird-dashboard.priority=1
- traefik.http.services.dashboard.loadbalancer.server.port=80
netbird-server:
image: netbirdio/netbird-server:0.82.0
restart: unless-stopped
networks: [netbird]
ports:
- "3478:3478/udp"
volumes:
- netbird_data:/var/lib/netbird
- ./config.yaml:/etc/netbird/config.yaml
command: ["--config", "/etc/netbird/config.yaml"]
labels:
- traefik.enable=true
- traefik.http.routers.netbird-grpc.rule=Host(`netbird.firma.de`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))
- traefik.http.routers.netbird-grpc.entrypoints=websecure
- traefik.http.routers.netbird-grpc.tls.certresolver=letsencrypt
- traefik.http.routers.netbird-grpc.service=netbird-server-h2c
- traefik.http.routers.netbird-grpc.priority=100
- traefik.http.routers.netbird-backend.rule=Host(`netbird.firma.de`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`))
- traefik.http.routers.netbird-backend.entrypoints=websecure
- traefik.http.routers.netbird-backend.tls.certresolver=letsencrypt
- traefik.http.routers.netbird-backend.service=netbird-server
- traefik.http.routers.netbird-backend.priority=100
- traefik.http.services.netbird-server.loadbalancer.server.port=80
- traefik.http.services.netbird-server-h2c.loadbalancer.server.port=80
- traefik.http.services.netbird-server-h2c.loadbalancer.server.scheme=h2c
volumes:
netbird_data:
netbird_traefik_letsencrypt:
networks:
netbird:
ipam:
config:
- subnet: 172.30.0.0/24
gateway: 172.30.0.1
Die Zeitlimits auf 0 sind kein Versehen: gRPC-Verbindungen zwischen Clients und Server bleiben dauerhaft offen und dürfen nicht nach einer Minute getrennt werden.
Verifizieren: docker compose config -q gibt nichts aus. docker compose config --images listet genau die drei Images mit festen Tags.
Schritt 4: Starten und erstes Konto anlegen
docker compose up -d
docker compose logs -f netbird-server
Der Server startet ohne Benutzer. Im Test meldete das Log unter anderem management server version 0.80.0, STUN server listening on [::]:3478 und Relay WebSocket multiplexed on management port, der Server-Container belegte etwa 90 MB RAM. Öffnen Sie https://netbird.firma.de; Sie landen auf /setup und legen dort mit E-Mail, Name und Passwort das Owner-Konto an. Solange noch kein Konto existiert, meldet /api/instance den Wert {"setup_required":true}, und die Einrichtungs-API nimmt Anfragen ohne Anmeldung an. Wer die Seite zuerst aufruft, wird Owner. Erledigen Sie die Einrichtung deshalb direkt nach dem Start. Im Test lehnte die API jeden weiteren Versuch danach mit HTTP 412 und setup already completed ab.
Verifizieren: docker compose ps zeigt alle drei Dienste als Up. curl -s https://netbird.firma.de/oauth2/.well-known/openid-configuration liefert JSON mit "issuer":"https://netbird.firma.de/oauth2". Nach der Kontoerstellung liefert curl -s https://netbird.firma.de/api/instance den Wert {"setup_required":false}, und /api/users ohne Token antwortet mit 401.
Schritt 5: Ersten Client verbinden
Installieren Sie den NetBird-Client auf einem Rechner (Pakete für Windows, macOS und Linux auf netbird.io) und verbinden Sie ihn mit Ihrem Server:
netbird up --management-url https://netbird.firma.de
Der Client öffnet den Browser zur Anmeldung beim eingebauten Identitätsanbieter. Für Server ohne Browser legen Sie im Dashboard unter Setup Keys einen Schlüssel an und verwenden netbird up --management-url https://netbird.firma.de --setup-key SCHLÜSSEL. Weitere Benutzer legen Sie im Dashboard an oder binden einen externen Anbieter per OIDC ein, etwa Microsoft Entra ID oder Keycloak.
Verifizieren: netbird status auf dem Client zeigt Management: Connected und Signal: Connected; im Dashboard erscheint der Rechner unter Peers. Ein zweiter Client ist per ping auf seine NetBird-IP (Bereich 100.64.0.0/10) erreichbar.
Schritt 6: Zugriffsrichtlinien festlegen
Eine neue Installation enthält eine Standardrichtlinie, die allen Peers gegenseitigen Zugriff erlaubt. Für ein Zero-Trust-Modell ersetzen Sie sie unter Access Control > Policies durch gezielte Regeln: Legen Sie Gruppen an (etwa „Admins“, „Server“, „Mitarbeiter“), weisen Sie Peers zu und erlauben Sie nur die nötigen Verbindungen, zum Beispiel „Admins zu Server, TCP 22“. Deaktivieren Sie die Standardrichtlinie erst, wenn Ihre eigenen Regeln greifen, sonst trennen Sie laufende Verbindungen.
Verifizieren: Nach dem Deaktivieren der Standardrichtlinie erreicht ein Peer aus „Mitarbeiter“ den Server nicht mehr per SSH, ein Peer aus „Admins“ weiterhin.
Schritt 7: Variante mit vorhandenem Reverse Proxy
Belegt bereits ein Proxy die Ports 80 und 443, lassen Sie Traefik weg und veröffentlichen Dashboard und Server nur lokal. Das Skript verwendet dafür die Ports 8080 (Dashboard) und 8081 (Server) auf 127.0.0.1:
dashboard:
ports:
- "127.0.0.1:8080:80"
netbird-server:
ports:
- "127.0.0.1:8081:80"
- "3478:3478/udp"
Der Proxy muss HTTP/2 und gRPC beherrschen. Für einen auf dem Host installierten Caddy lautet der offizielle Block:
netbird.firma.de {
@grpc header Content-Type application/grpc*
reverse_proxy @grpc h2c://127.0.0.1:8081
@backend path /relay* /ws-proxy/* /api/* /oauth2/*
reverse_proxy @backend 127.0.0.1:8081
reverse_proxy /* 127.0.0.1:8080
}
Tragen Sie dann unter reverseProxy in der config.yaml die Adresse Ihres Proxys ein, hier 127.0.0.1/32. Bleibt trustedPeers leer, vertraut der Server laut Skript weitergereichten Client-IPs von jeder Quelle. Vorlagen für Nginx und Nginx Proxy Manager stehen in der Hersteller-Doku „External Reverse Proxy“.
Verifizieren: curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/ liefert 200, und netbird status auf einem Client meldet nach dem Verbinden Management: Connected.
Schritt 8: Backup und Update
Verliert der Server sein Datenverzeichnis, müssen laut Doku alle Clients neu registriert werden. Sichern Sie deshalb die Konfigurationsdateien und das Volume, bei gestoppten Diensten für eine konsistente Datenbank:
docker compose stop
mkdir -p backup
cp docker-compose.yml config.yaml dashboard.env backup/
docker compose cp -a netbird-server:/var/lib/netbird/ backup/
docker compose start
tar czf netbird-backup-$(date +%F).tar.gz backup/
Für ein Update lesen Sie die Release Notes von Server und Dashboard, sichern, heben die Tags in der Compose-Datei an und führen docker compose pull netbird-server dashboard sowie docker compose up -d aus. Das Dashboard zeigt verfügbare Updates unten in der Navigation an. Am 16.10.2026 lief der Wechsel von netbird-server:0.80.0 und dashboard:v2.94.0 auf 0.81.1 und v2.95.0 mit unveränderter config.yaml: Dashboard und OpenID-Konfiguration antworteten über Traefik mit HTTP 200, /api/users ohne Anmeldung mit 401, das Log meldete STUN auf UDP 3478 und den eingebauten Relay. 0.81 bringt unter anderem Posture-Checks mit Gerätezertifikaten, die Management und Client ab 0.81.0 voraussetzen, und das Werkzeug nblink für Weiterleitungen ohne Root-Rechte. Aktualisieren Sie die Clients deshalb zeitnah mit, wenn Sie die neuen Prüfungen nutzen wollen. Am 28.10.2026 folgte der Wechsel auf netbird-server:0.81.5 und dashboard:v2.96.0, ebenfalls mit unveränderter config.yaml. Laut Release Notes bringen 0.81.2 bis 0.81.5 vor allem Korrekturen im Client (SSH-Sitzungen, Signal-Verbindung), ein aktualisiertes quic-go und entfernen die kompromittierten eMudhra-Zertifizierungsstellen aus dem Vertrauensspeicher; das Dashboard v2.96.0 hebt Next.js auf 16.4 und Tailwind CSS auf v4. Neue Pflichtvariablen nennen die Release Notes nicht. Im Test mit der Compose-Datei aus Schritt 3 meldete das Log management server version 0.81.5, /api/instance lieferte weiter setup_required:false, ein erneuter Einrichtungsversuch bekam 412, das Dashboard antwortete über Traefik mit 200 und /api/users ohne Anmeldung mit 401.
Am 30.10.2026 folgte der Wechsel auf netbird-server:0.82.0; das Dashboard bleibt auf v2.96.0. Laut Release Notes entfernt 0.82.0 die schon in 0.81.0 abgekündigte Speicher-Engine MySQL: Ein Management-Server mit engine: mysql startet nicht mehr. Wer MySQL nutzt, migriert noch unter 0.81.x mit dem Werkzeug netbird-mysql-migrate aus dem Release v0.81.5 auf PostgreSQL und aktualisiert erst danach; die SQLite-Konfiguration dieser Anleitung ist nicht betroffen. Neu sind eine eigene Datenbank für die Zugriffsprotokolle des Reverse Proxy (unter SQLite als Datei access_logs.db im Datenordner, ohne Änderung an der config.yaml), eine latenzabhängige Auswahl von Routing-Peers in HA-Gruppen und netbird status --table im Client; die Unterstützung für Rosenpass gilt als abgekündigt. Im Test des Updates von 0.81.5 auf 0.82.0 mit der Compose-Datei aus Schritt 3 meldete das Log management server version 0.82.0, STUN und Relay starteten, /api/instance lieferte weiter setup_required:false, ein erneuter Einrichtungsversuch bekam 412, das Dashboard antwortete über Traefik mit 200 und /api/users ohne Anmeldung mit 401.
Zum Zurückspielen erstellen Sie die Container neu, kopieren die Daten in den gestoppten Server und starten:
docker compose create
docker compose cp backup/netbird/. netbird-server:/var/lib/netbird/
docker compose start
Verifizieren: tar tzf netbird-backup-*.tar.gz listet backup/config.yaml und backup/netbird/ mit store.db und idp.db. Im Test lief genau diese Folge mit gelöschtem Volume: Nach dem Zurückspielen meldete /api/instance wieder setup_required:false, das Owner-Konto war erhalten.
Grundlagen zum Betrieb finden Sie in Docker Compose absichern: Secrets, Healthchecks, Non-Root, Linux-Server absichern mit UFW und Fail2ban und Docker-Container automatisch aktualisieren: Diun, WUD und Renovate im Vergleich.
Typische Fehler
- Kein Zertifikat, Browser meldet Traefik-Standardzertifikat: A-Record zeigt noch nicht auf den Server oder Port 443 ist gesperrt.
docker compose logs traefiknennt den Grund der ACME-Anfrage. - Clients verbinden sich, Peers erreichen sich aber nicht direkt: UDP 3478 ist gesperrt. Ohne STUN laufen Verbindungen nur über das Relay.
- Anmeldung im Dashboard endet in einer Schleife: Domain in
config.yaml(issuer,dashboardRedirectURIs) unddashboard.envstimmen nicht überein. - gRPC-Verbindungen brechen nach kurzer Zeit ab: Der eigene Proxy begrenzt Lese- oder Leerlaufzeiten. Timeouts wie in Schritt 3 aufheben.
- Admin-Passwort vergessen: Laut Doku über die API mit dem Token eines anderen Admins einen neuen Benutzer anlegen oder die Datenbank zurücksetzen.
Häufige Fragen
Ich habe noch den alten Aufbau mit Coturn. Was tun?
Für Installationen vor v0.65.0 mit eingebautem IdP bietet der Hersteller ein Migrationsskript an, das den Fünf-Container-Aufbau in den kombinierten Server überführt. Mit externem Identitätsanbieter bricht es ab; dann steht zuerst die Umstellung auf den eingebauten IdP an. Die Doku „Migration Guide: Combined Container Setup“ beschreibt beide Wege.
Brauche ich einen externen Identitätsanbieter?
Nein. Der eingebaute IdP auf Basis von Dex verwaltet lokale Benutzer. Externe Anbieter lassen sich zusätzlich unter Settings > Identity Providers einbinden.
Wie unterscheidet sich NetBird von Headscale?
Headscale ist ein Steuerserver für die Tailscale-Clients, NetBird ein eigenständiges Gesamtsystem mit eigenen Clients, Dashboard, Relay und Richtlinien. Den Vergleichsweg zeigt Headscale selbst hosten.
Kann ich PostgreSQL statt SQLite verwenden?
Ja, über store.engine: postgres und store.dsn in der config.yaml. SQLite unterstützt laut Doku keine parallelen Schreibzugriffe und keine mehreren Management-Instanzen, reicht für kleine Installationen aber aus.
Fazit
Mit dem kombinierten netbird-server ist ein selbst gehostetes NetBird deutlich übersichtlicher geworden: drei Container, drei Dateien, ein Datenvolume. Wer die vom Skript erzeugten Dateien versteht, kann Versionen festschreiben, den eigenen Reverse Proxy nutzen und Backups gezielt planen. Legen Sie das erste Konto sofort nach dem Start an, ersetzen Sie die offene Standardrichtlinie durch Gruppenregeln und sichern Sie config.yaml zusammen mit netbird_data, denn ohne beides müssen alle Geräte neu verbunden werden.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren
- Headscale selbst hosten
- WireGuard-VPN für Homeoffice und Standortvernetzung
- Zero Trust und Netzwerksegmentierung
- NetBird: Self-Hosting Quickstart Guide
- NetBird: Configuration Files Reference
- NetBird: External Reverse Proxy Configuration
- NetBird: Backup and Restore
- NetBird: Migration to Combined Container
- GitHub: NetBird v0.82.0


