{"record":{"id":"2afd802ca601e1a4","repo":"tailscale/tailscale","slug":"storing-device-ips-and-fqdn-in-kubernetes-secret","errorCode":null,"errorMessage":"storing device IPs and FQDN in Kubernetes Secret: %w","messagePattern":"storing device IPs and FQDN in Kubernetes Secret: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/containerboot/main.go","lineNumber":893,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentIPs = newCurrentIPs\n\n\t\t\t// Only store device FQDN and IP addresses to\n\t\t\t// Kubernetes Secret when any required proxy\n\t\t\t// route setup has succeeded. IPs and FQDN are\n\t\t\t// read from the Secret by the Tailscale\n\t\t\t// Kubernetes operator and, for some proxy\n\t\t\t// types, such as Tailscale Ingress, advertized\n\t\t\t// on the Ingress status. Writing them to the\n\t\t\t// Secret only after the proxy routing has been\n\t\t\t// set up ensures that the operator does not\n\t\t\t// advertize endpoints of broken proxies.\n\t\t\t// TODO (irbekrm): instead of using the IP and FQDN, have some other mechanism for the proxy signal that it is 'Ready'.\n\t\t\tdeviceEndpoints := []any{self.Name(), self.Addresses()}\n\t\t\tif hasKubeStateStore(cfg) && deephash.Update(&currentDeviceEndpoints, &deviceEndpoints) {\n\t\t\t\tif err := kc.storeDeviceEndpoints(ctx, self.Name(), addrs); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"storing device IPs and FQDN in Kubernetes Secret: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif healthCheck != nil {\n\t\t\t\thealthCheck.Update(len(addrs) != 0)\n\t\t\t}\n\n\t\t\tvar prevServeConfig *ipn.ServeConfig\n\t\t\tif getAutoAdvertiseBool() {\n\t\t\t\tprevServeConfig, err = client.GetServeConfig(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"autoadvertisement: failed to get serve config: %w\", err)\n\t\t\t\t}\n\n\t\t\t\terr = refreshAdvertiseServices(ctx, prevServeConfig, klc.New(client))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"autoadvertisement: failed to refresh advertise services: %w\", err)\n\t\t\t\t}","sourceCodeStart":875,"sourceCodeEnd":911,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/containerboot/main.go#L875-L911","documentation":"After routing rules are successfully installed, operator-managed proxies patch the node's FQDN and tailnet IPs into 'device_fqdn'/'device_ips' of the state Secret (storeDeviceEndpoints, kube.go:76 -> StrategicMergePatchSecret). The operator reads these to publish proxy endpoints (e.g. on Ingress status) and treats them as a readiness signal, so any Kubernetes API failure here is fatal to containerboot.","triggerScenarios":"State Secret deleted or renamed; ServiceAccount lacking get/patch on secrets; kube-apiserver unreachable; patch rejected with 409/422; expired SA tokens.","commonSituations":"Manual deletion of the state Secret while proxies run; missing Role bindings in custom operator installs; API server outages; strict namespace quotas or validating webhooks rejecting the patch.","solutions":["Verify the Secret exists: kubectl get secret <name>","Verify RBAC: kubectl auth can-i patch secret <name> as the pod's ServiceAccount","Check apiserver connectivity and any admission webhook rejecting the patch","Recreate the proxy resource so the operator rebuilds state"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# readiness of the state store before endpoints are published\nkubectl get secret \"$STATE_SECRET\" -o name >/dev/null || echo \"state Secret missing\"\nkubectl auth can-i patch secret \"$STATE_SECRET\" >/dev/null || echo \"no patch permission\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not delete or rename state Secrets under running proxies","Gate external automation (GitOps prunes) on operator-owned resources","Monitor proxy Secrets for unexpected mutations"],"tags":["tailscale","kubernetes","rbac","secret","operator","readiness"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}