netbirdio/netbird · error
private services require at least one access group
Error message
private services require at least one access group
What it means
Returned by Service.validatePrivateRequirements (management/internals/modules/reverseproxy/service/service.go:902). A service marked Private is NetBird-only: access is granted exclusively through attached access groups, so at least one must be configured. Zero access groups on a private service makes it unreachable and is rejected before bearer-auth checks run.
Source
Thrown at management/internals/modules/reverseproxy/service/service.go:902
case ModeTCP, ModeUDP:
return s.validateTCPUDPMode()
case ModeTLS:
return s.validateTLSMode()
default:
return fmt.Errorf("unsupported mode %q", s.Mode)
}
}
// validatePrivateRequirements enforces the private-service contract: HTTP mode, ≥1 access group, no bearer auth.
func (s *Service) validatePrivateRequirements() error {
if !s.Private {
return nil
}
if s.Mode != "" && s.Mode != ModeHTTP {
return fmt.Errorf("private services only support HTTP mode, got %q", s.Mode)
}
if len(s.AccessGroups) == 0 {
return errors.New("private services require at least one access group")
}
if s.Auth.BearerAuth != nil && s.Auth.BearerAuth.Enabled {
return errors.New("private services cannot enable bearer auth (SSO): NetBird-only access and SSO are mutually exclusive")
}
return nil
}
func (s *Service) validateHTTPMode() error {
if s.Domain == "" {
return errors.New("service domain is required")
}
if s.ListenPort != 0 {
return errors.New("listen_port is not supported for HTTP services")
}
return s.validateHTTPTargets()
}
func (s *Service) validateTCPUDPMode() error {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Attach at least one existing access group to the service (via its update API) together with setting Private.
- If no group exists yet, create the group first, then mark the service private.
- If you did not intend NetBird-only access, leave Private unset (public/SSO path).
Example fix
# before
{"name": "internal-api", "private": true, "targets": ["http://10.0.0.5:8080"]}
# after
{"name": "internal-api", "private": true, "access_groups": ["grp-ops"], "targets": ["http://10.0.0.5:8080"]} Defensive patterns
Strategy: validation
Validate before calling
if svc.Private && len(svc.AccessGroups) == 0 {
return fmt.Errorf("private services need at least one access group")
} Prevention
- Create access groups before marking services private.
- Automations should set access_groups in the same request that flips Private.
When it happens
Trigger: Creating/updating a service with Private: true and an empty or missing AccessGroups list.
Common situations: Converting a public service to private without first assigning groups; UI toggle for private enabled before group selection; API automation that omits the access_groups field.
Related errors
- private services cannot enable bearer auth (SSO): NetBird-on
- service name is required
- service name exceeds maximum length of 255 characters
- at least one target is required
- service domain is required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/2ee0b73d424e6510.
Report an issue: GitHub.