From e180804bdb9587e4198e5a0d3b8f3c5b791d6e94 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 19:47:38 +0100 Subject: [PATCH 01/12] initial draft --- src/components/NavigationDocs.jsx | 1 + src/pages/selfhosted/configuration-files.mdx | 696 +++++------------- .../scaling-your-self-hosted-deployment.mdx | 384 ++++++++++ .../selfhosted/selfhosted-quickstart.mdx | 16 +- 4 files changed, 562 insertions(+), 535 deletions(-) create mode 100644 src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx diff --git a/src/components/NavigationDocs.jsx b/src/components/NavigationDocs.jsx index d11c5cbd..c0c6a18e 100644 --- a/src/components/NavigationDocs.jsx +++ b/src/components/NavigationDocs.jsx @@ -291,6 +291,7 @@ export const docsNavigation = [ isOpen: false, links: [ { title: 'Configuration Files', href: '/selfhosted/configuration-files' }, + { title: 'Scaling Your Deployment', href: '/selfhosted/scaling-your-self-hosted-deployment' }, { title: 'Backup', href: '/selfhosted/maintenance/backup' }, { title: 'Upgrade', href: '/selfhosted/maintenance/upgrade' }, { title: 'Remove', href: '/selfhosted/maintenance/remove' }, diff --git a/src/pages/selfhosted/configuration-files.mdx b/src/pages/selfhosted/configuration-files.mdx index 2457f91d..31c40a92 100644 --- a/src/pages/selfhosted/configuration-files.mdx +++ b/src/pages/selfhosted/configuration-files.mdx @@ -15,9 +15,8 @@ A standard NetBird self-hosted deployment uses the following configuration files | File | Purpose | |------|---------| -| `docker-compose.yml` | Defines all NetBird services (dashboard, management, signal, relay), their Docker images, port mappings, volumes, and startup order. Modify this to change resource limits, add services, or adjust networking. | -| `management.json` | Central configuration for the management server including STUN/relay server addresses, authentication settings, and database configuration. Changes here affect how peers connect and authenticate. | -| `relay.env` | Environment variables for the relay service including the authentication secret, public address, and embedded STUN server settings. The relay secret here must match `management.json`. | +| `docker-compose.yml` | Defines all NetBird services (dashboard, netbird-server), their Docker images, port mappings, volumes, and startup order. Modify this to change resource limits, add services, or adjust networking. | +| `config.yaml` | Central configuration for the NetBird server including listen addresses, authentication settings, STUN ports, and database configuration. Changes here affect how peers connect and authenticate. | | `dashboard.env` | Configures the web dashboard including API endpoints, OAuth2/OIDC settings, and optional SSL settings for standalone deployments without a reverse proxy. | | `Caddyfile` | Configures the built-in Caddy reverse proxy for SSL termination and routing requests to NetBird services. Only present when using the default `getting-started.sh` deployment with Caddy. | @@ -28,8 +27,7 @@ After running the installation script, configuration files are located in the di ``` ./ ├── docker-compose.yml -├── management.json -├── relay.env +├── config.yaml ├── dashboard.env └── Caddyfile # Only when using built-in Caddy ``` @@ -45,9 +43,7 @@ The Docker Compose file defines all NetBird services, their dependencies, networ | Service | Image | Internal Port | External (Exposed) | Description | |---------|-------|---------------|-------------------|-------------| | `dashboard` | `netbirdio/dashboard` | 80 | 8080:80 | The web-based management console where administrators configure networks, manage peers, create access policies, and view activity logs. Includes an embedded nginx server for serving the UI. | -| `management` | `netbirdio/management` | 80 | 8081:80 | The central control plane that handles peer registration, distributes network configurations, manages access policies, and hosts the embedded identity provider. All peers connect to this service on startup. | -| `signal` | `netbirdio/signal` | 80 | 8083:80 | Rendezvous service that facilitates peer connections by enabling peers to exchange connection offers and establish direct WireGuard tunnels. Handles only connection setup metadata, not actual traffic. Uses HTTP/2 protocol via the reverse proxy. | -| `relay` | `netbirdio/relay` | 80, 3478/udp | 8084:80, 3478:3478/udp | Routes encrypted traffic between peers when direct connections fail due to restrictive NATs or firewalls. Also provides embedded STUN on UDP 3478 for NAT type detection. | +| `netbird-server` | `netbirdio/netbird-server` | 80, 3478/udp | 8081:80, 3478:3478/udp | The combined NetBird server that includes management, signal, and relay services in a single container. Also provides embedded STUN on UDP 3478 for NAT type detection. | | `caddy` | `caddy` | 80, 443 | 80:80, 443:443 | Handles TLS termination and routes incoming HTTPS requests to the appropriate NetBird services. Only included in default `getting-started.sh` deployments; can be replaced with your own reverse proxy. | @@ -55,7 +51,7 @@ The Docker Compose file defines all NetBird services, their dependencies, networ -The relay service includes an embedded STUN server, eliminating the need for a separate coturn container. STUN functionality is enabled via the `NB_ENABLE_STUN` environment variable in `relay.env`. +The combined server includes an embedded STUN server, eliminating the need for a separate coturn container. STUN functionality is configured via the `stunPorts` setting in `config.yaml`. ### Default Settings @@ -114,137 +110,23 @@ dashboard: The dashboard service is configured via the `dashboard.env` file. See the [dashboard.env section](#dashboard-env) for the full list of environment variables. When using the built-in Caddy, no ports are exposed directly from the dashboard container; Caddy routes traffic to it internally. -### Management Service +### Combined NetBird Server Service -The management service is the core of NetBird, handling peer registration, authentication, and network coordination. +The combined NetBird server is the core of NetBird, handling peer registration, authentication, signaling, and relay in a single container. **With built-in Caddy (default):** ```yaml -management: - image: netbirdio/management:latest - container_name: netbird-management - restart: unless-stopped - networks: [netbird] - volumes: - - netbird_management:/var/lib/netbird - - ./management.json:/etc/netbird/management.json - command: [ - "--port", "80", - "--log-file", "console", - "--log-level", "info", - "--disable-anonymous-metrics=false", - "--single-account-mode-domain=netbird.selfhosted", - "--dns-domain=netbird.selfhosted", - "--idp-sign-key-refresh-enabled", - ] - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - -**With external reverse proxy (exposed ports):** -```yaml -management: - image: netbirdio/management:latest - container_name: netbird-management - restart: unless-stopped - networks: [netbird] - ports: - - '127.0.0.1:8081:80' - volumes: - - netbird_management:/var/lib/netbird - - ./management.json:/etc/netbird/management.json - command: [ - "--port", "80", - "--log-file", "console", - "--log-level", "info", - "--disable-anonymous-metrics=false", - "--single-account-mode-domain=netbird.selfhosted", - "--dns-domain=netbird.selfhosted", - "--idp-sign-key-refresh-enabled", - ] - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - -To use an external database, add environment variables: -```yaml - environment: - - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://user:password@host:5432/netbird - # Or for MySQL: - # - NETBIRD_STORE_ENGINE_MYSQL_DSN=user:password@tcp(host:3306)/netbird -``` - -#### Command-Line Flags - -| Flag | Default | Description | -|------|---------|-------------| -| `--port` | `80` | The port the management server listens on inside the container. The default deployment uses port 80 internally; TLS is handled by the reverse proxy. | -| `--log-file` | `console` | Where to write log output. Use `console` for Docker logging (recommended) or specify a file path. Logs to console are captured by Docker's logging driver. | -| `--log-level` | `info` | Controls log verbosity. Use `debug` for troubleshooting connection issues, `info` for normal operation, `warn` or `error` for quieter logs in production. | -| `--disable-anonymous-metrics` | `false` | When `true`, stops sending anonymous usage statistics to NetBird. Set to `true` for air-gapped environments or if your security policy prohibits telemetry. | -| `--single-account-mode-domain` | `netbird.selfhosted` | Restricts all users to a single NetBird account associated with this domain. Required for most self-hosted deployments to prevent users from creating separate accounts. | -| `--dns-domain` | `netbird.selfhosted` | The DNS suffix used for peer name resolution within NetBird (e.g., `peer-name.netbird.selfhosted`). Must not conflict with your existing DNS domains. | -| `--idp-sign-key-refresh-enabled` | `false` | Enables automatic refresh of IdP signing keys. Recommended for the embedded IdP to ensure tokens remain valid. | - -### Signal Service - -The signal service acts as a rendezvous service for facilitating peer-to-peer connections. It enables peers to discover each other and exchange connection information needed to establish direct WireGuard tunnels. - -**With built-in Caddy (default):** -```yaml -signal: - image: netbirdio/signal:latest - container_name: netbird-signal - restart: unless-stopped - networks: [netbird] - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - -**With external reverse proxy (exposed ports):** -```yaml -signal: - image: netbirdio/signal:latest - container_name: netbird-signal - restart: unless-stopped - networks: [netbird] - ports: - - '127.0.0.1:8083:80' - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - - -The signal service listens on port 80 internally and uses HTTP/2 protocol. The reverse proxy (Caddy or your own) handles TLS termination and routes signal traffic to this service. - - -### Relay Service - -The relay service is a public service that forwards packets when direct peer-to-peer connections are not possible. It also includes an embedded STUN server for NAT detection and traversal. - -**With built-in Caddy (default):** -```yaml -relay: - image: netbirdio/relay:latest - container_name: netbird-relay +netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server restart: unless-stopped networks: [netbird] ports: - '3478:3478/udp' # Embedded STUN server (must be exposed publicly) - env_file: - - ./relay.env + volumes: + - netbird_data:/var/lib/netbird + - ./config.yaml:/etc/netbird/config.yaml + command: ["--config", "/etc/netbird/config.yaml"] logging: driver: "json-file" options: @@ -254,16 +136,18 @@ relay: **With external reverse proxy (exposed ports):** ```yaml -relay: - image: netbirdio/relay:latest - container_name: netbird-relay +netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server restart: unless-stopped networks: [netbird] ports: - - '127.0.0.1:8084:80' # Relay WebSocket (for reverse proxy) + - '127.0.0.1:8081:80' # HTTP (for reverse proxy) - '3478:3478/udp' # Embedded STUN server (must be exposed publicly) - env_file: - - ./relay.env + volumes: + - netbird_data:/var/lib/netbird + - ./config.yaml:/etc/netbird/config.yaml + command: ["--config", "/etc/netbird/config.yaml"] logging: driver: "json-file" options: @@ -275,385 +159,161 @@ relay: The STUN port (3478/udp) must always be exposed publicly, regardless of reverse proxy configuration. STUN uses UDP for NAT detection and cannot be proxied through HTTP reverse proxies. -The relay service is configured via the `relay.env` file. See the [relay.env section](#relay-env) for detailed configuration options. - - - - Log verbosity level. Options: `debug`, `info`, `warn`, `error`. Default: `info` - - - Address and port to listen on. Format: `:port`. Default: `:80` - - - Public address for peers to connect. Format: `rel://hostname:port` or `rels://hostname:port` for TLS. - - - Shared secret for relay authentication. Must match `management.json` Relay.Secret. - - - Enable the embedded STUN server. Default: `false` - - - Comma-separated list of UDP ports for the STUN server. Default: `3478` - - - -### STUN Server (Embedded in Relay) - - -Starting with the current quickstart installation, STUN functionality is **embedded directly in the relay service**. The separate coturn container is no longer used in the default deployment. This simplifies the architecture and reduces the number of containers to manage. - - -The embedded STUN server is enabled by setting `NB_ENABLE_STUN=true` in `relay.env`. The STUN server listens on UDP port 3478 by default. - -#### Legacy Coturn Configuration - -If you have an existing installation using coturn, or need advanced TURN functionality (such as time-based credentials or TCP relay), you can still use a separate coturn container. See the [advanced self-hosting guide](/selfhosted/selfhosted-guide) for coturn configuration details. +To use an external database, add environment variables: +```yaml + environment: + - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://user:password@host:5432/netbird + # Or for MySQL: + # - NETBIRD_STORE_ENGINE_MYSQL_DSN=user:password@tcp(host:3306)/netbird +``` ### Volume Configuration | Volume | Mount Point | Purpose | |--------|-------------|---------| -| `netbird_management` | `/var/lib/netbird` | Stores the management database (SQLite by default), encryption keys, and persistent state. Back up this volume regularly to preserve your accounts, peers, policies, and setup keys. | +| `netbird_data` | `/var/lib/netbird` | Stores the management database (SQLite by default), encryption keys, and persistent state. Back up this volume regularly to preserve your accounts, peers, policies, and setup keys. | | `netbird_caddy_data` | `/data` | Stores Caddy's TLS certificates and other persistent data. Only used when deploying with the built-in Caddy reverse proxy. Preserve this volume to maintain TLS certificates across restarts. | -The `getting-started.sh` deployment uses only two volumes: `netbird_management` for the management database and `netbird_caddy_data` for Caddy's certificate storage. The signal and relay services do not require persistent volumes in the default configuration. +The `getting-started.sh` deployment uses only two volumes: `netbird_data` for the server database and `netbird_caddy_data` for Caddy's certificate storage. --- -## management.json +## config.yaml -The management configuration file controls the core behavior of the NetBird Management service. This is the most complex configuration file. - -### Authentication - -NetBird comes with built-in local user management and also supports integration with any OIDC-compatible identity provider. -This enables Single Sign-On (SSO), Multi-Factor Authentication (MFA), and centralized user management. -For setup instructions, see the [Authentication & IdPs page](/selfhosted/identity-providers) for configuration details. - -### Configuration Sections - -The `management.json` file for `getting-started.sh` deployments uses these sections: - -| Section | Required | Description | -|---------|----------|-------------| -| `Stuns` | Yes | Lists STUN servers that peers use to discover their public IP address and NAT type. Without working STUN, peers cannot establish direct connections and will always use the relay. | -| `Relay` | Yes | Configures relay server addresses and authentication. Peers use relay servers when direct connections fail. The secret here must match `NB_AUTH_SECRET` in `relay.env`. | -| `Signal` | Yes | Specifies how the management server connects to the signal service. Peers receive this address and use it to exchange connection offers with other peers. | -| `EmbeddedIdP` | Yes | Enables and configures the built-in identity provider (based on DEX). Handles user authentication, token issuance, and OIDC endpoints. Required for `getting-started.sh` deployments. | +The `config.yaml` file is the central configuration for the combined NetBird server. It configures management, signal, relay, and STUN services in a single file. ### Complete Structure -```json -{ - "Stuns": [...], - "Relay": {...}, - "Signal": {...}, - "Datadir": "/var/lib/netbird", - "DataStoreEncryptionKey": "...", - "EmbeddedIdP": { - "Enabled": true, - "Issuer": "https://your-domain/oauth2", - "DashboardRedirectURIs": [...] - } -} +```yaml +server: + listenAddress: ":80" + exposedAddress: "https://netbird.example.com:443" + stunPorts: + - 3478 + metricsPort: 9090 + healthcheckAddress: ":9000" + logLevel: "info" + logFile: "console" + + authSecret: "your-relay-auth-secret" + dataDir: "/var/lib/netbird" + + auth: + enabled: true + issuer: "https://netbird.example.com/oauth2" + signKeyRefreshEnabled: true + storage: + type: "sqlite3" + dashboardRedirectURIs: + - "https://netbird.example.com/nb-auth" + - "https://netbird.example.com/nb-silent-auth" + cliRedirectURIs: + - "http://localhost:53000/" + + store: + engine: "sqlite" + encryptionKey: "your-encryption-key" ``` -### Stuns Section - -Configures STUN servers used for NAT detection and traversal. - -**What does STUN do?** - -STUN (Session Traversal Utilities for NAT) helps NetBird peers discover their public IP address and the type of NAT they are behind. This information is essential for establishing direct peer-to-peer connections: - -- **NAT type detection** - Determines if peers can connect directly or need relay assistance -- **Public address discovery** - Peers learn their external IP and port, which they share via the signal server -- **Connection optimization** - Enables direct connections when possible, reducing latency and relay load - -The embedded STUN server in the relay service (enabled via `NB_ENABLE_STUN=true` in `relay.env`) is typically sufficient for most deployments. - -```json -"Stuns": [ - { - "Proto": "udp", - "URI": "stun:netbird.example.com:3478", - "Username": "", - "Password": null - } -] -``` +### Server Settings - - Protocol for STUN communication. Options: `udp`, `tcp`. Default: `udp` + + The address and port the server listens on inside the container. Format: `:port` or `address:port`. Default: `:80` - - STUN server URI. Format: `stun:hostname:port` + + The public address peers use to connect to this server. Use `https://` for TLS. Must be reachable from all peers. - - Optional username for authenticated STUN. Usually empty. + + UDP port(s) for the embedded STUN server. Must be exposed in `docker-compose.yml` and reachable through firewalls. Default: `[3478]` - - Optional password for authenticated STUN. Usually `null`. + + Port to expose Prometheus metrics endpoint. When set, the server exposes metrics at `/metrics` for monitoring. + + + Address for health check endpoint (e.g., `:9000`). When set, exposes `/health` for container orchestration and load balancer health probes. + + + Controls log verbosity. Options: `debug`, `info`, `warn`, `error`. Default: `info` + + + Where to write log output. Use `console` for Docker logging (recommended) or specify a file path. + + + Shared secret used to authenticate relay connections. Auto-generated by the setup script. + + + Directory where the server stores its database and persistent state. Default: `/var/lib/netbird` -### Relay Section +### Authentication (auth) -Configures the NetBird relay server connection for NAT traversal. - -**What does the relay service do?** - -When two NetBird peers cannot establish a direct WireGuard connection (due to restrictive NATs, firewalls, or network topology), traffic is routed through the relay server. The relay acts as an encrypted intermediary, ensuring connectivity even in challenging network environments. - -- **Automatic fallback** - Peers attempt direct connections first; relay is used only when needed -- **End-to-end encryption** - Traffic remains WireGuard-encrypted; the relay cannot read packet contents -- **Credential-based authentication** - The shared secret ensures only authorized peers can use your relay - -```json -"Relay": { - "Addresses": ["rels://netbird.example.com:443"], - "CredentialsTTL": "24h", - "Secret": "your-relay-secret" -} -``` - - -The relay address uses the same port as HTTPS (443) when using TLS (`rels://`), or port 80 when not using TLS (`rel://`). The reverse proxy routes `/relay*` paths to the relay service internally. - +Configures the built-in identity provider that handles user authentication and management. - - Array of relay server addresses. Format: `rels://hostname:port` for TLS or `rel://hostname:port` for unencrypted. Default uses the public HTTPS port (443). + + Enable the embedded identity provider. When `true`, the server hosts OAuth2/OIDC endpoints at `/oauth2/`. - - Time-to-live for relay credentials. Default: `24h` + + The OAuth2/OIDC issuer URL (e.g., `https://netbird.example.com/oauth2`). This URL is used to validate JWT tokens. - - Shared authentication secret. Must match relay server's `NB_AUTH_SECRET`. + + Enables automatic refresh of IdP signing keys. Recommended for production. + + + Storage backend for IdP data. Default: `sqlite3` + + + OAuth2 redirect URIs for the dashboard application. + + + OAuth2 redirect URIs for CLI authentication. - -The relay secret must be identical in both `management.json` and the relay service environment. A mismatch will cause relay connections to fail. - - -### Signal Section - -Configures the connection to the Signal service for peer-to-peer connection establishment. - -**What does the signal service do?** - -The signal service acts as a rendezvous service that facilitates peer connections. When two peers want to establish a direct connection, they exchange connection offers, answers, and network candidates through the signal server. This coordination enables peers to discover each other and negotiate the optimal connection path. - -- **Rendezvous service** - Enables peers to find each other and exchange the information needed to establish direct WireGuard tunnels -- **No traffic routing** - Unlike the relay, the signal server only handles connection setup metadata, not actual traffic -- **Persistent connections** - Peers maintain a connection to the signal server to receive incoming connection requests -- **HTTP/2 protocol** - Clients connect via the reverse proxy on port 443 (HTTPS) or 80 (HTTP) - -```json -"Signal": { - "Proto": "https", - "URI": "netbird.example.com:443", - "Username": "", - "Password": null -} -``` - - -The signal URI uses the same public HTTPS port (443) as other services. The reverse proxy routes signal traffic (`/signalexchange.SignalExchange/*` and `/ws-proxy/signal*`) to the signal service internally. - - - - - Protocol for signal communication. Options: `http`, `https`. Use `https` for production deployments with TLS, or `http` for non-TLS setups. - - - Signal server address. Format: `hostname:port`. Uses the public port (443 for HTTPS, 80 for HTTP). - - - Optional authentication username. Usually empty. - - - Optional authentication password. Usually `null`. - - - -### ReverseProxy Section - -Configures trusted reverse proxies for proper client IP detection. - -```json -"ReverseProxy": { - "TrustedHTTPProxies": [], - "TrustedHTTPProxiesCount": 0, - "TrustedPeers": ["0.0.0.0/0"] -} -``` - - - - List of trusted proxy IP addresses or CIDR ranges. - - - Number of trusted proxy hops. Used with X-Forwarded-For header parsing. - - - CIDR ranges of trusted peers. Default: `["0.0.0.0/0"]` (trust all). - - - - -When running behind a reverse proxy, configure `TrustedHTTPProxies` with your proxy's IP to ensure accurate client IP logging and rate limiting. - - -### StoreConfig Section - -Configures the database backend for storing all NetBird management data. - -**What data is stored in the database?** - -The management database contains all persistent state for your NetBird deployment: - -- **Accounts and users** - User accounts, roles, and permissions -- **Peers** - Registered devices, their WireGuard keys, IP assignments, and metadata -- **Groups** - Peer groupings used for access control and network organization -- **Access policies** - Network access rules defining which peers can communicate -- **Routes** - Network routes for accessing external subnets through NetBird peers -- **DNS configuration** - Custom DNS settings and nameserver groups -- **Setup keys** - Keys used for automated peer enrollment -- **Activity logs** - Audit trail of user and system actions -- **Posture checks** - Device security compliance policies - -**Where is the data stored?** - -| Engine | Storage Location | Notes | -|--------|------------------|-------| -| SQLite (default) | `/var/lib/netbird/` volume | File-based database stored in the `netbird-mgmt` Docker volume. Zero configuration required, but does not support concurrent writes or running multiple management instances. Best for testing or small deployments with fewer than 100 peers. | -| PostgreSQL | External database server | Recommended for production deployments. Supports concurrent access, enabling multiple management instances for high availability. Requires managing a separate PostgreSQL server but offers standard backup tools and replication options. | -| MySQL | External database server | Alternative to PostgreSQL for organizations that have standardized on MySQL/MariaDB. Provides similar benefits to PostgreSQL including concurrent access and standard backup procedures. | - -```json -"StoreConfig": { - "Engine": "sqlite" -} -``` - - - - Database engine. Options: `sqlite`, `postgres`, `mysql`. Default: `sqlite` - - - -For PostgreSQL or MySQL, set the connection string via environment variables: -- `NETBIRD_STORE_ENGINE_POSTGRES_DSN` for PostgreSQL -- `NETBIRD_STORE_ENGINE_MYSQL_DSN` for MySQL - - -For production deployments with multiple users or high availability requirements, consider using PostgreSQL. SQLite is convenient for testing and small deployments but does not support concurrent writes or easy backups while the service is running. - - -See [Management Postgres Store](/selfhosted/postgres-store) for PostgreSQL setup. - -### EmbeddedIdP Section - -Configures the built-in identity provider that handles user authentication and management. The embedded IdP is based on DEX and supports both local user management and connections to external identity providers configured through the Dashboard. - -```json -"EmbeddedIdP": { - "Enabled": true, - "Issuer": "https://netbird.example.com/oauth2", - "DashboardRedirectURIs": [ - "https://netbird.example.com/nb-auth", - "https://netbird.example.com/nb-silent-auth" - ] -} -``` - - - - Enable the embedded identity provider. When `true`, the management server hosts OAuth2/OIDC endpoints at `/oauth2/`. - - - The issuer URL for tokens. Should be `https://your-domain/oauth2`. This URL is used to validate JWT tokens and must be accessible to clients. - - - Allowed redirect URIs for OAuth2 authorization flow. Must include the dashboard authentication callbacks, typically `/nb-auth` and `/nb-silent-auth` on your domain. - - - -When `EmbeddedIdP.Enabled` is `true`, the management server automatically: +When `auth.enabled` is `true`, the server automatically: - Hosts OIDC discovery at `https://your-domain/oauth2/.well-known/openid-configuration` - Provides JWKS (signing keys) at `https://your-domain/oauth2/keys` - Handles token issuance at `https://your-domain/oauth2/token` - Manages device authorization at `https://your-domain/oauth2/device/authorize` - Provides user management through the Dashboard UI -### Other Top-Level Settings +### Database (store) + +Configures the database backend for storing all NetBird data. - - Disable the default "allow all" access policy for new accounts. When `true`, new accounts start with no access rules, requiring explicit policy creation before peers can communicate. Default: `false` + + Database engine. Options: `sqlite`, `postgres`, `mysql`. Default: `sqlite` - - Data directory path where the management service stores its database and state files. Usually set via command line (`--datadir`). Default: `/var/lib/netbird` - - - 32-byte (256-bit) encryption key for sensitive data at rest. Used to encrypt setup keys, API tokens, and other secrets stored in the database. Auto-generated by setup scripts. + + Key used to encrypt sensitive data in the database. Keep this secure and backed up. +**What data is stored?** + +- **Accounts and users** - User accounts, roles, and permissions +- **Peers** - Registered devices, their WireGuard keys, IP assignments, and metadata +- **Groups** - Peer groupings used for access control +- **Access policies** - Network access rules +- **Routes** - Network routes for external subnets +- **DNS configuration** - Custom DNS settings +- **Setup keys** - Keys for automated peer enrollment +- **Activity logs** - Audit trail + +For PostgreSQL or MySQL, set the connection string via environment variables: +- `NETBIRD_STORE_ENGINE_POSTGRES_DSN` for PostgreSQL +- `NETBIRD_STORE_ENGINE_MYSQL_DSN` for MySQL + -Keep `DataStoreEncryptionKey` secure and backed up. This key encrypts sensitive data in your database, including setup keys and API tokens. Losing this key means losing access to encrypted data, and you will need to regenerate all setup keys and API tokens. +Keep `encryptionKey` secure and backed up. This key encrypts sensitive data in your database. Losing this key means losing access to encrypted data, and you will need to regenerate all setup keys and API tokens. ---- - -## relay.env - -Environment configuration for the relay service. This file is mounted into the relay container and configures both the relay and the embedded STUN server. - -```bash -# Log level: debug, info, warn, error -NB_LOG_LEVEL=info - -# Address to listen on for relay connections -NB_LISTEN_ADDRESS=:80 - -# Public address for peers to connect -# Use rel:// for unencrypted or rels:// for TLS -NB_EXPOSED_ADDRESS=rels://netbird.example.com:443 - -# Authentication secret (must match management.json Relay.Secret) -NB_AUTH_SECRET=your-secret-here - -# Embedded STUN server configuration -NB_ENABLE_STUN=true -NB_STUN_LOG_LEVEL=info -NB_STUN_PORTS=3478 -``` - -### All Relay Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `NB_LOG_LEVEL` | `info` | Controls relay log verbosity. Use `debug` when troubleshooting connection issues to see detailed peer connection attempts and failures. | -| `NB_LISTEN_ADDRESS` | `:80` | The address and port the relay listens on inside the container. Format: `:port` or `address:port`. Usually left as `:80` since the container port is mapped externally. | -| `NB_EXPOSED_ADDRESS` | - | The public address peers use to connect to this relay. Use `rel://` for unencrypted or `rels://` for TLS. Must be reachable from all peers. | -| `NB_AUTH_SECRET` | - | Shared secret used to authenticate peers connecting to the relay. Must exactly match the `Relay.Secret` value in `management.json` or relay connections will fail. | -| `NB_ENABLE_STUN` | `false` | When `true`, the relay also runs an embedded STUN server. This eliminates the need for a separate coturn container for NAT detection. | -| `NB_STUN_PORTS` | `3478` | UDP port(s) for the embedded STUN server. Comma-separated for multiple ports. Must be exposed in `docker-compose.yml` and reachable through firewalls. | -| `NB_STUN_LOG_LEVEL` | `info` | Separate log level for the embedded STUN server. Use `debug` to troubleshoot NAT detection issues without increasing relay log verbosity. | -| `NB_METRICS_PORT` | - | Port to expose Prometheus metrics endpoint. When set, the relay exposes metrics at `/metrics` for monitoring connection counts and performance. | -| `NB_TLS_CERT_FILE` | - | Path to TLS certificate file for relay-terminated HTTPS. Only needed when the relay handles TLS directly instead of using a reverse proxy. | -| `NB_TLS_KEY_FILE` | - | Path to TLS private key file. Must be provided together with `NB_TLS_CERT_FILE` for direct TLS termination. | -| `NB_LETSENCRYPT_DATA_DIR` | - | Directory to store Let's Encrypt certificates when the relay obtains certificates automatically. Not needed when using an external reverse proxy for TLS. | -| `NB_LETSENCRYPT_DOMAINS` | - | Comma-separated domains for automatic Let's Encrypt certificate provisioning. The relay must be reachable on port 443 for ACME challenges. | -| `NB_LETSENCRYPT_EMAIL` | - | Email address for Let's Encrypt registration. Required for certificate expiry notifications if using automatic provisioning. | -| `NB_HEALTH_LISTEN_ADDRESS` | - | Address for health check endpoint (e.g., `:8080`). When set, exposes `/health` for container orchestration and load balancer health probes. | +See [Management Postgres Store](/selfhosted/postgres-store) for PostgreSQL setup. --- @@ -754,96 +414,78 @@ When behind a reverse proxy: To use PostgreSQL instead of SQLite: -1. Update `management.json`: -```json -"StoreConfig": { - "Engine": "postgres" -} +1. Update `config.yaml`: +```yaml +server: + store: + engine: "postgres" ``` 2. Set the connection string in `docker-compose.yml`: ```yaml -management: +netbird-server: environment: - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://user:password@host:5432/netbird?sslmode=disable ``` See [Management Postgres Store](/selfhosted/postgres-store) for detailed setup. -### Disabling Anonymous Metrics +### Custom STUN Ports -In `docker-compose.yml`, update the management command: +To use multiple STUN ports, update `config.yaml`: ```yaml -management: - command: [ - "--port", "80", - "--disable-anonymous-metrics=true", - # ... other flags - ] +server: + stunPorts: + - 3478 + - 3479 ``` -### Custom Relay Configuration - -To use multiple relay servers, update `management.json`: - -```json -"Relay": { - "Addresses": [ - "rels://relay1.example.com:443", - "rels://relay2.example.com:443" - ], - "CredentialsTTL": "24h", - "Secret": "shared-secret" -} +Make sure to expose all ports in `docker-compose.yml`: +```yaml +netbird-server: + ports: + - '3478:3478/udp' + - '3479:3479/udp' ``` -Each relay server must use the same `NB_AUTH_SECRET`. Use `rels://` for TLS (port 443) or `rel://` for unencrypted (port 80). - ### Behind a Reverse Proxy When running behind your own reverse proxy (Traefik, Nginx, etc.): 1. Set `LETSENCRYPT_DOMAIN=none` in `dashboard.env` -2. Configure trusted proxies in `management.json`: -```json -"ReverseProxy": { - "TrustedHTTPProxies": ["10.0.0.1"], - "TrustedHTTPProxiesCount": 1, - "TrustedPeers": ["10.0.0.0/8"] -} -``` +2. The combined server handles trusted proxy detection automatically when running behind Caddy or other proxies. See [Reverse Proxy Configuration](/selfhosted/reverse-proxy) for detailed templates. -### Configuring External TURN Servers +### Using External Services (Advanced) -The default NetBird deployment uses the relay service for NAT traversal, which handles most connectivity scenarios. External TURN servers are only needed for advanced use cases like geographically distributed deployments or environments with restrictive firewalls that block the relay protocol. +The default NetBird deployment includes embedded relay, signal, and STUN services. External services are only needed for advanced use cases. -To use external TURN servers (e.g., coturn deployed separately): +To use external STUN, relay, or signal servers, add overrides to `config.yaml`: -```json -"TURNConfig": { - "Turns": [ - { - "Proto": "udp", - "URI": "turn:turn-us.example.com:3478", - "Username": "netbird", - "Password": "password1" - }, - { - "Proto": "udp", - "URI": "turn:turn-eu.example.com:3478", - "Username": "netbird", - "Password": "password2" - } - ], - "CredentialsTTL": "12h", - "TimeBasedCredentials": false -} +```yaml +server: + # ... basic settings ... + + # Optional: Use external STUN servers + stuns: + - uri: "stun:stun.example.com:3478" + proto: "udp" + + # Optional: Use external relay servers + relays: + addresses: + - "rels://relay.example.com:443" + secret: "relay-auth-secret" + credentialsTTL: "24h" + + # Optional: Use external signal server + signalUri: "https://signal.example.com:443" ``` +See the [From Homelab to Production guide]() for more details on configuring external services. --- diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx new file mode 100644 index 00000000..fd82620e --- /dev/null +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -0,0 +1,384 @@ +# 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. + + + 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). + + +## Architecture Overview + +### Before: Single Server + +``` +┌───────────────────────────────────────────────────────────────┐ +│ Main Server (combined) │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌─────────────┐ │ +│ │Dashboard│ │ Management │ │ Signal │ │ Relay │ │ +│ │(Web UI) │ │ │ │ │ │ + STUN │ │ +│ │ │ │ │ │ │ │ │ │ +│ └─────────┘ └────────────┘ └──────────┘ └─────────────┘ │ +│ Port 3478/udp │ +│ ┌─────────────┐ │ +│ │ Caddy │ │ +│ │ │ │ +│ └─────────────┘ │ +│ │ +│ Port 443,80/tcp │ +└───────────────────────────────────────────────────────────────┘ +``` + +### After: Distributed Relays + +``` +┌────────────────────────────────────────────────┐ +│ Main Server (combined) │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌──────────┐ │ +│ │Dashboard│ │ Management │ │ Signal │ │ +│ │(Web UI) │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ └─────────┘ └────────────┘ └──────────┘ │ +│ │ +│ ┌─────────────┐ │ +│ │ Caddy │ │ +│ │ │ │ +│ └─────────────┘ │ +│ │ +│ Port 443,80/tcp │ +└────────────────────────────────────────────────┘ + │ + │ Peers get relay addresses + ▼ +┌──────────────────────┐ ┌──────────────────────┐ +│ Relay Server 1 │ │ Relay Server 2 │ +│ │ │ │ +│ ┌────────────────┐ │ │ ┌────────────────┐ │ +│ │ Relay │ │ │ │ Relay │ │ +│ │ + STUN │ │ │ │ + STUN │ │ +│ └────────────────┘ │ │ └────────────────┘ │ +│ │ │ │ +│ Port 443, 3478/udp │ │ Port 443, 3478/udp │ +└──────────────────────┘ └──────────────────────┘ +``` + +## Step 1: Set Up External Relay Servers + +For each relay server you want to deploy: + +### 1.1 Server Requirements + +- A Linux VM with at least **1 CPU** and **1GB RAM** +- Public IP address +- Open ports: **443/tcp** (relay) and **3478/udp** (STUN) +- A domain name pointing to the server (e.g., `relay-us.example.com`) +- Docker installed + +### 1.2 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. + +### 1.3 Create Relay Configuration + +On your relay server, create a directory and configuration: + +```bash +mkdir -p ~/netbird-relay +cd ~/netbird-relay +``` + +Create `docker-compose.yml`: + +```yaml +services: + relay: + image: netbirdio/relay:latest + container_name: netbird-relay + restart: unless-stopped + ports: + - '443:443' + - '3478:3478/udp' + environment: + - 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 - Option 1: Let's Encrypt (recommended) + - NB_LETSENCRYPT_DOMAINS=relay-us.example.com + - NB_LETSENCRYPT_EMAIL=admin@example.com + - NB_LETSENCRYPT_DATA_DIR=/data/letsencrypt + # Embedded STUN + - NB_ENABLE_STUN=true + - NB_STUN_PORTS=3478 + volumes: + - relay_data:/data + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + +volumes: + relay_data: +``` + + +Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated. + + +### 1.4 Alternative: TLS with Existing Certificates + +If you have existing TLS certificates (e.g., from your own CA or a wildcard cert): + +```yaml + environment: + - NB_LOG_LEVEL=info + - NB_LISTEN_ADDRESS=:443 + - NB_EXPOSED_ADDRESS=rels://relay-us.example.com:443 + - NB_AUTH_SECRET=your-shared-secret-here + - NB_TLS_CERT_FILE=/certs/fullchain.pem + - NB_TLS_KEY_FILE=/certs/privkey.pem + - NB_ENABLE_STUN=true + - NB_STUN_PORTS=3478 + volumes: + - /path/to/certs:/certs:ro + - relay_data:/data +``` + +### 1.5 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" +``` + +### 1.6 Repeat for Additional Relay Servers + +If deploying multiple relays (e.g., for different regions), repeat steps 1.1-1.5 on each server. Use the **same `NB_AUTH_SECRET`** but update the domain name for each. + +## Step 2: Update Main Server Configuration + +Now update your main NetBird server to use the external relays instead of the embedded one. + +### 2.1 Edit config.yaml + +On your main server, edit the `config.yaml` file: + +```bash +cd ~/netbird # or wherever your deployment is +nano config.yaml +``` + +Add the external relay and STUN configuration under the `server` section: + +```yaml +server: + listenAddress: ":80" + exposedAddress: "https://netbird.example.com:443" + # Remove or comment out stunPorts since we're using external STUN + # stunPorts: + # - 3478 + metricsPort: 9090 + healthcheckAddress: ":9000" + logLevel: "info" + logFile: "console" + + authSecret: "your-shared-secret-here" # Same secret as relay servers + 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: + enabled: true + issuer: "https://netbird.example.com/oauth2" + # ... rest of auth config +``` + + +The `authSecret` in the main server config and `NB_AUTH_SECRET` on all relay servers **must be identical**. Mismatched secrets will cause relay connections to fail silently. + + +### 2.2 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"] +``` + +### 2.3 Restart the Main Server + +```bash +docker compose down +docker compose up -d +``` + +## Step 3: Verify the Configuration + +### 3.1 Check Main Server Logs + +```bash +docker compose logs netbird-server | grep -i relay +``` + +You should see the relay addresses being loaded. + +### 3.2 Check Peer Configuration + +Connect a NetBird client and check its status: + +```bash +netbird status +``` + +The output should show your external relay servers: + +``` +Relays: + [relay-us.example.com:443] is Available + [relay-eu.example.com:443] is Available +``` + +### 3.3 Test Relay Connectivity + +Force a connection through relay to verify it works: + +1. Connect two peers that cannot establish direct connections (e.g., both behind symmetric NAT) +2. Check if they can communicate +3. Verify in the dashboard that the connection is using relay + +### 3.4 Test STUN + +The NetBird client automatically uses STUN for NAT detection. You can verify STUN is working by checking the client logs: + +```bash +netbird status -d +``` + +Look for NAT type detection results. + +## 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 + +## Next Steps + +- Add monitoring with Prometheus metrics (`NB_METRICS_PORT`) +- Set up health checks for container orchestration +- Consider geographic DNS for automatic relay selection +- Review [Reverse Proxy Configuration](/selfhosted/reverse-proxy) if placing relays behind a proxy + +## See Also + +- [Configuration Files Reference](/selfhosted/configuration-files) - Full config.yaml documentation +- [Self-hosting Quickstart](/selfhosted/selfhosted-quickstart) - Initial deployment guide +- [Troubleshooting](/selfhosted/troubleshooting) - Common issues and solutions \ No newline at end of file diff --git a/src/pages/selfhosted/selfhosted-quickstart.mdx b/src/pages/selfhosted/selfhosted-quickstart.mdx index a6c85b40..624cf90e 100644 --- a/src/pages/selfhosted/selfhosted-quickstart.mdx +++ b/src/pages/selfhosted/selfhosted-quickstart.mdx @@ -73,14 +73,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_caddy_data Created 0.0s + ✔ Container netbird-server Created 0.1s + ✔ Container netbird-caddy Created 0.1s + ✔ Container netbird-dashboard Created 0.1s +Waiting for NetBird server to become ready . . . done Done! From 0e2e639a1c91311add0816dc1caf4def3e5ce673 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 21:29:12 +0100 Subject: [PATCH 02/12] add relays --- .../scaling-your-self-hosted-deployment.mdx | 146 +++++++++++------- 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index fd82620e..b973be5f 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -23,8 +23,8 @@ This guide assumes you have already [deployed a single-server NetBird](/selfhost ``` ┌───────────────────────────────────────────────────────────────┐ -│ Main Server (combined) │ │ │ +│ ┌──── Main Server (combined) ────┐ │ │ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌─────────────┐ │ │ │Dashboard│ │ Management │ │ Signal │ │ Relay │ │ │ │(Web UI) │ │ │ │ │ │ + STUN │ │ @@ -44,8 +44,8 @@ This guide assumes you have already [deployed a single-server NetBird](/selfhost ``` ┌────────────────────────────────────────────────┐ -│ Main Server (combined) │ │ │ +│ ┌ Main Server (combined) ┐ │ │ ┌─────────┐ ┌────────────┐ ┌──────────┐ │ │ │Dashboard│ │ Management │ │ Signal │ │ │ │(Web UI) │ │ │ │ │ │ @@ -82,9 +82,9 @@ For each relay server you want to deploy: - A Linux VM with at least **1 CPU** and **1GB RAM** - Public IP address -- Open ports: **443/tcp** (relay) and **3478/udp** (STUN) - 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) ### 1.2 Generate Authentication Secret @@ -106,6 +106,28 @@ 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 +NB_ENABLE_STUN=true +NB_STUN_PORTS=3478 +``` + + +Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated. + + Create `docker-compose.yml`: ```yaml @@ -117,18 +139,8 @@ services: ports: - '443:443' - '3478:3478/udp' - environment: - - 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 - Option 1: Let's Encrypt (recommended) - - NB_LETSENCRYPT_DOMAINS=relay-us.example.com - - NB_LETSENCRYPT_EMAIL=admin@example.com - - NB_LETSENCRYPT_DATA_DIR=/data/letsencrypt - # Embedded STUN - - NB_ENABLE_STUN=true - - NB_STUN_PORTS=3478 + env_file: + - relay.env volumes: - relay_data:/data logging: @@ -141,24 +153,19 @@ volumes: relay_data: ``` - -Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated. - - ### 1.4 Alternative: TLS with Existing Certificates -If you have existing TLS certificates (e.g., from your own CA or a wildcard cert): +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 - environment: - - NB_LOG_LEVEL=info - - NB_LISTEN_ADDRESS=:443 - - NB_EXPOSED_ADDRESS=rels://relay-us.example.com:443 - - NB_AUTH_SECRET=your-shared-secret-here - - NB_TLS_CERT_FILE=/certs/fullchain.pem - - NB_TLS_KEY_FILE=/certs/privkey.pem - - NB_ENABLE_STUN=true - - NB_STUN_PORTS=3478 volumes: - /path/to/certs:/certs:ro - relay_data:/data @@ -182,6 +189,21 @@ 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. +``` + ### 1.6 Repeat for Additional Relay Servers If deploying multiple relays (e.g., for different regions), repeat steps 1.1-1.5 on each server. Use the **same `NB_AUTH_SECRET`** but update the domain name for each. @@ -199,12 +221,14 @@ cd ~/netbird # or wherever your deployment is nano config.yaml ``` -Add the external relay and STUN configuration under the `server` section: +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 @@ -212,8 +236,6 @@ server: healthcheckAddress: ":9000" logLevel: "info" logFile: "console" - - authSecret: "your-shared-secret-here" # Same secret as relay servers dataDir: "/var/lib/netbird" # External STUN servers (your relay servers) @@ -238,7 +260,7 @@ server: ``` -The `authSecret` in the main server config and `NB_AUTH_SECRET` on all relay servers **must be identical**. Mismatched secrets will cause relay connections to fail silently. +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. ### 2.2 Update docker-compose.yml (Optional) @@ -272,44 +294,52 @@ docker compose up -d ### 3.1 Check Main Server Logs ```bash -docker compose logs netbird-server | grep -i relay +docker compose logs netbird-server ``` -You should see the relay addresses being loaded. - -### 3.2 Check Peer Configuration - -Connect a NetBird client and check its status: - -```bash -netbird status -``` - -The output should show your external relay servers: +Verify that the embedded relay is disabled and your external relay addresses are listed: ``` -Relays: - [relay-us.example.com:443] is Available - [relay-eu.example.com:443] is Available +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: ) ``` -### 3.3 Test Relay Connectivity +``` +Relay addresses: [rels://relay-us.example.com:443 rels://relay-eu.example.com:443] +``` -Force a connection through relay to verify it works: +### 3.2 Check Peer Status -1. Connect two peers that cannot establish direct connections (e.g., both behind symmetric NAT) -2. Check if they can communicate -3. Verify in the dashboard that the connection is using relay - -### 3.4 Test STUN - -The NetBird client automatically uses STUN for NAT detection. You can verify STUN is working by checking the client logs: +Connect a NetBird client and verify that both STUN and relay services are available: ```bash netbird status -d ``` -Look for NAT type detection results. +The output should list your external STUN and relay servers: + +``` +Relays: + [stun:relay-us.example.com:3478] is Available + [rels://relay-us.example.com:443] is Available +``` + +### 3.3 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 +``` ## Configuration Reference From 0eb13726de65710df2abdf024bd03b34401b251b Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 21:42:01 +0100 Subject: [PATCH 03/12] Finalize relay part --- .../selfhosted/scaling-your-self-hosted-deployment.mdx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index b973be5f..e2c743aa 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -84,7 +84,7 @@ For each relay server you want to deploy: - 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) +- 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 ### 1.2 Generate Authentication Secret @@ -119,7 +119,7 @@ NB_LETSENCRYPT_DOMAINS=relay-us.example.com NB_LETSENCRYPT_EMAIL=admin@example.com NB_LETSENCRYPT_DATA_DIR=/data/letsencrypt -# Embedded STUN +# Embedded STUN (comma-separated for multiple ports, e.g., 3478,3479) NB_ENABLE_STUN=true NB_STUN_PORTS=3478 ``` @@ -138,6 +138,7 @@ services: restart: unless-stopped ports: - '443:443' + # Expose all ports listed in NB_STUN_PORTS - '3478:3478/udp' env_file: - relay.env @@ -317,12 +318,13 @@ Connect a NetBird client and verify that both STUN and relay services are availa netbird status -d ``` -The output should list your external STUN and relay servers: +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 - [rels://relay-us.example.com:443] is Available + [stun:relay-eu.example.com:3478] is Available + [rels://relay-eu.example.com:443] is Available ``` ### 3.3 Test Relay Connectivity From 3ef821d7e9a51739d1eb5f17019668322c2949ff Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 21:45:49 +0100 Subject: [PATCH 04/12] Add relay failover test --- .../selfhosted/scaling-your-self-hosted-deployment.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index e2c743aa..90199051 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -327,6 +327,15 @@ Relays: [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 +``` + ### 3.3 Test Relay Connectivity You can force all peer connections through relay to verify it works end-to-end. On a client, run: From 2866a42651ee8fc05acb7b87687ffb7c0c43124a Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 22:27:11 +0100 Subject: [PATCH 05/12] Add signal extraction --- .../scaling-your-self-hosted-deployment.mdx | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index 90199051..66d1a2c4 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -352,6 +352,132 @@ Once confirmed, switch back to normal mode. The client will attempt peer-to-peer sudo netbird service reconfigure --service-env NB_FORCE_RELAY=false ``` +## Step 4: Extract the Signal Server (Optional) + +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. + + +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. + + +### 4.1 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) + +### 4.2 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. Unlike the relay, the signal server requires CLI flags for configuration. + + +Replace `signal.example.com` with your signal server's domain. + + +Create `docker-compose.yml`: + +```yaml +services: + signal: + image: netbirdio/signal:latest + container_name: netbird-signal + restart: unless-stopped + ports: + - '443:443' + - '80:80' + command: ["--port", "443", "--letsencrypt-domain", "signal.example.com"] + volumes: + - signal_data:/var/lib/netbird + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + +volumes: + signal_data: +``` + +### 4.3 Alternative: TLS with Existing Certificates + +If you have existing TLS certificates, replace the `--letsencrypt-domain` flag with certificate paths: + +```yaml + command: ["--port", "443", "--cert-file", "/certs/fullchain.pem", "--cert-key", "/certs/privkey.pem"] + volumes: + - /path/to/certs:/certs:ro + - signal_data:/var/lib/netbird +``` + +### 4.4 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. +``` + +### 4.5 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 +``` + +### 4.6 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: ) +``` + ## Configuration Reference ### Relay Server Environment Variables From 67856f8f6a81860a2d0fb5d37643bbb90c3695b6 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 10 Feb 2026 22:31:23 +0100 Subject: [PATCH 06/12] Add commercial license mention --- src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index 66d1a2c4..1511cd78 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -356,6 +356,8 @@ sudo netbird service reconfigure --service-env NB_FORCE_RELAY=false 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). + 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. From 0e2a0b6c87d31a892eb9e502078a1e79fe655e69 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 11 Feb 2026 11:52:49 +0100 Subject: [PATCH 07/12] Add sqlite to postgres migration --- .../scaling-your-self-hosted-deployment.mdx | 134 +++++++++++++++++- 1 file changed, 127 insertions(+), 7 deletions(-) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index 1511cd78..72118c48 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -352,7 +352,127 @@ Once confirmed, switch back to normal mode. The client will attempt peer-to-peer sudo netbird service reconfigure --service-env NB_FORCE_RELAY=false ``` -## Step 4: Extract the Signal Server (Optional) +## Step 4: Migrate from SQLite to PostgreSQL (Optional) + +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. + +### 4.1 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 +``` + + +Replace the default `password` with a strong, unique password for production deployments. + + +### 4.2 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/ +``` + +### 4.3 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 +``` + +### 4.4 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 +; +``` + + +Update the SQLite path and PostgreSQL connection string to match your environment. + + +### 4.5 Run the Migration + +```bash +pgloader sqlite.load +``` + +### 4.6 Update config.yaml + +On your main server, update the `store` section in `config.yaml` to use PostgreSQL: + +```yaml +server: + # ... existing settings ... + + store: + engine: "postgres" +``` + +Then pass the PostgreSQL connection string as an environment variable in your `docker-compose.yml`: + +```yaml + netbird-server: + environment: + - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://postgres:password@postgres-server:5432/postgres +``` + +### 4.7 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 +``` + +## Step 5: Extract the Signal Server (Optional) 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. @@ -362,7 +482,7 @@ Unlike relay servers, the Signal server cannot be replicated as it maintains in- 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. -### 4.1 Server Requirements +### 5.1 Server Requirements - A Linux VM with at least **1 CPU** and **1GB RAM** - Public IP address @@ -370,7 +490,7 @@ Changing the Signal server URL requires all clients to restart. After updating t - Docker installed - Firewall ports open: **80/tcp** (Let's Encrypt HTTP challenge) and **443/tcp** (gRPC/WebSocket client communication) -### 4.2 Create Signal Configuration +### 5.2 Create Signal Configuration On your signal server, create a directory and configuration: @@ -409,7 +529,7 @@ volumes: signal_data: ``` -### 4.3 Alternative: TLS with Existing Certificates +### 5.3 Alternative: TLS with Existing Certificates If you have existing TLS certificates, replace the `--letsencrypt-domain` flag with certificate paths: @@ -420,7 +540,7 @@ If you have existing TLS certificates, replace the `--letsencrypt-domain` flag w - signal_data:/var/lib/netbird ``` -### 4.4 Start the Signal Server +### 5.4 Start the Signal Server ```bash docker compose up -d @@ -447,7 +567,7 @@ Confirm the certificate was issued: * SSL certificate verify ok. ``` -### 4.5 Update Main Server Configuration +### 5.5 Update Main Server Configuration On your main server, add `signalUri` to `config.yaml`. This disables the embedded Signal server: @@ -466,7 +586,7 @@ docker compose down docker compose up -d ``` -### 4.6 Verify Signal Extraction +### 5.6 Verify Signal Extraction Check the main server logs to confirm the embedded Signal is disabled: From 598a1b5fa6c885e8c316172e8e7620265c1869de Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 11 Feb 2026 11:58:27 +0100 Subject: [PATCH 08/12] Add architecture overview --- .../scaling-your-self-hosted-deployment.mdx | 57 ++----------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index 72118c48..e4ea3229 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -19,60 +19,11 @@ This guide assumes you have already [deployed a single-server NetBird](/selfhost ## Architecture Overview -### Before: Single Server +The default single-server deployment runs all services on one machine: **Caddy** (reverse proxy), **Dashboard** (web UI), +and a **combined netbird-server** container that includes Management, Signal, and Relay + STUN as components. Caddy handles TLS termination on ports 80/443, while STUN listens on UDP port 3478. The Management server uses a **SQLite** database by default. -``` -┌───────────────────────────────────────────────────────────────┐ -│ │ -│ ┌──── Main Server (combined) ────┐ │ -│ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌─────────────┐ │ -│ │Dashboard│ │ Management │ │ Signal │ │ Relay │ │ -│ │(Web UI) │ │ │ │ │ │ + STUN │ │ -│ │ │ │ │ │ │ │ │ │ -│ └─────────┘ └────────────┘ └──────────┘ └─────────────┘ │ -│ Port 3478/udp │ -│ ┌─────────────┐ │ -│ │ Caddy │ │ -│ │ │ │ -│ └─────────────┘ │ -│ │ -│ Port 443,80/tcp │ -└───────────────────────────────────────────────────────────────┘ -``` - -### After: Distributed Relays - -``` -┌────────────────────────────────────────────────┐ -│ │ -│ ┌ Main Server (combined) ┐ │ -│ ┌─────────┐ ┌────────────┐ ┌──────────┐ │ -│ │Dashboard│ │ Management │ │ Signal │ │ -│ │(Web UI) │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ └─────────┘ └────────────┘ └──────────┘ │ -│ │ -│ ┌─────────────┐ │ -│ │ Caddy │ │ -│ │ │ │ -│ └─────────────┘ │ -│ │ -│ Port 443,80/tcp │ -└────────────────────────────────────────────────┘ - │ - │ Peers get relay addresses - ▼ -┌──────────────────────┐ ┌──────────────────────┐ -│ Relay Server 1 │ │ Relay Server 2 │ -│ │ │ │ -│ ┌────────────────┐ │ │ ┌────────────────┐ │ -│ │ Relay │ │ │ │ Relay │ │ -│ │ + STUN │ │ │ │ + STUN │ │ -│ └────────────────┘ │ │ └────────────────┘ │ -│ │ │ │ -│ Port 443, 3478/udp │ │ Port 443, 3478/udp │ -└──────────────────────┘ └──────────────────────┘ -``` +After splitting, the **main server** keeps Caddy, 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. ## Step 1: Set Up External Relay Servers From fce254b26582f4a393ba31da944e6e7b0324fb7e Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 11 Feb 2026 12:17:45 +0100 Subject: [PATCH 09/12] Use signal.env instead of commands --- .../scaling-your-self-hosted-deployment.mdx | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx index e4ea3229..1f7d3c45 100644 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx @@ -450,12 +450,22 @@ mkdir -p ~/netbird-signal cd ~/netbird-signal ``` -Like the relay, the signal server can automatically obtain TLS certificates via Let's Encrypt. Unlike the relay, the signal server requires CLI flags for configuration. +Like the relay, the signal server can automatically obtain TLS certificates via Let's Encrypt. Replace `signal.example.com` with your signal server's domain. +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 @@ -467,7 +477,8 @@ services: ports: - '443:443' - '80:80' - command: ["--port", "443", "--letsencrypt-domain", "signal.example.com"] + env_file: + - signal.env volumes: - signal_data:/var/lib/netbird logging: @@ -482,10 +493,17 @@ volumes: ### 5.3 Alternative: TLS with Existing Certificates -If you have existing TLS certificates, replace the `--letsencrypt-domain` flag with certificate paths: +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 - command: ["--port", "443", "--cert-file", "/certs/fullchain.pem", "--cert-key", "/certs/privkey.pem"] volumes: - /path/to/certs:/certs:ro - signal_data:/var/lib/netbird From f06ba8855fe5a0585cae74293bd3d565f8d67f38 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 11 Feb 2026 13:49:51 +0100 Subject: [PATCH 10/12] Split combined server split migration guides --- src/components/NavigationDocs.jsx | 10 +- .../scaling/migrate-sqlite-to-postgresql.mdx | 123 ++++ .../scaling-your-self-hosted-deployment.mdx | 96 +++ .../scaling/set-up-external-relays.mdx | 281 ++++++++ .../scaling/set-up-external-signal.mdx | 149 ++++ .../scaling-your-self-hosted-deployment.mdx | 642 ------------------ 6 files changed, 658 insertions(+), 643 deletions(-) create mode 100644 src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx create mode 100644 src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx create mode 100644 src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx create mode 100644 src/pages/selfhosted/maintenance/scaling/set-up-external-signal.mdx delete mode 100644 src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx diff --git a/src/components/NavigationDocs.jsx b/src/components/NavigationDocs.jsx index c0c6a18e..70510586 100644 --- a/src/components/NavigationDocs.jsx +++ b/src/components/NavigationDocs.jsx @@ -291,7 +291,15 @@ export const docsNavigation = [ isOpen: false, links: [ { title: 'Configuration Files', href: '/selfhosted/configuration-files' }, - { title: 'Scaling Your Deployment', href: '/selfhosted/scaling-your-self-hosted-deployment' }, + { + title: 'Scaling Your Deployment', href: '/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment', + isOpen: false, + links: [ + { title: 'Set Up External Relays', href: '/selfhosted/maintenance/scaling/set-up-external-relays' }, + { title: 'Migrate SQLite to PostgreSQL', href: '/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql' }, + { title: 'Set Up External Signal', href: '/selfhosted/maintenance/scaling/set-up-external-signal' }, + ] + }, { title: 'Backup', href: '/selfhosted/maintenance/backup' }, { title: 'Upgrade', href: '/selfhosted/maintenance/upgrade' }, { title: 'Remove', href: '/selfhosted/maintenance/remove' }, diff --git a/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx b/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx new file mode 100644 index 00000000..ec3ccd5f --- /dev/null +++ b/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx @@ -0,0 +1,123 @@ +# 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 +``` + + +Replace the default `password` with a strong, unique password for production deployments. + + +## 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 +; +``` + + +Update the SQLite path and PostgreSQL connection string to match your environment. + + +## 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" +``` + +Then pass the PostgreSQL connection string as an environment variable in your `docker-compose.yml`: + +```yaml + netbird-server: + environment: + - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://postgres:password@postgres-server:5432/postgres +``` + +## 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 +``` \ No newline at end of file diff --git a/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx new file mode 100644 index 00000000..3261bc2c --- /dev/null +++ b/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx @@ -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. + + + 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). + + +## Architecture Overview + +The default single-server deployment runs all services on one machine: **Caddy** (reverse proxy), **Dashboard** (web UI), +and a **combined netbird-server** container that includes Management, Signal, and Relay + STUN as components. Caddy 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 Caddy, 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 \ No newline at end of file diff --git a/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx b/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx new file mode 100644 index 00000000..b43cc327 --- /dev/null +++ b/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx @@ -0,0 +1,281 @@ +# 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 +``` + + +Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated. + + +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: + enabled: true + issuer: "https://netbird.example.com/oauth2" + # ... rest of auth config +``` + + +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. + + +### 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 +``` \ No newline at end of file diff --git a/src/pages/selfhosted/maintenance/scaling/set-up-external-signal.mdx b/src/pages/selfhosted/maintenance/scaling/set-up-external-signal.mdx new file mode 100644 index 00000000..125ed0bb --- /dev/null +++ b/src/pages/selfhosted/maintenance/scaling/set-up-external-signal.mdx @@ -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). + + +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. + + +## 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. + + +Replace `signal.example.com` with your signal server's domain. + + +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: ) +``` \ No newline at end of file diff --git a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx deleted file mode 100644 index 1f7d3c45..00000000 --- a/src/pages/selfhosted/scaling-your-self-hosted-deployment.mdx +++ /dev/null @@ -1,642 +0,0 @@ -# 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. - - - 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). - - -## Architecture Overview - -The default single-server deployment runs all services on one machine: **Caddy** (reverse proxy), **Dashboard** (web UI), -and a **combined netbird-server** container that includes Management, Signal, and Relay + STUN as components. Caddy 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 Caddy, 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. - -## Step 1: Set Up External Relay Servers - -For each relay server you want to deploy: - -### 1.1 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 - -### 1.2 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. - -### 1.3 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 -``` - - -Replace `relay-us.example.com` with your relay server's domain and `your-shared-secret-here` with the secret you generated. - - -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: -``` - -### 1.4 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 -``` - -### 1.5 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. -``` - -### 1.6 Repeat for Additional Relay Servers - -If deploying multiple relays (e.g., for different regions), repeat steps 1.1-1.5 on each server. Use the **same `NB_AUTH_SECRET`** but update the domain name for each. - -## Step 2: Update Main Server Configuration - -Now update your main NetBird server to use the external relays instead of the embedded one. - -### 2.1 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: - enabled: true - issuer: "https://netbird.example.com/oauth2" - # ... rest of auth config -``` - - -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. - - -### 2.2 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"] -``` - -### 2.3 Restart the Main Server - -```bash -docker compose down -docker compose up -d -``` - -## Step 3: Verify the Configuration - -### 3.1 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] -``` - -### 3.2 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 -``` - -### 3.3 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 -``` - -## Step 4: Migrate from SQLite to PostgreSQL (Optional) - -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. - -### 4.1 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 -``` - - -Replace the default `password` with a strong, unique password for production deployments. - - -### 4.2 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/ -``` - -### 4.3 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 -``` - -### 4.4 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 -; -``` - - -Update the SQLite path and PostgreSQL connection string to match your environment. - - -### 4.5 Run the Migration - -```bash -pgloader sqlite.load -``` - -### 4.6 Update config.yaml - -On your main server, update the `store` section in `config.yaml` to use PostgreSQL: - -```yaml -server: - # ... existing settings ... - - store: - engine: "postgres" -``` - -Then pass the PostgreSQL connection string as an environment variable in your `docker-compose.yml`: - -```yaml - netbird-server: - environment: - - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://postgres:password@postgres-server:5432/postgres -``` - -### 4.7 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 -``` - -## Step 5: Extract the Signal Server (Optional) - -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). - - -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. - - -### 5.1 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) - -### 5.2 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. - - -Replace `signal.example.com` with your signal server's domain. - - -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: -``` - -### 5.3 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 -``` - -### 5.4 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. -``` - -### 5.5 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 -``` - -### 5.6 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: ) -``` - -## 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 - -## Next Steps - -- Add monitoring with Prometheus metrics (`NB_METRICS_PORT`) -- Set up health checks for container orchestration -- Consider geographic DNS for automatic relay selection -- Review [Reverse Proxy Configuration](/selfhosted/reverse-proxy) if placing relays behind a proxy - -## See Also - -- [Configuration Files Reference](/selfhosted/configuration-files) - Full config.yaml documentation -- [Self-hosting Quickstart](/selfhosted/selfhosted-quickstart) - Initial deployment guide -- [Troubleshooting](/selfhosted/troubleshooting) - Common issues and solutions \ No newline at end of file From a4f2765c73535dc7f8b753cdebc733f606b915e6 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 12 Feb 2026 17:40:24 +0100 Subject: [PATCH 11/12] Adjust docs according to new getting satrted --- src/pages/selfhosted/configuration-files.mdx | 253 +++++++++--------- src/pages/selfhosted/maintenance/backup.mdx | 10 +- src/pages/selfhosted/maintenance/remove.mdx | 2 +- .../scaling/migrate-sqlite-to-postgresql.mdx | 5 +- .../scaling-your-self-hosted-deployment.mdx | 6 +- .../scaling/set-up-external-relays.mdx | 1 - src/pages/selfhosted/maintenance/upgrade.mdx | 4 +- src/pages/selfhosted/reverse-proxy.mdx | 6 +- src/pages/selfhosted/selfhosted-guide.mdx | 2 +- .../selfhosted/selfhosted-quickstart.mdx | 38 ++- 10 files changed, 178 insertions(+), 149 deletions(-) diff --git a/src/pages/selfhosted/configuration-files.mdx b/src/pages/selfhosted/configuration-files.mdx index 31c40a92..9ed8e2dc 100644 --- a/src/pages/selfhosted/configuration-files.mdx +++ b/src/pages/selfhosted/configuration-files.mdx @@ -15,10 +15,9 @@ A standard NetBird self-hosted deployment uses the following configuration files | File | Purpose | |------|---------| -| `docker-compose.yml` | Defines all NetBird services (dashboard, netbird-server), their Docker images, port mappings, volumes, and startup order. Modify this to change resource limits, add services, or adjust networking. | +| `docker-compose.yml` | Defines all NetBird services (dashboard, netbird-server, traefik), their Docker images, port mappings, volumes, and startup order. Modify this to change resource limits, add services, or adjust networking. | | `config.yaml` | Central configuration for the NetBird server including listen addresses, authentication settings, STUN ports, and database configuration. Changes here affect how peers connect and authenticate. | | `dashboard.env` | Configures the web dashboard including API endpoints, OAuth2/OIDC settings, and optional SSL settings for standalone deployments without a reverse proxy. | -| `Caddyfile` | Configures the built-in Caddy reverse proxy for SSL termination and routing requests to NetBird services. Only present when using the default `getting-started.sh` deployment with Caddy. | ### File Locations @@ -28,8 +27,7 @@ After running the installation script, configuration files are located in the di ./ ├── docker-compose.yml ├── config.yaml -├── dashboard.env -└── Caddyfile # Only when using built-in Caddy +└── dashboard.env ``` --- @@ -44,138 +42,157 @@ The Docker Compose file defines all NetBird services, their dependencies, networ |---------|-------|---------------|-------------------|-------------| | `dashboard` | `netbirdio/dashboard` | 80 | 8080:80 | The web-based management console where administrators configure networks, manage peers, create access policies, and view activity logs. Includes an embedded nginx server for serving the UI. | | `netbird-server` | `netbirdio/netbird-server` | 80, 3478/udp | 8081:80, 3478:3478/udp | The combined NetBird server that includes management, signal, and relay services in a single container. Also provides embedded STUN on UDP 3478 for NAT type detection. | -| `caddy` | `caddy` | 80, 443 | 80:80, 443:443 | Handles TLS termination and routes incoming HTTPS requests to the appropriate NetBird services. Only included in default `getting-started.sh` deployments; can be replaced with your own reverse proxy. | +| `traefik` | `traefik:v3.6` | 80, 443 | 80:80, 443:443 | Handles TLS termination via Let's Encrypt and routes incoming HTTPS requests to the appropriate NetBird services. Only included in default `getting-started.sh` deployments; can be replaced with your own reverse proxy. | -**Internal vs External ports**: Internal ports are what services listen on inside their containers. External (Exposed) ports show the host-to-container mapping used when running without the built-in Caddy (e.g., with Nginx, Traefik, or other reverse proxies). When using the default Caddy deployment, only Caddy exposes ports externally. +**Internal vs External ports**: Internal ports are what services listen on inside their containers. External (Exposed) ports show the host-to-container mapping used when running without the built-in Traefik (e.g., with Nginx or other reverse proxies). When using the default Traefik deployment, only Traefik exposes ports externally. The combined server includes an embedded STUN server, eliminating the need for a separate coturn container. STUN functionality is configured via the `stunPorts` setting in `config.yaml`. -### Default Settings +### Default Deployment (Built-in Traefik) -The compose file includes these defaults applied to all services: +The default `getting-started.sh` deployment generates a Docker Compose file with three services: Traefik (reverse proxy with automatic TLS), the dashboard, and the combined NetBird server. Traefik uses Docker labels on each service to configure routing. ```yaml -x-default: &default - restart: 'unless-stopped' - logging: - driver: 'json-file' - options: - max-size: '500m' - max-file: '2' -``` +services: + # Traefik reverse proxy (automatic TLS via Let's Encrypt) + traefik: + image: traefik:v3.6 + container_name: netbird-traefik + restart: unless-stopped + networks: [netbird] + command: + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--providers.docker.network=netbird" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=0" + - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" + - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" + ports: + - '443:443' + - '80:80' + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - netbird_traefik_letsencrypt:/letsencrypt -### Dashboard Service + # UI dashboard + dashboard: + image: netbirdio/dashboard:latest + container_name: netbird-dashboard + restart: unless-stopped + networks: [netbird] + env_file: + - ./dashboard.env + labels: + - traefik.enable=true + - traefik.http.routers.netbird-dashboard.rule=Host(`netbird.example.com`) + - traefik.http.routers.netbird-dashboard.entrypoints=websecure + - traefik.http.routers.netbird-dashboard.tls=true + - traefik.http.routers.netbird-dashboard.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-dashboard.priority=1 + - traefik.http.services.netbird-dashboard.loadbalancer.server.port=80 -The dashboard provides the web interface for NetBird management. + # Combined server (Management + Signal + Relay + STUN) + netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server + 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 + # gRPC router (needs h2c backend for HTTP/2 cleartext) + - traefik.http.routers.netbird-grpc.rule=Host(`netbird.example.com`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`)) + - traefik.http.routers.netbird-grpc.entrypoints=websecure + - traefik.http.routers.netbird-grpc.tls=true + - traefik.http.routers.netbird-grpc.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-grpc.service=netbird-server-h2c + # Backend router (relay, WebSocket, API, OAuth2) + - traefik.http.routers.netbird-backend.rule=Host(`netbird.example.com`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`)) + - traefik.http.routers.netbird-backend.entrypoints=websecure + - traefik.http.routers.netbird-backend.tls=true + - traefik.http.routers.netbird-backend.tls.certresolver=letsencrypt + - traefik.http.routers.netbird-backend.service=netbird-server + # Services + - 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 -**With built-in Caddy (default):** -```yaml -dashboard: - image: netbirdio/dashboard:latest - container_name: netbird-dashboard - restart: unless-stopped - networks: [netbird] - env_file: - - ./dashboard.env - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` +volumes: + netbird_data: + netbird_traefik_letsencrypt: -**With external reverse proxy (exposed ports):** -```yaml -dashboard: - image: netbirdio/dashboard:latest - container_name: netbird-dashboard - restart: unless-stopped - networks: [netbird] - ports: - - '127.0.0.1:8080:80' - env_file: - - ./dashboard.env - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" +networks: + netbird: ``` -The dashboard service is configured via the `dashboard.env` file. See the [dashboard.env section](#dashboard-env) for the full list of environment variables. When using the built-in Caddy, no ports are exposed directly from the dashboard container; Caddy routes traffic to it internally. +The `readTimeout=0` on the websecure entrypoint is required for gRPC long-lived streams (Management sync and Signal). Without it, Traefik's default 60-second timeout disconnects clients periodically. -### Combined NetBird Server Service - -The combined NetBird server is the core of NetBird, handling peer registration, authentication, signaling, and relay in a single container. - -**With built-in Caddy (default):** -```yaml -netbird-server: - image: netbirdio/netbird-server:latest - container_name: netbird-server - restart: unless-stopped - networks: [netbird] - ports: - - '3478:3478/udp' # Embedded STUN server (must be exposed publicly) - volumes: - - netbird_data:/var/lib/netbird - - ./config.yaml:/etc/netbird/config.yaml - command: ["--config", "/etc/netbird/config.yaml"] - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - -**With external reverse proxy (exposed ports):** -```yaml -netbird-server: - image: netbirdio/netbird-server:latest - container_name: netbird-server - restart: unless-stopped - networks: [netbird] - ports: - - '127.0.0.1:8081:80' # HTTP (for reverse proxy) - - '3478:3478/udp' # Embedded STUN server (must be exposed publicly) - volumes: - - netbird_data:/var/lib/netbird - - ./config.yaml:/etc/netbird/config.yaml - command: ["--config", "/etc/netbird/config.yaml"] - logging: - driver: "json-file" - options: - max-size: "500m" - max-file: "2" -``` - The STUN port (3478/udp) must always be exposed publicly, regardless of reverse proxy configuration. STUN uses UDP for NAT detection and cannot be proxied through HTTP reverse proxies. -To use an external database, add environment variables: +### With External Reverse Proxy (Exposed Ports) + +When using your own reverse proxy (Nginx, external Traefik, etc.), the script generates a simpler compose file without the Traefik container, exposing service ports on localhost instead: + ```yaml - environment: - - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://user:password@host:5432/netbird - # Or for MySQL: - # - NETBIRD_STORE_ENGINE_MYSQL_DSN=user:password@tcp(host:3306)/netbird +services: + dashboard: + image: netbirdio/dashboard:latest + container_name: netbird-dashboard + restart: unless-stopped + networks: [netbird] + ports: + - '127.0.0.1:8080:80' + env_file: + - ./dashboard.env + + netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server + restart: unless-stopped + networks: [netbird] + ports: + - '127.0.0.1:8081:80' + - '3478:3478/udp' + volumes: + - netbird_data:/var/lib/netbird + - ./config.yaml:/etc/netbird/config.yaml + command: ["--config", "/etc/netbird/config.yaml"] + +volumes: + netbird_data: + +networks: + netbird: ``` +Your reverse proxy should forward traffic to `127.0.0.1:8081` for the NetBird server and `127.0.0.1:8080` for the dashboard. gRPC routes require HTTP/2 (h2c) upstream support. + ### Volume Configuration | Volume | Mount Point | Purpose | |--------|-------------|---------| | `netbird_data` | `/var/lib/netbird` | Stores the management database (SQLite by default), encryption keys, and persistent state. Back up this volume regularly to preserve your accounts, peers, policies, and setup keys. | -| `netbird_caddy_data` | `/data` | Stores Caddy's TLS certificates and other persistent data. Only used when deploying with the built-in Caddy reverse proxy. Preserve this volume to maintain TLS certificates across restarts. | +| `netbird_traefik_letsencrypt` | `/letsencrypt` | Stores Traefik's Let's Encrypt TLS certificates. Only used when deploying with the built-in Traefik reverse proxy. Preserve this volume to maintain TLS certificates across restarts. | -The `getting-started.sh` deployment uses only two volumes: `netbird_data` for the server database and `netbird_caddy_data` for Caddy's certificate storage. +The `getting-started.sh` deployment uses only two volumes: `netbird_data` for the server database and `netbird_traefik_letsencrypt` for Traefik's certificate storage. --- @@ -201,11 +218,8 @@ server: dataDir: "/var/lib/netbird" auth: - enabled: true issuer: "https://netbird.example.com/oauth2" signKeyRefreshEnabled: true - storage: - type: "sqlite3" dashboardRedirectURIs: - "https://netbird.example.com/nb-auth" - "https://netbird.example.com/nb-silent-auth" @@ -213,7 +227,8 @@ server: - "http://localhost:53000/" store: - engine: "sqlite" + engine: "sqlite" # sqlite, postgres, or mysql + dsn: "" # Connection string for postgres or mysql encryptionKey: "your-encryption-key" ``` @@ -254,18 +269,12 @@ server: Configures the built-in identity provider that handles user authentication and management. - - Enable the embedded identity provider. When `true`, the server hosts OAuth2/OIDC endpoints at `/oauth2/`. - The OAuth2/OIDC issuer URL (e.g., `https://netbird.example.com/oauth2`). This URL is used to validate JWT tokens. Enables automatic refresh of IdP signing keys. Recommended for production. - - Storage backend for IdP data. Default: `sqlite3` - OAuth2 redirect URIs for the dashboard application. @@ -274,7 +283,7 @@ Configures the built-in identity provider that handles user authentication and m -When `auth.enabled` is `true`, the server automatically: +The embedded identity provider is always enabled in the combined server. It automatically: - Hosts OIDC discovery at `https://your-domain/oauth2/.well-known/openid-configuration` - Provides JWKS (signing keys) at `https://your-domain/oauth2/keys` - Handles token issuance at `https://your-domain/oauth2/token` @@ -289,6 +298,9 @@ Configures the database backend for storing all NetBird data. Database engine. Options: `sqlite`, `postgres`, `mysql`. Default: `sqlite` + + Connection string for postgres or mysql engines. For postgres: `host=localhost user=netbird password=secret dbname=netbird port=5432`. Alternatively, use the `NETBIRD_STORE_ENGINE_POSTGRES_DSN` or `NETBIRD_STORE_ENGINE_MYSQL_DSN` environment variables. + Key used to encrypt sensitive data in the database. Keep this secure and backed up. @@ -354,13 +366,13 @@ AUTH_SUPPORTED_SCOPES=openid profile email groups AUTH_REDIRECT_URI=/nb-auth AUTH_SILENT_REDIRECT_URI=/nb-silent-auth -# SSL - disabled when behind reverse proxy (Caddy handles TLS) +# SSL - disabled when behind reverse proxy (Traefik handles TLS) NGINX_SSL_PORT=443 LETSENCRYPT_DOMAIN=none ``` -When using the built-in Caddy or an external reverse proxy, set `LETSENCRYPT_DOMAIN=none` because the reverse proxy handles TLS termination. Only set a domain here if running the dashboard standalone without a reverse proxy. +When using the built-in Traefik or an external reverse proxy, set `LETSENCRYPT_DOMAIN=none` because the reverse proxy handles TLS termination. Only set a domain here if running the dashboard standalone without a reverse proxy. ### Endpoint Configuration @@ -398,7 +410,7 @@ The dashboard container's embedded nginx server can be configured using these en The `NGINX_SSL_PORT` and Let's Encrypt variables are **only necessary when running the dashboard standalone** without an external reverse proxy. In standalone mode, the dashboard's embedded nginx handles SSL/TLS termination directly. -**For most installations** that use the built-in Caddy reverse proxy (the default `getting-started.sh` deployment) or an external reverse proxy like Traefik or Nginx, **you do not need to configure these nginx variables**. The reverse proxy handles SSL termination and routes traffic to the dashboard container, which serves content over HTTP internally. +**For most installations** that use the built-in Traefik reverse proxy (the default `getting-started.sh` deployment) or an external reverse proxy like Nginx, **you do not need to configure these nginx variables**. The reverse proxy handles SSL termination and routes traffic to the dashboard container, which serves content over HTTP internally. When behind a reverse proxy: - Set `LETSENCRYPT_DOMAIN=none` to disable the dashboard's internal Let's Encrypt @@ -414,18 +426,19 @@ When behind a reverse proxy: To use PostgreSQL instead of SQLite: -1. Update `config.yaml`: +Update `config.yaml`: ```yaml server: store: engine: "postgres" + dsn: "host=db-server user=netbird password=secret dbname=netbird port=5432" ``` -2. Set the connection string in `docker-compose.yml`: +Alternatively, you can use an environment variable instead of putting the DSN in the config file: ```yaml netbird-server: environment: - - NETBIRD_STORE_ENGINE_POSTGRES_DSN=postgres://user:password@host:5432/netbird?sslmode=disable + - NETBIRD_STORE_ENGINE_POSTGRES_DSN=host=db-server user=netbird password=secret dbname=netbird port=5432 ``` See [Management Postgres Store](/selfhosted/postgres-store) for detailed setup. @@ -454,7 +467,7 @@ netbird-server: When running behind your own reverse proxy (Traefik, Nginx, etc.): 1. Set `LETSENCRYPT_DOMAIN=none` in `dashboard.env` -2. The combined server handles trusted proxy detection automatically when running behind Caddy or other proxies. +2. The combined server handles trusted proxy detection automatically when running behind Traefik or other proxies. See [Reverse Proxy Configuration](/selfhosted/reverse-proxy) for detailed templates. diff --git a/src/pages/selfhosted/maintenance/backup.mdx b/src/pages/selfhosted/maintenance/backup.mdx index 537c9c1f..f82db025 100644 --- a/src/pages/selfhosted/maintenance/backup.mdx +++ b/src/pages/selfhosted/maintenance/backup.mdx @@ -5,17 +5,17 @@ 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/ ``` For detailed information about each configuration file and its options, see the [Configuration Files Reference](/selfhosted/configuration-files). -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 management -docker compose cp -a management:/var/lib/netbird/ backup/ -docker compose start management +docker compose stop netbird-server +docker compose cp -a netbird-server:/var/lib/netbird/ backup/ +docker compose start netbird-server ``` ## Get In Touch diff --git a/src/pages/selfhosted/maintenance/remove.mdx b/src/pages/selfhosted/maintenance/remove.mdx index 66b1dacf..122fe66c 100644 --- a/src/pages/selfhosted/maintenance/remove.mdx +++ b/src/pages/selfhosted/maintenance/remove.mdx @@ -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 diff --git a/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx b/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx index ec3ccd5f..969995bc 100644 --- a/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx +++ b/src/pages/selfhosted/maintenance/scaling/migrate-sqlite-to-postgresql.mdx @@ -95,14 +95,15 @@ server: store: engine: "postgres" + dsn: "host=postgres-server user=postgres password=password dbname=postgres port=5432" ``` -Then pass the PostgreSQL connection string as an environment variable in your `docker-compose.yml`: +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=postgres://postgres:password@postgres-server:5432/postgres + - NETBIRD_STORE_ENGINE_POSTGRES_DSN=host=postgres-server user=postgres password=password dbname=postgres port=5432 ``` ## Restart and Verify diff --git a/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx b/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx index 3261bc2c..4d817cde 100644 --- a/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx +++ b/src/pages/selfhosted/maintenance/scaling/scaling-your-self-hosted-deployment.mdx @@ -19,10 +19,10 @@ This guide assumes you have already [deployed a single-server NetBird](/selfhost ## Architecture Overview -The default single-server deployment runs all services on one machine: **Caddy** (reverse proxy), **Dashboard** (web UI), -and a **combined netbird-server** container that includes Management, Signal, and Relay + STUN as components. Caddy handles TLS termination on ports 80/443, while STUN listens on UDP port 3478. The Management server uses a **SQLite** database by default. +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 Caddy, Dashboard, Management, and optionally Signal. +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 diff --git a/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx b/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx index b43cc327..7bb1e22f 100644 --- a/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx +++ b/src/pages/selfhosted/maintenance/scaling/set-up-external-relays.mdx @@ -183,7 +183,6 @@ server: credentialsTTL: "24h" auth: - enabled: true issuer: "https://netbird.example.com/oauth2" # ... rest of auth config ``` diff --git a/src/pages/selfhosted/maintenance/upgrade.mdx b/src/pages/selfhosted/maintenance/upgrade.mdx index 1e8d57f3..9f8c4a17 100644 --- a/src/pages/selfhosted/maintenance/upgrade.mdx +++ b/src/pages/selfhosted/maintenance/upgrade.mdx @@ -46,11 +46,11 @@ 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 ``` diff --git a/src/pages/selfhosted/reverse-proxy.mdx b/src/pages/selfhosted/reverse-proxy.mdx index d74745a0..abf0fd2b 100644 --- a/src/pages/selfhosted/reverse-proxy.mdx +++ b/src/pages/selfhosted/reverse-proxy.mdx @@ -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. 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) diff --git a/src/pages/selfhosted/selfhosted-guide.mdx b/src/pages/selfhosted/selfhosted-guide.mdx index 05ab4664..8a5195f7 100644 --- a/src/pages/selfhosted/selfhosted-guide.mdx +++ b/src/pages/selfhosted/selfhosted-guide.mdx @@ -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. Not all reverse proxies are supported as NetBird uses *gRPC* for various components. Your reverse proxy must support HTTP/2 and gRPC proxying. diff --git a/src/pages/selfhosted/selfhosted-quickstart.mdx b/src/pages/selfhosted/selfhosted-quickstart.mdx index 624cf90e..20ffba7e 100644 --- a/src/pages/selfhosted/selfhosted-quickstart.mdx +++ b/src/pages/selfhosted/selfhosted-quickstart.mdx @@ -39,8 +39,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,7 +49,7 @@ 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 setup and handles TLS certificates automatically via Let's Encrypt with no additional configuration. 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. @@ -61,8 +61,8 @@ If you already have a reverse proxy (Traefik, Nginx, etc.) and want to use it in 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) @@ -74,12 +74,12 @@ Rendering initial files... Starting NetBird services [+] up 6/6 - ✔ Network combined_netbird Created 0.1s - ✔ Volume combined_netbird_data Created 0.0s - ✔ Volume combined_netbird_caddy_data Created 0.0s - ✔ Container netbird-server Created 0.1s - ✔ Container netbird-caddy Created 0.1s - ✔ Container netbird-dashboard Created 0.1s + ✔ 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! @@ -148,6 +148,22 @@ Once your NetBird instance is running, refer to these guides for ongoing mainten ]} /> + + --- ## Troubleshoot From 6a4d7b4f9d603350271916bec4ed1d023b2ee8ab Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 12 Feb 2026 17:43:34 +0100 Subject: [PATCH 12/12] Consider older setups --- src/pages/selfhosted/maintenance/backup.mdx | 15 +++++++++++++++ src/pages/selfhosted/maintenance/upgrade.mdx | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/pages/selfhosted/maintenance/backup.mdx b/src/pages/selfhosted/maintenance/backup.mdx index f82db025..337deb0e 100644 --- a/src/pages/selfhosted/maintenance/backup.mdx +++ b/src/pages/selfhosted/maintenance/backup.mdx @@ -18,6 +18,21 @@ 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/ +docker compose start management +``` + ## Get In Touch Feel free to ping us on [Slack](/slack-url) if you have any questions. diff --git a/src/pages/selfhosted/maintenance/upgrade.mdx b/src/pages/selfhosted/maintenance/upgrade.mdx index 9f8c4a17..e64d405f 100644 --- a/src/pages/selfhosted/maintenance/upgrade.mdx +++ b/src/pages/selfhosted/maintenance/upgrade.mdx @@ -57,6 +57,18 @@ To upgrade NetBird to the latest version: For upgrades from older versions (pre-v0.26.0), see the [Legacy upgrade notes](#legacy-self-hosting-with-zitadel-idp). +### 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.