tailscale/tailscale · critical
invalid configuration: %w
Error message
invalid configuration: %w
What it means
containerboot's very first act is parsing its environment (configFromEnv); this wrap reports any validation failure before any networking starts, and main() turns it into log.Fatal — an immediate, permanent container exit. The message chain is precise: it names the exact conflicting variables (e.g. 'TS_AUTHKEY cannot be used with TS_CLIENT_ID...', 'TS_DEST_IP is not supported with TS_USERSPACE', 'POD_IPs can contain at most 2 IPs').
Source
Thrown at cmd/containerboot/main.go:332
return
}
}
}
}
}
func main() {
if err := run(); err != nil && !errors.Is(err, context.Canceled) {
log.Fatal(err)
}
}
func run() error {
log.SetPrefix("boot: ")
cfg, err := configFromEnv()
if err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}
if !cfg.UserspaceMode {
if err := ensureTunFile(cfg.Root); err != nil {
return fmt.Errorf("unable to create tuntap device file: %w", err)
}
if cfg.ProxyTargetIP != "" || cfg.ProxyTargetDNSName != "" || cfg.Routes != nil || cfg.TailnetTargetIP != "" || cfg.TailnetTargetFQDN != "" {
if err := ensureIPForwarding(cfg.Root, cfg.ProxyTargetIP, cfg.TailnetTargetIP, cfg.TailnetTargetFQDN, cfg.Routes); err != nil {
log.Printf("Failed to enable IP forwarding: %v", err)
log.Printf("To run tailscale as a proxy or router container, IP forwarding must be enabled.")
if cfg.InKubernetes {
return fmt.Errorf("you can either set the sysctls as a privileged initContainer, or run the tailscale container with privileged=true.")
} else {
return fmt.Errorf("you can fix this by running the container with privileged=true, or the equivalent in your container runtime that permits access to sysctls.")
}
}
}
}View on GitHub (pinned to cfe32b8be6)
Solutions
- Read the wrapped message — it names the exact variables in conflict; unset or fix those
- For auth: keep exactly one mechanism (TS_AUTHKEY, or TS_CLIENT_ID+TS_CLIENT_SECRET, or TS_ID_TOKEN with TS_CLIENT_ID)
- For proxies: either set TS_USERSPACE=false when using TS_DEST_IP/TS_TAILNET_TARGET_*, or drop the target vars
- Validate POD_IPS is comma-separated with at most one IPv4 and one IPv6
Example fix
# before (conflicting auth + userspace proxy target) TS_AUTHKEY=tskey-... TS_CLIENT_ID=k1234 TS_USERSPACE=true TS_DEST_IP=10.44.0.17 # after (single auth mechanism; kernel mode for proxying) TS_AUTHKEY=tskey-... TS_USERSPACE=false TS_DEST_IP=10.44.0.17
Defensive patterns
Strategy: validation
Validate before calling
// Dry-run the exact validation containerboot performs, in CI or an initContainer
func validateEnv() error {
authkey := os.Getenv("TS_AUTHKEY") != ""
if authkey && (os.Getenv("TS_CLIENT_ID") != "" || os.Getenv("TS_CLIENT_SECRET") != "" || os.Getenv("TS_ID_TOKEN") != "" || os.Getenv("TS_AUDIENCE") != "") {
return errors.New("TS_AUTHKEY cannot be used with TS_CLIENT_ID, TS_CLIENT_SECRET, TS_ID_TOKEN, or TS_AUDIENCE")
}
if def.Bool(os.Getenv("TS_USERSPACE"), true) && os.Getenv("TS_DEST_IP") != "" {
return errors.New("TS_DEST_IP is not supported with TS_USERSPACE")
}
return nil
} Try / catch
cfg, err := configFromEnv()
if err != nil {
// message names the exact conflicting env vars — fix env and restart; retrying without changes cannot succeed
return fmt.Errorf("invalid configuration: %w", err)
} Prevention
- Choose exactly one auth mechanism per deployment: authkey, OAuth client (TS_CLIENT_ID+TS_CLIENT_SECRET), or TS_ID_TOKEN
- Automate env rendering through the operator/helm chart instead of hand-copying manifests so mutually exclusive vars never co-occur
- Add a CI step that runs containerboot with your production env in a fake root and asserts it reaches startup
When it happens
Trigger: Mutually exclusive auth envs set simultaneously (TS_AUTHKEY + TS_CLIENT_ID/SECRET/ID_TOKEN/AUDIENCE; TS_ID_TOKEN + TS_CLIENT_SECRET/AUDIENCE); proxy targets set with TS_USERSPACE=true (TS_DEST_IP, TS_TAILNET_TARGET_IP/FQDN); TS_DEST_IP together with TS_EXPERIMENTAL_DEST_DNS_NAME; POD_IPS containing >2 entries or an unparseable IP; EXPERIMENTAL_ALLOW_PROXYING_CLUSTER_TRAFFIC_VIA_INGRESS set in userspace mode, outside an ingress proxy, or without POD_IP.
Common situations: Helm chart values or Operator ProxyClass env changes that set both an authkey and OAuth vars; copying a sidecar manifest and leaving TS_DEST_IP while enabling userspace; deprecated TS_HEALTHCHECK_ADDR_PORT still set after 1.82; malformed POD_IPS from a custom admission webhook.
Related errors
- oauth authkeys require --advertise-tags
- no state store
- portlist disabled by envknob
- error retrieving state Secret: %w
- error unmarshalling previous config: %w
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/ba235a6a31ed8fe8.
Report an issue: GitHub.