juanfont/headscale · critical · ErrNoPrefixConfigured

no IPv4 or IPv6 prefix configured, minimum one prefix is req

Error message

no IPv4 or IPv6 prefix configured, minimum one prefix is required

What it means

ErrNoPrefixConfigured is returned from hscontrol/types/config.go:1134 during prefix resolution when the ip_prefixes list yields no usable prefix. Every node in a tailnet must get an IP from the CGNAT range (default 100.64.0.0/10 and fd7a:115c:a1e0::/48), so with zero prefixes the server cannot allocate addresses and refuses to start. This is a fatal config-validation error, not a runtime one.

Source

Thrown at hscontrol/types/config.go:43

)

const (
	PKCEMethodPlain string = "plain"
	PKCEMethodS256  string = "S256"

	defaultNodeStoreBatchSize = 100
)

var (
	errOidcMutuallyExclusive     = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
	errOIDCIssuerInvalid         = errors.New("oidc.issuer must be a valid http(s) URL")
	errOIDCClientIDRequired      = errors.New("oidc.client_id is required when oidc.issuer is set")
	errOIDCClientSecretRequired  = errors.New("oidc.client_secret or oidc.client_secret_path is required when oidc.issuer is set")
	errServerURLSuffix           = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
	errServerURLSame             = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
	errInvalidPKCEMethod         = errors.New("pkce.method must be either 'plain' or 'S256'")
	errTrustedProxyZeroRange     = errors.New("0.0.0.0/0 and ::/0 are not allowed")
	ErrNoPrefixConfigured        = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
	ErrInvalidAllocationStrategy = errors.New("invalid prefix allocation strategy")
)

type IPAllocationStrategy string

const (
	IPAllocationStrategySequential IPAllocationStrategy = "sequential"
	IPAllocationStrategyRandom     IPAllocationStrategy = "random"
)

type PolicyMode string

const (
	PolicyModeDB   = "database"
	PolicyModeFile = "file"
)

// EphemeralConfig contains configuration for ephemeral node lifecycle.

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Restore at least one prefix in config.yaml: ip_prefixes: ["100.64.0.0/10"] (IPv4 CGNAT) and/or ["fd7a:115c:a1e0::/48"] (IPv6 ULA)
  2. If you intentionally want no IPv4, keep the IPv6 prefix fd7a:115c:a1e0::/48 so allocation still works
  3. Validate with `headscale config verify` before restarting the service

Example fix

# before
ip_prefixes: []

# after
ip_prefixes:
  - fd7a:115c:a1e0::/48
  - 100.64.0.0/10
Defensive patterns

Strategy: validation

Validate before calling

// ensure at least one prefix is configured before starting
if len(cfg.Prefixes.V4) == 0 && len(cfg.Prefixes.V6) == 0 {
	return errors.New("ip_prefixes must contain at least one of 100.64.0.0/10 or fd7a:115c:a1e0::/48")
}

Prevention

When it happens

Trigger: Setting ip_prefixes: [] (empty list) in config.yaml; listing only prefixes that are filtered out (e.g. invalid families or unusable ranges) so the surviving list is empty; then calling LoadConfig / starting `headscale serve`.

Common situations: Commenting out the ip_prefixes block while experimenting and accidentally leaving an empty key; narrowing prefixes to avoid a CGNAT clash (per the disable-ipv4 nodeAttr docs) and removing both families; config migration between headscale versions where the key name or defaults changed.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/0d07a181533e7f6e. Report an issue: GitHub.