netbirdio/netbird · error
domain is required for TLS services (used for SNI matching)
Error message
domain is required for TLS services (used for SNI matching)
What it means
Returned by validateTLSMode when a tls service has an empty Domain. TLS mode routes connections by matching the server_name in the TLS ClientHello (SNI), and the service domain is that match key - without it the proxy cannot decide which service a connection belongs to. This is why the domain is required even though TLS also uses a listen_port.
Source
Thrown at management/internals/modules/reverseproxy/service/service.go:938
func (s *Service) validateTCPUDPMode() error {
if s.Domain == "" {
return errors.New("domain is required for TCP/UDP services (used for cluster derivation)")
}
if s.isAuthEnabled() {
return errors.New("auth is not supported for TCP/UDP services")
}
if len(s.Targets) != 1 {
return errors.New("TCP/UDP services must have exactly one target")
}
if s.Mode == ModeUDP && s.Targets[0].ProxyProtocol {
return errors.New("proxy_protocol is not supported for UDP services")
}
return s.validateL4Target(s.Targets[0])
}
func (s *Service) validateTLSMode() error {
if s.Domain == "" {
return errors.New("domain is required for TLS services (used for SNI matching)")
}
if s.isAuthEnabled() {
return errors.New("auth is not supported for TLS services")
}
if s.ListenPort == 0 {
return errors.New("listen_port is required for TLS services")
}
if len(s.Targets) != 1 {
return errors.New("TLS services must have exactly one target")
}
return s.validateL4Target(s.Targets[0])
}
func (s *Service) validateHTTPTargets() error {
for i, target := range s.Targets {
switch target.TargetType {
case TargetTypePeer, TargetTypeHost, TargetTypeDomain:
// Host is normally overwritten by replaceHostByLookup with theView on GitHub (pinned to 93e97f4bf1)
Solutions
- Set the service domain to the hostname clients will put in their TLS SNI, e.g. "db.netbird.example.com".
- Ensure the domain resolves through your account's zone configuration so certificates/SNI line up.
- Verify the API client is not silently dropping the domain field on serialization.
Example fix
// before
{ "mode": "tls", "listen_port": 853, "targets": [ { "target_id": "peer-a", "port": 853 } ] }
// after
{ "mode": "tls", "domain": "dns.netbird.example.com", "listen_port": 853, "targets": [ { "target_id": "peer-a", "port": 853 } ] } Defensive patterns
Strategy: validation
Validate before calling
func checkTLSDomain(mode, domain string) error {
if mode == "tls" && domain == "" {
return errors.New("domain is required for tls services (SNI matching)")
}
return nil
} Type guard
func hasTLSDomain(mode, domain string) bool {
return mode != "tls" || domain != ""
} Try / catch
if err := svc.Validate(); err != nil {
if strings.Contains(err.Error(), "domain is required for TLS") {
return respondBadRequest(errors.New("set domain to the SNI hostname clients will send"))
}
return respondBadRequest(err)
} Prevention
- Derive the tls service domain from the hostname you issue certificates for, so SNI always matches.
- Require domain in your client struct for every mode - all four modes need it.
- Add a smoke test that connects with openssl s_client and checks SNI dispatch after creation.
When it happens
Trigger: Creating a tls service supplying only listen_port and a target, with no domain; migrating a tcp service to tls (where domain was set for cluster derivation) and dropping the field in the migration script.
Common situations: Assuming the dedicated port alone identifies the service so the domain is redundant. Copying the minimal field set from an L4 example that omitted domain. Automation that treats domain as an HTTP-only attribute.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- domain is required for TCP/UDP services (used for cluster de
- auth is not supported for TLS services
- listen_port is required for TLS services
- TLS services must have exactly one target
- skip_tls_verify is not supported for L4 services
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/233f124578452728.
Report an issue: GitHub.