Self-Hosted Deployment Configuration Files Reference
This page provides a comprehensive reference for all configuration files used when self-hosting NetBird with the getting-started.sh deployment method. Understanding these files helps you customize your deployment, troubleshoot issues, and integrate with existing infrastructure.
Configuration files are generated automatically by the getting-started.sh script. Modifying files directly is only necessary for advanced customization after initial setup.
Overview
A standard NetBird self-hosted deployment uses the following configuration files:
| File | Purpose |
|---|---|
docker-compose.yml | Defines all services (dashboard, netbird-server, optionally traefik), their Docker images, port mappings, volumes, and startup order. Structure varies depending on the reverse proxy option chosen during setup. |
config.yaml | Unified server configuration for the combined NetBird server: listen addresses, STUN, relay authentication, embedded IdP, and database settings. Replaces the old management.json and relay.env files. |
dashboard.env | Configures the web dashboard including API endpoints, OAuth2/OIDC settings, and optional SSL settings for standalone deployments without a reverse proxy. |
File Locations
After running the installation script, configuration files are located in the directory where you ran the script (typically ~/netbird/ or the current working directory):
./
├── docker-compose.yml
├── config.yaml
├── dashboard.env
└── nginx-netbird.conf # Only when using Nginx reverse proxy
npm-advanced-config.txt # Only when using Nginx Proxy Manager
caddyfile-netbird.txt # Only when using Caddy reverse proxy
docker-compose.yml
The Docker Compose file defines all NetBird services, their dependencies, networking, and volumes.
Services Overview
| Service | Image | Internal Port | External (Exposed) | Description |
|---|---|---|---|---|
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 when using the built-in Traefik option (option 0). |
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 | Combined server that includes management, signal, relay, and embedded STUN in a single container. Configured via config.yaml. |
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 netbird-server container combines what were previously separate management, signal, relay, and STUN containers into a single service. This simplifies the deployment architecture while maintaining the same functionality.
Traefik Service
The Traefik service is only present when using the built-in reverse proxy option (option 0 during setup). It handles automatic TLS certificate provisioning via Let's Encrypt and routes requests to the correct backend services.
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
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
The readTimeout=0 setting on the websecure entrypoint disables read timeouts, which is required for long-lived gRPC and WebSocket connections used by the signal and relay services.
Dashboard Service
With built-in Traefik (default):
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
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
The dashboard service is configured via the dashboard.env file. See the dashboard.env section for the full list of environment variables. When using the built-in Traefik, no ports are exposed directly from the dashboard container; Traefik routes traffic to it internally via Docker labels.
NetBird Server Service
The combined server runs management, signal, relay, and STUN in a single container, configured by config.yaml.
With built-in Traefik (default):
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)
# The /management.ProxyService/ path is only required if the reverse proxy
# container (netbirdio/reverse-proxy) connects through Traefik — i.e., it
# runs on a separate host or a different Docker network. If the proxy is on
# the same Docker network as netbird-server, it connects directly and this
# path prefix can be omitted.
- traefik.http.routers.netbird-grpc.rule=Host(`netbird.example.com`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))
- 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
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
With external reverse proxy (exposed ports):
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"]
logging:
driver: "json-file"
options:
max-size: "500m"
max-file: "2"
Traefik Routing Labels
When using the built-in Traefik, the netbird-server service uses two routers to handle different traffic types:
| Router | Path Prefixes | Backend Service | Purpose |
|---|---|---|---|
netbird-grpc | /signalexchange.SignalExchange/, /management.ManagementService/, /management.ProxyService/ | netbird-server-h2c (h2c scheme) | gRPC traffic for signal exchange, management API, and the Reverse Proxy feature. Uses HTTP/2 cleartext (h2c) backend because gRPC requires HTTP/2. The /management.ProxyService/ path is only needed if the reverse proxy container connects through Traefik (see comment in the snippet above). |
netbird-backend | /relay, /ws-proxy/, /api, /oauth2 | netbird-server (http scheme) | HTTP traffic for relay connections, WebSocket proxying, REST API, and OAuth2/OIDC endpoints. |
The dashboard router has priority=1 (lowest), so it acts as a catch-all for requests that don't match the more specific server routes.
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 STUN server is embedded in the combined netbird-server container and configured via the server.stunPorts field in config.yaml.
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_traefik_letsencrypt | /letsencrypt | Stores Traefik's Let's Encrypt TLS certificates. Only used when deploying with the built-in Traefik reverse proxy (option 0). Preserve this volume to maintain TLS certificates across restarts. |
config.yaml
The unified configuration file controls the combined NetBird server. It replaces the separate management.json and relay.env files from older deployments.
Complete Structure
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:
issuer: "https://netbird.example.com/oauth2"
signKeyRefreshEnabled: true
dashboardRedirectURIs:
- "https://netbird.example.com/nb-auth"
- "https://netbird.example.com/nb-silent-auth"
cliRedirectURIs:
- "http://localhost:53000/"
store:
engine: "sqlite" # sqlite, postgres, or mysql
dsn: "" # Connection string for postgres or mysql
encryptionKey: "your-encryption-key"
Server Settings
- Name
server.listenAddress- Type
- string
- Required
- optional
- Enum
- Description
The address and port the combined server listens on inside the container. Default:
:80. TLS is handled by the reverse proxy.
- Name
server.exposedAddress- Type
- string
- Required
- optional
- Enum
- Description
The public-facing URL where peers connect to the server. Format:
https://hostname:port. This address is distributed to peers and must be reachable from all clients.
- Name
server.stunPorts- Type
- array
- Required
- optional
- Enum
- Description
List of UDP ports for the embedded STUN server. Default:
[3478]. These ports must be exposed indocker-compose.ymland reachable through firewalls.
- Name
server.metricsPort- Type
- number
- Required
- optional
- Enum
- Description
Port to expose Prometheus metrics endpoint. Default:
9090. Metrics are available at/metricsfor monitoring.
- Name
server.healthcheckAddress- Type
- string
- Required
- optional
- Enum
- Description
Address for health check endpoint. Default:
:9000. Exposes/healthfor container orchestration and load balancer health probes.
- Name
server.logLevel- Type
- string
- Required
- optional
- Enum
- Description
Controls log verbosity. Options:
debug,info,warn,error. Default:info. Usedebugfor troubleshooting connection issues.
- Name
server.logFile- Type
- string
- Required
- optional
- Enum
- Description
Where to write log output. Use
consolefor Docker logging (recommended) or specify a file path. Default:console.
- Name
server.authSecret- Type
- string
- Required
- optional
- Enum
- Description
Shared secret for relay authentication. Auto-generated by the setup script. This secret is used internally by the combined server for relay credential validation.
- Name
server.dataDir- Type
- string
- Required
- optional
- Enum
- Description
Data directory path where the server stores its database and state files. Default:
/var/lib/netbird. Maps to thenetbird_dataDocker volume.
Authentication Settings
Configures the built-in identity provider (embedded IdP) that handles user authentication and management.
- Name
server.auth.issuer- Type
- string
- Required
- optional
- Enum
- Description
The issuer URL for OAuth2/OIDC tokens. Format:
https://your-domain/oauth2. This URL is used to validate JWT tokens and must be accessible to clients.
- Name
server.auth.signKeyRefreshEnabled- Type
- boolean
- Required
- optional
- Enum
- Description
Enables automatic refresh of IdP signing keys. Recommended:
true. Ensures tokens remain valid by periodically rotating signing keys.
- Name
server.auth.dashboardRedirectURIs- Type
- array
- Required
- optional
- Enum
- Description
Allowed redirect URIs for OAuth2 authorization flow. Must include the dashboard authentication callbacks, typically
/nb-authand/nb-silent-authon your domain.
- Name
server.auth.cliRedirectURIs- Type
- array
- Required
- optional
- Enum
- Description
Redirect URIs for CLI-based authentication. Default:
["http://localhost:53000/"]. Used when authenticating via thenetbirdCLI tool.
When the embedded IdP is active, the server automatically hosts these OIDC endpoints:
- Discovery:
https://your-domain/oauth2/.well-known/openid-configuration - JWKS (signing keys):
https://your-domain/oauth2/keys - Token issuance:
https://your-domain/oauth2/token - Device authorization:
https://your-domain/oauth2/device/authorize
NetBird also supports integration with external OIDC-compatible identity providers for Single Sign-On (SSO), Multi-Factor Authentication (MFA), and centralized user management. See the Authentication & IdPs page for configuration details.
Store Settings
Configures the database backend for storing all NetBird management data including accounts, peers, groups, access policies, routes, DNS configuration, setup keys, and activity logs.
- Name
server.store.engine- Type
- string
- Required
- optional
- Enum
- Description
Database engine. Options:
sqlite,postgres,mysql. Default:sqlite.
- Name
server.store.dsn- Type
- string
- Required
- optional
- Enum
- Description
Connection string for postgres or mysql engines. For postgres:
host=localhost user=netbird password=secret dbname=netbird port=5432. Alternatively, use theNETBIRD_STORE_ENGINE_POSTGRES_DSNorNETBIRD_STORE_ENGINE_MYSQL_DSNenvironment variables.
- Name
server.store.encryptionKey- Type
- string
- Required
- optional
- Enum
- Description
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 the setup script.
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
| Engine | Storage | Notes |
|---|---|---|
| SQLite (default) | /var/lib/netbird/ volume | File-based database stored in the netbird_data Docker volume. Zero configuration required, but does not support concurrent writes or running multiple management instances. Best for testing or small deployments. |
| PostgreSQL | External database server | Recommended for production deployments. Supports concurrent access, enabling multiple management instances for high availability. |
| MySQL | External database server | Alternative to PostgreSQL for organizations that have standardized on MySQL/MariaDB. Provides similar benefits including concurrent access. |
For PostgreSQL or MySQL, set the connection string via the server.store.dsn field in config.yaml or environment variables on the netbird-server container. See Using an External Database below.
See Management Postgres Store for detailed PostgreSQL setup.
Keep server.store.encryptionKey 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.
dashboard.env
Environment configuration for the dashboard service.
Dashboard Architecture
The NetBird dashboard container includes an embedded nginx server that serves the dashboard web pages. This nginx instance is built into the container image and handles serving the static web UI files.
# Endpoints
NETBIRD_MGMT_API_ENDPOINT=https://netbird.example.com
NETBIRD_MGMT_GRPC_API_ENDPOINT=https://netbird.example.com
# OIDC - using embedded IdP
AUTH_AUDIENCE=netbird-dashboard
AUTH_CLIENT_ID=netbird-dashboard
AUTH_CLIENT_SECRET=
AUTH_AUTHORITY=https://netbird.example.com/oauth2
USE_AUTH0=false
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 (Traefik handles TLS)
NGINX_SSL_PORT=443
LETSENCRYPT_DOMAIN=none
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
| Variable | Description |
|---|---|
NETBIRD_MGMT_API_ENDPOINT | The URL where the dashboard makes REST API calls to the management server (e.g., https://netbird.example.com). Must be accessible from users' browsers since API calls are made client-side. |
NETBIRD_MGMT_GRPC_API_ENDPOINT | The URL for gRPC communication with the management server. Usually the same as the REST endpoint. Used for in-browser SSH and RDP clients, as well as surfacing management links for client config instructions. |
Authentication Configuration
| Variable | Description |
|---|---|
AUTH_AUDIENCE | The expected audience claim in OAuth2 tokens. Must match the audience configured in your IdP. For embedded IdP, use netbird-dashboard. |
AUTH_CLIENT_ID | The OAuth2 client identifier for the dashboard application. For embedded IdP deployments, this is netbird-dashboard. Must match the client ID registered with your identity provider. |
AUTH_CLIENT_SECRET | OAuth2 client secret for confidential clients. Leave empty for public clients (the default for browser-based apps like the dashboard). |
AUTH_AUTHORITY | The OAuth2/OIDC issuer URL (e.g., https://netbird.example.com/oauth2 for embedded IdP). The dashboard fetches OIDC discovery metadata from {AUTH_AUTHORITY}/.well-known/openid-configuration. |
USE_AUTH0 | Set to true only when using Auth0 as your identity provider. Leave as false for embedded IdP or other OIDC providers. |
AUTH_SUPPORTED_SCOPES | Space-separated list of OAuth2 scopes to request during login. Standard value is openid profile email groups. |
AUTH_REDIRECT_URI | The path where the IdP redirects after authentication (e.g., /nb-auth). Must match a redirect URI registered with your identity provider. |
AUTH_SILENT_REDIRECT_URI | The path for silent token refresh (e.g., /nb-silent-auth). Used by the dashboard to refresh tokens in the background without user interaction. |
Embedded Nginx Configuration
The dashboard container's embedded nginx server can be configured using these environment variables. These settings control how the dashboard serves its web UI.
| Variable | Default | Description |
|---|---|---|
NGINX_SSL_PORT | 443 | The HTTPS port for the dashboard's embedded nginx server. Only relevant in standalone mode without an external reverse proxy. |
LETSENCRYPT_DOMAIN | - | The domain name for automatic Let's Encrypt certificate provisioning. Set to none when using an external reverse proxy that handles TLS. |
LETSENCRYPT_EMAIL | - | Email address for Let's Encrypt account registration and certificate expiry notifications. Required when LETSENCRYPT_DOMAIN is set to an actual domain. |
The NGINX_SSL_PORT and Let's Encrypt variables are only necessary when running the dashboard standalone without an external reverse proxy. For most installations using the built-in Traefik or an external reverse proxy, set LETSENCRYPT_DOMAIN=none and the embedded nginx will serve on HTTP (port 80) internally while your reverse proxy handles HTTPS.
Common Configuration Scenarios
Using an External Database
To use PostgreSQL instead of SQLite:
- Update
config.yaml:
server:
store:
engine: "postgres"
dsn: "host=db-server user=netbird password=secret dbname=netbird port=5432"
Alternatively, you can use an environment variable instead of putting the DSN in the config file:
netbird-server:
environment:
- NETBIRD_STORE_ENGINE_POSTGRES_DSN=host=db-server user=netbird password=secret dbname=netbird port=5432
# Or for MySQL:
# - NETBIRD_STORE_ENGINE_MYSQL_DSN=user:password@tcp(host:3306)/netbird
See Management Postgres Store for detailed setup.
Changing Log Level
Update server.logLevel in config.yaml:
server:
logLevel: "debug"
Options: debug, info, warn, error. Use debug for troubleshooting connection issues.
Custom STUN Ports
To use multiple STUN ports, update config.yaml:
server:
stunPorts:
- 3478
- 3479
Make sure to expose all ports in docker-compose.yml:
netbird-server:
ports:
- '3478:3478/udp'
- '3479:3479/udp'
Behind a Reverse Proxy
When running behind your own reverse proxy (Nginx, Caddy, Nginx Proxy Manager, etc.) instead of the built-in Traefik:
- Set
LETSENCRYPT_DOMAIN=noneindashboard.env - Use the exposed-ports variant of
docker-compose.yml(the setup script generates this automatically for options 1-4) - Configure your reverse proxy to route traffic to the correct containers and ports:
- Dashboard:
127.0.0.1:8080(HTTP) - NetBird Server:
127.0.0.1:8081(HTTP), with gRPC paths using h2c (HTTP/2 cleartext)
- Dashboard:
See External Reverse Proxy Configuration for detailed templates for Nginx, Caddy, and other proxies.
Using External Services (Advanced)
The default NetBird deployment includes embedded relay, signal, and STUN services. External services are only needed for advanced use cases.
To use external STUN, relay, or signal servers, add overrides to config.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 Scaling Your Self-Hosted Deployment guide for more details on configuring external services.
See Also
- Self-hosting Quickstart Guide - Get started quickly with default settings
- External Reverse Proxy Configuration - Nginx, Caddy, NPM, HAProxy setup
- Management SQLite Store - SQLite database details
- Management Postgres Store - PostgreSQL setup

