Add Reverse Proxy documentation and update self-hosted quickstart (#594)

- Add Reverse Proxy docs: overview, custom domains, authentication, access logs
- Add Reverse Proxy section to sidebar navigation
- Update self-hosted quickstart for new getting-started.sh (Traefik default, combined server)
This commit is contained in:
shuuri-labs
2026-02-13 19:07:01 +01:00
committed by GitHub
parent 320e9fdcaf
commit 432602e35e
23 changed files with 2031 additions and 634 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -81,6 +81,10 @@ NetBird supports any OIDC-compliant identity providers. Here are some popular pr
your NetBird installation and will never be exposed to the public.
</Note>
<Note>
If you plan to use the [Reverse Proxy](/manage/reverse-proxy) SSO feature with an external identity provider, you also need to register the reverse proxy callback URL with your IdP. See [Configure SSO for external identity providers](/selfhosted/migration/enable-reverse-proxy#configure-sso-for-external-identity-providers) for details.
</Note>
<p>
<img src="/docs-static/img/selfhosted/identity-providers/add-idp.png" alt="Identity Providers List" className="imagewrapper-big"/>
</p>

View File

@@ -5,13 +5,28 @@ To back up your NetBird installation, you need to copy the configuration files a
The configuration files are located in the folder where you ran [the installation script](/selfhosted/selfhosted-quickstart#installation-script). To back up, copy the files to a backup location:
```bash
mkdir backup
cp docker-compose.yml Caddyfile dashboard.env management.json relay.env backup/
cp docker-compose.yml dashboard.env config.yaml backup/
```
<Note>
For detailed information about each configuration file and its options, see the [Configuration Files Reference](/selfhosted/configuration-files).
</Note>
To save the Management service databases, stop the Management service and copy the files from the store directory:
To save the server databases, stop the server and copy the files from the data directory:
```bash
docker compose stop netbird-server
docker compose cp -a netbird-server:/var/lib/netbird/ backup/
docker compose start netbird-server
```
## Older Setup (Separate Containers)
If your deployment uses the older setup with separate containers (`management`, `signal`, `relay`, `coturn`), back up the configuration files:
```bash
mkdir backup
cp docker-compose.yml Caddyfile dashboard.env management.json relay.env backup/
```
To save the Management service database:
```bash
docker compose stop management
docker compose cp -a management:/var/lib/netbird/ backup/

View File

@@ -5,7 +5,7 @@ To remove the NetBird installation and all related data from your server, run th
# remove all NetBird-related containers and volumes (data)
docker compose down --volumes
# remove downloaded and generated config files
rm -f docker-compose.yml Caddyfile dashboard.env management.json relay.env
rm -f docker-compose.yml dashboard.env config.yaml nginx-netbird.conf caddyfile-netbird.txt npm-advanced-config.txt
```
## Get In Touch

View File

@@ -0,0 +1,124 @@
# Migrate from SQLite to PostgreSQL
import {Note, Warning} from "@/components/mdx";
This guide is part of the [Splitting Your Self-Hosted Deployment](/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment) guide. It covers migrating your Management server database from SQLite to PostgreSQL.
The default NetBird deployment uses SQLite, which stores all data in a single file. This works well for smaller setups, but you may want to migrate to PostgreSQL if:
- You want the database on a separate, dedicated machine
- You need better concurrency handling for larger deployments
- You prefer the operational tooling and backup options that PostgreSQL provides
For smaller teams, SQLite is perfectly capable and migration is not required.
## Set Up PostgreSQL
If you don't already have a PostgreSQL instance, you can run one in Docker:
```bash
docker run -d \
--name postgres-server \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
postgres:16
```
<Warning>
Replace the default `password` with a strong, unique password for production deployments.
</Warning>
## Back Up the SQLite Store
Before migrating, create a backup of your SQLite database:
```bash
mkdir backup
docker compose cp -a netbird-server:/var/lib/netbird/. backup/
```
## Install pgloader
The migration uses [pgloader](https://github.com/dimitri/pgloader) to transfer data from SQLite to PostgreSQL:
```bash
# Debian/Ubuntu
sudo apt-get install pgloader
# macOS
brew install pgloader
```
## Create the Migration File
Create a file called `sqlite.load` with the following content:
```
LOAD DATABASE
FROM sqlite:///root/combined/backup/store.db
INTO postgresql://postgres:password@localhost:5432/postgres
WITH include drop, create tables, create indexes, reset sequences
CAST
column accounts.is_domain_primary_account to boolean,
column accounts.settings_peer_login_expiration_enabled to boolean,
column accounts.settings_peer_inactivity_expiration_enabled to boolean,
column accounts.settings_regular_users_view_blocked to boolean,
column accounts.settings_groups_propagation_enabled to boolean,
column accounts.settings_jwt_groups_enabled to boolean,
column accounts.settings_routing_peer_dns_resolution_enabled to boolean,
column accounts.settings_extra_peer_approval_enabled to boolean,
column accounts.settings_extra_user_approval_required to boolean,
column accounts.settings_lazy_connection_enabled to boolean
;
```
<Note>
Update the SQLite path and PostgreSQL connection string to match your environment.
</Note>
## Run the Migration
```bash
pgloader sqlite.load
```
## Update config.yaml
On your main server, update the `store` section in `config.yaml` to use PostgreSQL:
```yaml
server:
# ... existing settings ...
store:
engine: "postgres"
dsn: "host=postgres-server user=postgres password=password dbname=postgres port=5432"
```
Alternatively, you can pass the connection string as an environment variable instead of putting it in the config file:
```yaml
netbird-server:
environment:
- NETBIRD_STORE_ENGINE_POSTGRES_DSN=host=postgres-server user=postgres password=password dbname=postgres port=5432
```
## Restart and Verify
```bash
docker compose up -d
```
Check the logs to confirm PostgreSQL is being used:
```bash
docker compose logs netbird-server | grep store
```
You should see:
```
using Postgres store engine
```

View File

@@ -0,0 +1,96 @@
# Splitting Your Self-Hosted Deployment
import {Note, Warning} from "@/components/mdx";
This guide explains how to split your NetBird self-hosted deployment from a single-server setup into a distributed architecture for better reliability and performance.
The most common approach is extracting the relay service (with its embedded STUN server) to separate servers and moving the PostgreSQL database to a dedicated machine.
In most cases, you won't need to extract the Signal server, but for completeness, this guide covers that as well.
NetBird clients can tolerate a Management server outage as long as connections are already established through relays or peer-to-peer.
This makes a stable relay infrastructure especially important.
This guide assumes you have already [deployed a single-server NetBird](/selfhosted/selfhosted-quickstart) and have a working configuration.
<Note>
If you are looking for a high-availability setup for the Management and Signal services, this is available through an enterprise
commercial license [here](https://netbird.io/pricing#on-prem).
</Note>
## Architecture Overview
The default single-server deployment runs all services on one machine: **Traefik** (reverse proxy), **Dashboard** (web UI),
and a **combined netbird-server** container that includes Management, Signal, and Relay + STUN as components. Traefik handles TLS termination on ports 80/443, while STUN listens on UDP port 3478. The Management server uses a **SQLite** database by default.
After splitting, the **main server** keeps Traefik, Dashboard, Management, and optionally Signal.
The **relay servers** run independently on different machines, each handling relay (port 443) and STUN (port 3478) traffic. Peers receive relay addresses from the Management server and connect to them directly. Optionally, the SQLite database can be migrated to **PostgreSQL** on a dedicated server, and Signal can also be extracted to its own machine.
## Guides
- [Set Up External Relay Servers](/selfhosted/maintenance/scaling/set-up-external-relays) — Deploy relay and STUN servers on separate machines and configure the main server to use them
- [Migrate from SQLite to PostgreSQL](/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql) — Move the Management database to a dedicated PostgreSQL instance (optional)
- [Set Up External Signal Server](/selfhosted/maintenance/scaling/set-up-external-signal) — Extract the Signal server to its own machine (optional)
## Configuration Reference
### Relay Server Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `NB_LISTEN_ADDRESS` | Yes | Address to listen on (e.g., `:443`) |
| `NB_EXPOSED_ADDRESS` | Yes | Public relay URL (`rels://` for TLS, `rel://` for plain) |
| `NB_AUTH_SECRET` | Yes | Shared authentication secret |
| `NB_ENABLE_STUN` | No | Enable embedded STUN server (`true`/`false`) |
| `NB_STUN_PORTS` | No | STUN UDP port(s), default `3478` |
| `NB_LETSENCRYPT_DOMAINS` | No | Domain(s) for automatic Let's Encrypt certificates |
| `NB_LETSENCRYPT_EMAIL` | No | Email for Let's Encrypt notifications |
| `NB_TLS_CERT_FILE` | No | Path to TLS certificate (alternative to Let's Encrypt) |
| `NB_TLS_KEY_FILE` | No | Path to TLS private key |
| `NB_LOG_LEVEL` | No | Log level: `debug`, `info`, `warn`, `error` |
### Main Server config.yaml - External Services
```yaml
server:
# External STUN servers
stuns:
- uri: "stun:hostname:port"
proto: "udp" # or "tcp"
# External relay servers
relays:
addresses:
- "rels://hostname:port" # TLS
- "rel://hostname:port" # Plain (not recommended)
secret: "shared-secret"
credentialsTTL: "24h" # How long relay credentials are valid
# External signal server (optional, usually keep embedded)
# signalUri: "https://signal.example.com:443"
```
## Troubleshooting
### Peers Can't Connect via Relay
1. **Check secrets match**: The `authSecret`/`NB_AUTH_SECRET` must be identical everywhere
2. **Check firewall**: Ensure port 443/tcp is open on relay servers
3. **Check TLS**: If using `rels://`, ensure TLS is properly configured
4. **Check logs**: `docker compose logs relay` on the relay server
### STUN Not Working
1. **Check UDP port**: Ensure port 3478/udp is open and not blocked by firewall
2. **Check NAT**: Some carrier-grade NATs block STUN; try a different network
3. **Verify STUN is enabled**: `NB_ENABLE_STUN=true` on relay servers
### Relay Shows as Unavailable
1. **DNS resolution**: Ensure the relay domain resolves correctly
2. **Port reachability**: Test with `nc -zv relay-us.example.com 443`
3. **Certificate issues**: Check Let's Encrypt logs or certificate validity
## See Also
- [Configuration Files Reference](/selfhosted/configuration-files) - Full config.yaml documentation
- [Self-hosting Quickstart](/selfhosted/selfhosted-quickstart) - Initial deployment guide

View File

@@ -0,0 +1,280 @@
# Set Up External Relay Servers
import {Note, Warning} from "@/components/mdx";
This guide is part of the [Splitting Your Self-Hosted Deployment](/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment) guide. It covers deploying external relay and STUN servers and configuring your main server to use them.
For each relay server you want to deploy:
## Server Requirements
- A Linux VM with at least **1 CPU** and **1GB RAM**
- Public IP address
- A domain name pointing to the server (e.g., `relay-us.example.com`)
- Docker installed
- Firewall ports open: **80/tcp** (Let's Encrypt HTTP challenge), **443/tcp** (relay), and **3478/udp** (STUN). If you configure multiple STUN ports, open all of them
## Generate Authentication Secret
All relay servers must share the same authentication secret with your main server. You can generate one with:
```bash
# Generate a secure random secret
openssl rand -base64 32
```
Save this secret - you'll need it for both the relay servers and your main server's config.
## Create Relay Configuration
On your relay server, create a directory and configuration:
```bash
mkdir -p ~/netbird-relay
cd ~/netbird-relay
```
Create `relay.env` with your relay settings. The relay server can automatically obtain and renew TLS certificates via Let's Encrypt:
```bash
NB_LOG_LEVEL=info
NB_LISTEN_ADDRESS=:443
NB_EXPOSED_ADDRESS=rels://relay-us.example.com:443
NB_AUTH_SECRET=your-shared-secret-here
# TLS via Let's Encrypt (automatic certificate provisioning)
NB_LETSENCRYPT_DOMAINS=relay-us.example.com
NB_LETSENCRYPT_EMAIL=admin@example.com
NB_LETSENCRYPT_DATA_DIR=/data/letsencrypt
# Embedded STUN (comma-separated for multiple ports, e.g., 3478,3479)
NB_ENABLE_STUN=true
NB_STUN_PORTS=3478
```
<Note>
Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated.
</Note>
Create `docker-compose.yml`:
```yaml
services:
relay:
image: netbirdio/relay:latest
container_name: netbird-relay
restart: unless-stopped
ports:
- '443:443'
# Expose all ports listed in NB_STUN_PORTS
- '3478:3478/udp'
env_file:
- relay.env
volumes:
- relay_data:/data
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
volumes:
relay_data:
```
## Alternative: TLS with Existing Certificates
If you have existing TLS certificates (e.g., from your own CA or a wildcard cert), replace the Let's Encrypt variables in `relay.env` with:
```bash
# Replace the NB_LETSENCRYPT_* lines with:
NB_TLS_CERT_FILE=/certs/fullchain.pem
NB_TLS_KEY_FILE=/certs/privkey.pem
```
And add a certificate volume to `docker-compose.yml`:
```yaml
volumes:
- /path/to/certs:/certs:ro
- relay_data:/data
```
## Start the Relay Server
```bash
docker compose up -d
```
Verify it's running:
```bash
docker compose logs -f
```
You should see:
```
level=info msg="Starting relay server on :443"
level=info msg="Starting STUN server on port 3478"
```
If you configured Let's Encrypt, the relay generates TLS certificates lazily on the first incoming request. Trigger certificate provisioning and verify it by running:
```bash
curl -v https://relay-us.example.com/
```
A `404 page not found` response is expected — what matters is that the TLS handshake succeeds. Look for a valid Let's Encrypt certificate in the output:
```
* Server certificate:
* subject: CN=relay-us.example.com
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
```
## Repeat for Additional Relay Servers
If deploying multiple relays (e.g., for different regions), repeat the steps above on each server. Use the **same `NB_AUTH_SECRET`** but update the domain name for each.
## Update Main Server Configuration
Now update your main NetBird server to use the external relays instead of the embedded one.
### Edit config.yaml
On your main server, edit the `config.yaml` file:
```bash
cd ~/netbird # or wherever your deployment is
nano config.yaml
```
Remove the `authSecret` from the `server` section and add `relays` and `stuns` sections pointing to your external servers. The presence of the `relays` section disables both the embedded relay and the embedded STUN server, so the `stuns` section is required to provide external STUN addresses:
```yaml
server:
listenAddress: ":80"
exposedAddress: "https://netbird.example.com:443"
# Remove authSecret to disable the embedded relay
# authSecret: ...
# Remove or comment out stunPorts since we're using external STUN
# stunPorts:
# - 3478
metricsPort: 9090
healthcheckAddress: ":9000"
logLevel: "info"
logFile: "console"
dataDir: "/var/lib/netbird"
# External STUN servers (your relay servers)
stuns:
- uri: "stun:relay-us.example.com:3478"
proto: "udp"
- uri: "stun:relay-eu.example.com:3478"
proto: "udp"
# External relay servers
relays:
addresses:
- "rels://relay-us.example.com:443"
- "rels://relay-eu.example.com:443"
secret: "your-shared-secret-here"
credentialsTTL: "24h"
auth:
issuer: "https://netbird.example.com/oauth2"
# ... rest of auth config
```
<Warning>
The `secret` under `relays` and the `NB_AUTH_SECRET` on all relay servers **must be identical**. Mismatched secrets will cause relay connections to fail silently.
</Warning>
### Update docker-compose.yml (Optional)
If your main server was exposing STUN port 3478, you can remove it since STUN is now handled by external relays:
```yaml
netbird-server:
image: netbirdio/netbird-server:latest
container_name: netbird-server
restart: unless-stopped
networks: [netbird]
# Remove the STUN port - no longer needed
# ports:
# - '3478:3478/udp'
volumes:
- netbird_data:/var/lib/netbird
- ./config.yaml:/etc/netbird/config.yaml
command: ["--config", "/etc/netbird/config.yaml"]
```
### Restart the Main Server
```bash
docker compose down
docker compose up -d
```
## Verify the Configuration
### Check Main Server Logs
```bash
docker compose logs netbird-server
```
Verify that the embedded relay is disabled and your external relay addresses are listed:
```
INFO combined/cmd/root.go: Management: true (log level: info)
INFO combined/cmd/root.go: Signal: true (log level: info)
INFO combined/cmd/root.go: Relay: false (log level: )
```
```
Relay addresses: [rels://relay-us.example.com:443 rels://relay-eu.example.com:443]
```
### Check Peer Status
Connect a NetBird client and verify that both STUN and relay services are available:
```bash
netbird status -d
```
The output should list your external STUN and relay servers. All configured STUN servers will appear, but only one randomly chosen relay is used per client:
```
Relays:
[stun:relay-us.example.com:3478] is Available
[stun:relay-eu.example.com:3478] is Available
[rels://relay-eu.example.com:443] is Available
```
You can also test failover by stopping one of the relay servers and checking the status again. The client will detect the unavailable server and use the remaining one:
```
Relays:
[stun:relay-us.example.com:3478] is Available
[stun:relay-eu.example.com:3478] is Unavailable, reason: stun request: context deadline exceeded
[rels://relay-us.example.com:443] is Available
```
### Test Relay Connectivity
You can force all peer connections through relay to verify it works end-to-end. On a client, run:
```bash
sudo netbird service reconfigure --service-env NB_FORCE_RELAY=true
```
Then test connectivity to another peer (e.g., with `ping`).
Once confirmed, switch back to normal mode. The client will attempt peer-to-peer connections first and fall back to relay only when direct connectivity isn't possible:
```bash
sudo netbird service reconfigure --service-env NB_FORCE_RELAY=false
```

View File

@@ -0,0 +1,149 @@
# Set Up External Signal Server
import {Note, Warning} from "@/components/mdx";
This guide is part of the [Splitting Your Self-Hosted Deployment](/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment) guide. It covers extracting the Signal server to a dedicated machine.
In most deployments, the embedded Signal server works well and does not need to be extracted. Consider running an external Signal server if you want to separate it from the Management server for organizational or infrastructure reasons.
Unlike relay servers, the Signal server cannot be replicated as it maintains in-memory connection state. If you need high-availability active-active mode for both Management and Signal, this is available through an [enterprise commercial license](https://netbird.io/pricing#on-prem).
<Warning>
Changing the Signal server URL requires all clients to restart. After updating the configuration, each client must run `netbird down` followed by `netbird up` to reconnect to the new Signal server. This limitation will be addressed in a future client release.
</Warning>
## Server Requirements
- A Linux VM with at least **1 CPU** and **1GB RAM**
- Public IP address
- A domain name pointing to the server (e.g., `signal.example.com`)
- Docker installed
- Firewall ports open: **80/tcp** (Let's Encrypt HTTP challenge) and **443/tcp** (gRPC/WebSocket client communication)
## Create Signal Configuration
On your signal server, create a directory and configuration:
```bash
mkdir -p ~/netbird-signal
cd ~/netbird-signal
```
Like the relay, the signal server can automatically obtain TLS certificates via Let's Encrypt.
<Note>
Replace `signal.example.com` with your signal server's domain.
</Note>
Create `signal.env` with your signal settings:
```bash
NB_PORT=443
NB_LOG_LEVEL=info
# TLS via Let's Encrypt (automatic certificate provisioning)
NB_LETSENCRYPT_DOMAIN=signal.example.com
```
Create `docker-compose.yml`:
```yaml
services:
signal:
image: netbirdio/signal:latest
container_name: netbird-signal
restart: unless-stopped
ports:
- '443:443'
- '80:80'
env_file:
- signal.env
volumes:
- signal_data:/var/lib/netbird
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
volumes:
signal_data:
```
## Alternative: TLS with Existing Certificates
If you have existing TLS certificates, replace the Let's Encrypt variable in `signal.env` with:
```bash
# Replace the NB_LETSENCRYPT_DOMAIN line with:
NB_CERT_FILE=/certs/fullchain.pem
NB_CERT_KEY=/certs/privkey.pem
```
And add a certificate volume to `docker-compose.yml`:
```yaml
volumes:
- /path/to/certs:/certs:ro
- signal_data:/var/lib/netbird
```
## Start the Signal Server
```bash
docker compose up -d
```
Verify it's running:
```bash
docker compose logs -f
```
If you configured Let's Encrypt, trigger certificate provisioning with an HTTPS request:
```bash
curl -v https://signal.example.com/
```
Confirm the certificate was issued:
```
* Server certificate:
* subject: CN=signal.example.com
* issuer: C=US; O=Let's Encrypt; CN=E8
* SSL certificate verify ok.
```
## Update Main Server Configuration
On your main server, add `signalUri` to `config.yaml`. This disables the embedded Signal server:
```yaml
server:
# ... existing settings ...
# External signal server
signalUri: "https://signal.example.com:443"
```
Restart the main server:
```bash
docker compose down
docker compose up -d
```
## Verify Signal Extraction
Check the main server logs to confirm the embedded Signal is disabled:
```bash
docker compose logs netbird-server
```
```
INFO combined/cmd/root.go: Management: true (log level: info)
INFO combined/cmd/root.go: Signal: false (log level: )
INFO combined/cmd/root.go: Relay: false (log level: )
```

View File

@@ -46,17 +46,29 @@ To upgrade NetBird to the latest version:
2. Review the release notes (see above) for any breaking changes.
3. Pull the latest NetBird docker images:
```bash
docker compose pull management dashboard signal relay
docker compose pull netbird-server dashboard
```
4. Restart the NetBird containers with the new images:
```bash
docker compose up -d --force-recreate management dashboard signal relay
docker compose up -d --force-recreate netbird-server dashboard
```
<Note>
For upgrades from older versions (pre-v0.26.0), see the [Legacy upgrade notes](#legacy-self-hosting-with-zitadel-idp).
</Note>
### Legacy Setup (Separate Containers)
If your deployment uses the older setup with separate containers (`management`, `signal`, `relay`, `coturn`), pull and recreate those containers instead:
```bash
docker compose pull management dashboard signal relay
```
```bash
docker compose up -d --force-recreate management dashboard signal relay
```
## Get In Touch
Feel free to ping us on [Slack](/slack-url) if you have any questions.

View File

@@ -0,0 +1,318 @@
# Migration Guide: Enable Reverse Proxy Feature
This guide walks you through adding the NetBird Reverse Proxy to an existing self-hosted deployment. By the end, you'll have a `netbird-proxy` container running alongside your existing services, ready to expose internal applications to the public internet.
<Note>
**Who is this guide for?** This migration guide is for existing self-hosted users who:
- Already have a working NetBird deployment (management server, dashboard, signal, relay)
- Want to enable the [Reverse Proxy](/manage/reverse-proxy) feature to expose internal services publicly
- Are running their services via Docker Compose
</Note>
## Why Traefik is required
The NetBird proxy container manages its own TLS certificates (via Let's Encrypt or static files). This means the reverse proxy sitting in front of it **must not terminate TLS** - it needs to pass raw TLS connections through to the proxy container untouched.
This capability is called **TLS passthrough**, and among common reverse proxies, **only Traefik supports it** via its TCP routers. Other reverse proxies (Nginx, Caddy, Nginx Proxy Manager) terminate TLS themselves and cannot forward the raw encrypted connection, which breaks the proxy's certificate management.
If your current deployment uses a reverse proxy other than Traefik, you'll need to switch before enabling this feature. See [Switching to Traefik](/selfhosted/reverse-proxy#traefik) for instructions.
## Overview of changes
### What you're adding
- **`netbird-proxy` container** - a new service in your Docker Compose stack that handles TLS termination, certificate provisioning, and traffic forwarding for reverse proxy services
- **Traefik TCP labels** - routing rules that tell Traefik to pass TLS connections through to the proxy container
- **Wildcard DNS record** - so that all service subdomains (e.g., `myapp.proxy.example.com`) resolve to your server
- **Proxy access token** - generated via the management CLI, used by the proxy to authenticate with the management server
### What stays the same
- Your existing `netbird-server`, `dashboard`, `signal`, and `relay` services are unchanged
- Your `management.json` and other configuration files require no modifications — **unless** you use an external identity provider (not the embedded IdP). See [Configure SSO for external identity providers](#configure-sso-for-external-identity-providers) below.
- Existing peers, networks, and access policies are unaffected
## Prerequisites
Before starting, ensure you have:
- **Traefik** as your reverse proxy (see [Why Traefik is required](#why-traefik-is-required) above)
- **Latest `netbird-server` image** - pull the latest version to ensure the management CLI supports token creation
- **A domain for the proxy** - e.g., `proxy.example.com`. Service subdomains will be created under this domain (e.g., `myapp.proxy.example.com`)
- **Wildcard DNS capability** - ability to create a `*.proxy.example.com` DNS record pointing to your server
<Warning>
The proxy domain **must not** be a subdomain of your NetBird management domain. For example, if your management server is at `netbird.example.com`, do not use `proxy.netbird.example.com`. Use a separate subdomain like `proxy.example.com` instead. Using a subdomain of the management domain causes TLS and routing conflicts between the proxy and management services.
</Warning>
- **Port 443 accessible** - the proxy needs this for ACME TLS-ALPN-01 challenges (certificate provisioning)
## Migration steps
### Step 1: Backup current configuration
```bash
# Create a backup directory
mkdir -p netbird-backup-$(date +%Y%m%d)
cd netbird-backup-$(date +%Y%m%d)
# Backup configuration files
cp ../docker-compose.yml .
cp ../management.json .
cp ../*.env . 2>/dev/null || echo "No .env files found"
```
### Step 2: Generate a proxy access token
The proxy authenticates with the management server using an access token. Generate one using the management CLI:
```bash
docker exec -it netbird-server netbird-mgmt token create --name "my-proxy"
```
This outputs a token in the format `nbx_...` (40 characters). **Save the token immediately** - it is only displayed once. The management server stores only a SHA-256 hash.
You can manage tokens later with:
```bash
# List all tokens
docker exec -it netbird-server netbird-mgmt token list
# Revoke a token by ID
docker exec -it netbird-server netbird-mgmt token revoke <token-id>
```
### Step 3: Add the proxy service to docker-compose.yml
Add the following service to your `docker-compose.yml`. Replace the placeholder values with your actual token and domains:
```yaml
netbird-proxy:
image: netbirdio/netbird-proxy:latest
container_name: netbird-proxy
restart: unless-stopped
networks: [netbird]
environment:
- NB_PROXY_TOKEN=nbx_your_token_here
- NB_PROXY_DOMAIN=proxy.example.com
- NB_PROXY_MANAGEMENT_ADDRESS=https://netbird.example.com:443
- NB_PROXY_ACME_CERTIFICATES=true
labels:
- traefik.enable=true
- traefik.tcp.routers.netbird-proxy.rule=HostSNI(`*.proxy.example.com`)
- traefik.tcp.routers.netbird-proxy.entrypoints=websecure
- traefik.tcp.routers.netbird-proxy.tls.passthrough=true
- traefik.tcp.routers.netbird-proxy.priority=1
- traefik.tcp.services.netbird-proxy.loadbalancer.server.port=8443
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
```
The Traefik labels configure a **TCP router** that:
- Matches any request to `*.proxy.example.com` via SNI (Server Name Indication)
- Uses the `websecure` entrypoint (port 443)
- Passes the TLS connection through **without termination** (`tls.passthrough=true`)
- Uses `priority=1` to avoid intercepting traffic meant for the main NetBird HTTP routers on the same entrypoint
- Forwards traffic to the proxy container on port 8443
<Note>
Replace `proxy.example.com` in both the `NB_PROXY_DOMAIN` environment variable and the Traefik `HostSNI` rule with your actual proxy domain. These must match.
</Note>
### Step 4: Set up wildcard DNS
Create a wildcard DNS record pointing to the server running your NetBird stack:
```
*.proxy.example.com → <your-server-IP>
```
This ensures that all service subdomains (e.g., `myapp.proxy.example.com`, `dashboard.proxy.example.com`) resolve to your server where Traefik forwards them to the proxy container.
### Step 5: Apply changes
```bash
# Pull the new image
docker compose pull netbird-proxy
# Start the proxy alongside existing services
docker compose up -d
# Verify all services are running
docker compose ps
# Check proxy logs
docker compose logs -f netbird-proxy
```
You should see log messages indicating the proxy has connected to the management server and is ready to serve traffic.
### Step 6: Verify in the dashboard
Once the proxy connects to the management server:
1. Open your NetBird dashboard
2. Navigate to **Reverse Proxy** > **Services**
3. Click **Add Service**
4. In the domain selector, you should see your proxy domain (e.g., `proxy.example.com`) with a **Cluster** badge
If the domain appears, the proxy is connected and ready. You can now [create your first service](/manage/reverse-proxy#quick-start).
## Configure SSO for external identity providers
### Who this applies to
This section applies to deployments using a **standalone external identity provider** (Auth0, Okta, Keycloak, Zitadel, etc.) instead of the built-in embedded IdP (Dex). If you deployed using the quickstart script with default settings, you are using the embedded IdP and can skip this section.
### Why this is needed
The reverse proxy SSO feature authenticates users through an OAuth2/OIDC flow that redirects through a callback endpoint on the management server (`/api/reverse-proxy/callback`). The embedded IdP registers this callback automatically, but external IdPs need it configured manually. Without this configuration, SSO authentication on reverse proxy services will silently fail.
### Option A: Quick fix (keep your external IdP)
If you want to keep using your current external identity provider, follow these three steps:
#### Step 1: Add callback URL to management.json
Add the `AuthCallbackURL` field to the `HttpConfig` section of your `management.json`:
```json
"HttpConfig": {
...existing fields...,
"AuthCallbackURL": "https://<your-management-domain>/api/reverse-proxy/callback"
}
```
Replace `<your-management-domain>` with your NetBird management server domain (the same domain used for the dashboard).
#### Step 2: Register callback in your IdP
In your identity provider's application settings, add the following URL as an allowed redirect URI / callback URL:
```
https://<your-management-domain>/api/reverse-proxy/callback
```
This is in addition to any existing redirect URIs (like `/auth` or `/silent-auth`).
Where to find this setting in common providers:
| Provider | Where to add the redirect URI |
|----------|-------------------------------|
| Auth0 | Application > Settings > Allowed Callback URLs |
| Okta | Application > General > Login redirect URIs |
| Keycloak | Client > Settings > Valid redirect URIs |
| Zitadel | Application > Redirect Settings > Redirect URIs |
| Generic OIDC | Refer to your provider's documentation |
#### Step 3: Restart management server
Restart the management service to pick up the configuration change:
```bash
docker compose restart netbird-management
```
### Option B: Migrate to the embedded IdP (recommended)
The embedded IdP (Dex) handles the reverse proxy callback registration automatically — no manual configuration needed. If you want a simpler setup, consider migrating to the embedded IdP.
With the embedded IdP, external identity providers can still be used as **connectors** alongside local authentication. This means your users can continue to sign in with their existing accounts (Google, Okta, Keycloak, etc.) while the embedded IdP manages the OIDC layer.
See the [Identity Providers page](/selfhosted/identity-providers) for instructions on adding external IdPs as connectors.
<Note>
Migrating from a standalone external IdP to the embedded IdP with your IdP as a connector requires user ID migration. See the [Migration Guide](/selfhosted/identity-providers#migration-guide-and-backwards-compatibility) or contact [support@netbird.io](mailto:support@netbird.io) for assistance.
</Note>
### Verification
After configuring SSO for your external identity provider, verify it works:
1. Create a reverse proxy service with **SSO authentication** enabled
2. Open the service URL in an incognito/private browser window
3. Confirm you are redirected to your IdP login page
4. After authenticating, confirm you are redirected back to the service and can access it
If the redirect fails or you see an error from your IdP, double-check that the callback URL is correctly registered in both `management.json` and your identity provider's settings.
## For users not on Traefik
If your self-hosted deployment currently uses Nginx, Caddy, or another reverse proxy, you'll need to switch to Traefik before enabling the Reverse Proxy feature. See the [Traefik setup instructions](/selfhosted/reverse-proxy#traefik) for a step-by-step guide on configuring Traefik for your NetBird deployment.
## Environment variable reference
| Variable | Required | Description | Default |
|----------|----------|-------------|---------|
| `NB_PROXY_TOKEN` | Yes | Access token generated via `netbird-mgmt token create`. The proxy refuses to start without it. | - |
| `NB_PROXY_DOMAIN` | Yes | Base domain for this proxy instance (e.g., `proxy.example.com`). Determines the domain available for services. | - |
| `NB_PROXY_MANAGEMENT_ADDRESS` | No | URL of your NetBird management server. The proxy connects via gRPC to register itself. | `https://api.netbird.io:443` |
| `NB_PROXY_ADDRESS` | No | Address the proxy listens on. | `:8443` (Docker), `:443` (binary) |
| `NB_PROXY_ACME_CERTIFICATES` | No | Set to `true` to enable automatic TLS certificate provisioning via Let's Encrypt. | `false` |
| `NB_PROXY_ACME_CHALLENGE_TYPE` | No | ACME challenge type: `tls-alpn-01` (port 443) or `http-01` (port 80). | `tls-alpn-01` |
| `NB_PROXY_CERTIFICATE_FILE` | No | TLS certificate filename within the certificate directory (for static certificate mode). | `tls.crt` |
| `NB_PROXY_CERTIFICATE_KEY_FILE` | No | TLS private key filename within the certificate directory (for static certificate mode). | `tls.key` |
| `NB_PROXY_CERTIFICATE_DIRECTORY` | No | Directory where static certificate files are stored. | `./certs` |
## Troubleshooting
### Certificate provisioning failures
**Symptom**: Services stay in `certificate_pending` or move to `certificate_failed` status.
**Checklist**:
1. Verify port 443 is accessible from the internet (required for `tls-alpn-01` challenge)
2. Ensure the wildcard DNS record resolves correctly: `dig myapp.proxy.example.com`
3. Check proxy logs for ACME errors: `docker compose logs netbird-proxy | grep -i acme`
4. If using `http-01` challenge type, ensure port 80 is also accessible
### TLS passthrough not working
**Symptom**: The proxy starts but services return TLS errors or Traefik's default certificate.
**Checklist**:
1. Verify Traefik labels include `tls.passthrough=true`
2. Confirm the router is configured as a **TCP** router (not HTTP) - labels should use `traefik.tcp.routers`, not `traefik.http.routers`
3. Check that the `HostSNI` rule matches your proxy domain with the wildcard (`*.proxy.example.com`)
4. Verify the TCP router has `priority=1` to prevent it from intercepting traffic meant for the main NetBird HTTP routers
5. Ensure the `websecure` entrypoint is configured in your Traefik configuration
6. Restart Traefik after adding the proxy container: `docker compose restart traefik`
### Port conflicts
**Symptom**: The proxy container fails to start with an address-in-use error.
**Solution**: The proxy listens on port 8443 inside the container. If another service uses port 8443 on the same Docker network, change `NB_PROXY_ADDRESS` to a different port and update the Traefik label `loadbalancer.server.port` to match.
## Rollback procedure
If you need to remove the proxy and revert to your previous configuration:
```bash
# Stop all services
docker compose down
# Restore your backup
cd netbird-backup-YYYYMMDD
cp docker-compose.yml ../
# Restart without the proxy
cd ..
docker compose up -d
```
You can also revoke the proxy token to prevent the proxy from reconnecting:
```bash
docker exec -it netbird-server netbird-mgmt token list
docker exec -it netbird-server netbird-mgmt token revoke <token-id>
```
## Additional resources
- [Reverse Proxy feature documentation](/manage/reverse-proxy) - full overview of services, targets, domains, and authentication
- [Custom Domains](/manage/reverse-proxy/custom-domains) - use your own domain names for reverse proxy services
- [Reverse Proxy configuration reference](/selfhosted/configuration-files#reverse-proxy-configuration) - all proxy environment variables and options
- [Self-Hosting Quickstart](/selfhosted/selfhosted-quickstart) - getting started with self-hosted NetBird

View File

@@ -1,6 +1,6 @@
# Reverse Proxy Configuration
NetBird includes a built-in Caddy reverse proxy that handles TLS certificates automatically. However, if you already have an existing reverse proxy (Traefik, Nginx, etc.), you can configure NetBird to work with it instead.
NetBird includes a built-in Traefik reverse proxy that handles TLS certificates automatically via Let's Encrypt. However, if you already have an existing reverse proxy (Nginx, Caddy, etc.), you can configure NetBird to work with it instead.
<Note>
Not all reverse proxies are supported as NetBird uses *gRPC* for various components. Your reverse proxy must support HTTP/2 and gRPC proxying.
@@ -14,8 +14,8 @@ The `getting-started.sh` script supports multiple reverse proxy configurations.
```
Which reverse proxy will you use?
[0] Built-in Caddy (recommended - automatic TLS)
[1] Traefik (labels added to containers)
[0] Traefik (recommended - automatic TLS, included in Docker Compose)
[1] Existing Traefik (labels for external Traefik instance)
[2] Nginx (generates config template)
[3] Nginx Proxy Manager (generates config + instructions)
[4] External Caddy (generates Caddyfile snippet)

View File

@@ -201,7 +201,7 @@ docker compose logs dashboard
## Advanced: Running NetBird behind an existing reverse-proxy
If you already have a reverse proxy (Traefik, Nginx, Caddy, etc.), you can configure NetBird to work with it instead of using the built-in Caddy.
If you already have a reverse proxy (Nginx, Caddy, etc.), you can configure NetBird to work with it instead of using the built-in Traefik.
<Note>
Not all reverse proxies are supported as NetBird uses *gRPC* for various components. Your reverse proxy must support HTTP/2 and gRPC proxying.

View File

@@ -13,14 +13,13 @@ This is the quickest way to try self-hosted NetBird. It should take around 5 min
## Infrastructure requirements
- A Linux VM with at least **1CPU** and **2GB** of memory.
- The VM must be publicly accessible on **TCP ports 80 and 443**, and **UDP port 3478**.
- A **public domain** name that resolves to the VMs public IP address.
- A **public domain** name that resolves to the VM's public IP address.
## Software requirements
- Docker with docker-compose plugin ([Docker installation guide](https://docs.docker.com/engine/install/)) or Docker with docker-compose version 2 or higher
- [jq](https://jqlang.github.io/jq/) install with `sudo apt install jq` or `sudo yum install jq`
- [curl](https://curl.se/) install with `sudo apt install curl` or `sudo yum install curl`
Usually available in the official repositories and can be installed with `sudo apt install curl` or `sudo yum install curl`
- [jq](https://jqlang.github.io/jq/) - install with `sudo apt install jq` or `sudo yum install jq`
- [curl](https://curl.se/) - install with `sudo apt install curl` or `sudo yum install curl`
## Installation Script
@@ -30,8 +29,8 @@ Download and run the installation script:
curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash
```
Once finished, you can manage the resources via `docker compose`. The quick start script generates a full, production-ready NetBird installation. If you'd like to customize the install or gain a better understanding of the files
generated by the script, including the docker compose file, please refer to our [Configuration files](/selfhosted/configuration-files) guide.
Once finished, you can manage the resources via `docker compose`. The quick start script generates a full, production-ready NetBird installation. If you'd like to customize the install or gain a better understanding of the files
generated by the script, including the docker compose file, please refer to our [Configuration files](/selfhosted/configuration-files) guide.
### Reverse Proxy Selection
@@ -39,8 +38,8 @@ The script will prompt you to select a reverse proxy option:
```
Which reverse proxy will you use?
[0] Built-in Caddy (recommended - automatic TLS)
[1] Traefik (labels added to containers)
[0] Traefik (recommended - automatic TLS, included in Docker Compose)
[1] Existing Traefik (labels for external Traefik instance)
[2] Nginx (generates config template)
[3] Nginx Proxy Manager (generates config + instructions)
[4] External Caddy (generates Caddyfile snippet)
@@ -49,20 +48,32 @@ Which reverse proxy will you use?
Enter choice [0-5] (default: 0):
```
**For this quickstart guide, select option `[0]` (Built-in Caddy)** - just press Enter to use the default. This option handles TLS certificates automatically via Let's Encrypt and requires no additional configuration.
**For this quickstart guide, select option `[0]` (Traefik)** - just press Enter to use the default. This option includes a Traefik container in the Docker Compose that handles TLS certificates automatically via Let's Encrypt and requires no additional configuration.
<Note>
If you already have a reverse proxy (Traefik, Nginx, etc.) and want to use it instead, the script will guide you through the setup. See the [Reverse Proxy Configuration](/selfhosted/reverse-proxy) guide for detailed instructions on each option.
</Note>
### Generated Files
The script generates the following files:
| File | Description |
|------|-------------|
| `docker-compose.yml` | Docker Compose configuration with all services |
| `config.yaml` | Combined server configuration (management, signal, relay, STUN) |
| `dashboard.env` | Environment variables for the dashboard container |
For options 2-4, additional configuration files are generated (e.g., `nginx-netbird.conf`, `caddyfile-netbird.txt`, or `npm-advanced-config.txt`).
### Example Output
```bash
root@selfhosted-1:~/netbird# bash getting-started.sh
Which reverse proxy will you use?
[0] Built-in Caddy (recommended - automatic TLS)
[1] Traefik (labels added to containers)
[0] Traefik (recommended - automatic TLS, included in Docker Compose)
[1] Existing Traefik (labels for external Traefik instance)
[2] Nginx (generates config template)
[3] Nginx Proxy Manager (generates config + instructions)
[4] External Caddy (generates Caddyfile snippet)
@@ -73,14 +84,14 @@ Rendering initial files...
Starting NetBird services
[+] Running 5/5
✔ Network netbird Created
Container netbird-dashboard Started
Container netbird-management Started
✔ Container netbird-relay Started
✔ Container netbird-signal Started
✔ Container netbird-caddy Started
Waiting for Management server to become ready . . done
[+] up 6/6
✔ Network combined_netbird Created 0.1s
Volume combined_netbird_data Created 0.0s
Volume combined_netbird_traefik_letsencrypt Created 0.0s
✔ Container netbird-server Created 0.1s
✔ Container netbird-traefik Created 0.1s
✔ Container netbird-dashboard Created 0.1s
Waiting for NetBird server to become ready . . . done
Done!
@@ -112,7 +123,7 @@ The `/setup` page is only accessible when no users exist. After creating the fir
## Add More Users
NetBird includes built-in local user management powered by an embedded <a href="https://dexidp.io/" target="_blank" rel="noopener noreferrer">Dex</a> server, allowing you to create and manage users directly from the Dashboard without requiring an external identity provider. You can also add external identity providers for SSO authentication alongside local users.
<Tiles
<Tiles
id="user-management"
items={[
{
@@ -125,9 +136,19 @@ NetBird includes built-in local user management powered by an embedded <a href="
name: 'Identity Providers',
description: 'Connect external identity providers like Google, Microsoft, Okta, or self-hosted IdPs for SSO authentication.',
},
]}
]}
/>
## Enable the Reverse Proxy Feature
The quickstart installation does not include the [Reverse Proxy](/manage/reverse-proxy) feature by default. To enable it, you need to add the `netbird-proxy` container to your deployment and configure a separate proxy domain.
<Warning>
The proxy domain **must not** be a subdomain of your NetBird management domain. For example, if your management server is at `netbird.example.com`, do not use `proxy.netbird.example.com`. Use a separate subdomain like `proxy.example.com` instead. Using a subdomain of the management domain causes TLS and routing conflicts between the proxy and management services.
</Warning>
See the [Enable Reverse Proxy migration guide](/selfhosted/migration/enable-reverse-proxy) for step-by-step instructions on adding the proxy to an existing deployment, including token generation, Docker Compose configuration, and DNS setup.
## Maintenance
Once your NetBird instance is running, refer to these guides for ongoing maintenance:
@@ -148,20 +169,36 @@ Once your NetBird instance is running, refer to these guides for ongoing mainten
]}
/>
<Tiles
id="scaling"
items={[
{
href: '/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment',
name: 'Scaling Your Self-Hosted Deployment',
description: 'Split your NetBird deployment into multiple nodes to scale your deployment.',
},
{
href: '/selfhosted/configuration-files',
name: 'Configuration Files Reference',
description: 'Learn more about the configuration files generated by the quick start script and how to customize them.',
},
]}
/>
---
## Troubleshoot
- **I can't access the `/setup` page**
The setup page is only available when no users exist. If you've already created a user, go to the main login page instead.
- **I forgot my admin password**
You can create a new user via the API using a PAT (Personal Access Token) from an existing admin, or reset the database to start fresh.
- **SSO provider not appearing on login page**
Check that the connector is properly configured in **Settings** → **Identity Providers**. Ensure the redirect URL is correctly configured in your IdP.
For more troubleshooting help, see the [Troubleshooting guide](/selfhosted/troubleshooting).