k3s-io/k3s · error

tailscale does not provide an ipv6 address

Error message

tailscale does not provide an ipv6 address

What it means

With the built-in tailscale/VPN integration (--vpn-config/--vpn-auth), k3s derives the advertise address from the VPN address instead of the node IP. When the node's primary IP is IPv6-only, k3s requires the VPN to provide an IPv6 address; if tailscale reports none, startup fails rather than advertising an unreachable address.

Source

Thrown at pkg/cli/server/server.go:528

	}

	logrus.Info("Starting " + version.Program + " " + app.App.Version)

	notifySocket := os.Getenv("NOTIFY_SOCKET")
	os.Unsetenv("NOTIFY_SOCKET")

	// try setting advertise-ip from agent VPN
	if vpnInfo, _ := vpn.GetInfoFromExecutor(); vpnInfo != nil {
		// If we are in ipv6-only mode, we should pass the ipv6 address. Otherwise, ipv4
		if utilsnet.IsIPv6(nodeIPs[0]) {
			if vpnInfo.IPv6Address != nil {
				logrus.Infof("Changed advertise-address to %v due to VPN", vpnInfo.IPv6Address)
				if serverConfig.ControlConfig.AdvertiseIP != "" {
					logrus.Warn("Conflict in the config detected. VPN integration overwrites advertise-address but the config is setting the advertise-address parameter")
				}
				serverConfig.ControlConfig.AdvertiseIP = vpnInfo.IPv6Address.String()
			} else {
				return errors.New("tailscale does not provide an ipv6 address")
			}
		} else {
			// We are in dual-stack or ipv4-only mode
			if vpnInfo.IPv4Address != nil {
				logrus.Infof("Changed advertise-address to %v due to VPN", vpnInfo.IPv4Address)
				if serverConfig.ControlConfig.AdvertiseIP != "" {
					logrus.Warn("Conflict in the config detected. VPN integration overwrites advertise-address but the config is setting the advertise-address parameter")
				}
				serverConfig.ControlConfig.AdvertiseIP = vpnInfo.IPv4Address.String()
			} else {
				return errors.New("tailscale does not provide an ipv4 address")
			}
		}
		logrus.Warn("Etcd IP (PrivateIP) remains the local IP. Running etcd traffic over VPN is not recommended due to performance issues")
	} else {
		// if not set, try setting advertise-ip from agent node-external-ip
		if serverConfig.ControlConfig.AdvertiseIP == "" && len(cmds.AgentConfig.NodeExternalIP.Value()) != 0 {
			serverConfig.ControlConfig.AdvertiseIP = util.GetFirstValidIPString(cmds.AgentConfig.NodeExternalIP.Value())

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Ensure the tailnet assigns the node an IPv6 address (tailscale side) and restart with the VPN info refreshed
  2. Switch the node to dual-stack or IPv4 node-ip so the IPv4 path in this code is used
  3. If VPN integration is not required, remove --vpn-config/--vpn-auth flags and set --advertise-address explicitly

Example fix

# before (ipv6-only node, tailscale has no v6)
k3s server --node-ip=fd00::10 --vpn-config=hostname=tsw1

# after (dual-stack node uses IPv4 path)
k3s server --node-ip=10.0.0.10,fd00::10 --vpn-config=hostname=tsw1
Defensive patterns

Strategy: validation

Validate before calling

// before enabling vpn integration on an ipv6-only node
ips := cmds.AgentConfig.NodeIP.Value()
if len(ips) > 0 && utilsnet.IsIPv6(net.ParseIP(ips[0])) {
    info, _ := vpn.GetInfoFromExecutor()
    if info != nil && info.IPv6Address == nil {
        return errors.New("VPN has no IPv6 address; fix tailscale or drop --vpn-* flags")
    }
}

Type guard

func vpnHasNeededFamily(vpnInfo *vpn.Info, wantIPv6 bool) bool {
    if vpnInfo == nil {
        return false
    }
    if wantIPv6 {
        return vpnInfo.IPv6Address != nil
    }
    return vpnInfo.IPv4Address != nil
}

Prevention

When it happens

Trigger: Node whose first node-ip is IPv6 (ipv6-only mode) started with `--vpn-config=tailscale...` where vpnInfo.IPv6Address is nil — e.g. tailscale interface only bound to IPv4.

Common situations: IPv6-only clusters adding tailscale for node-to-node connectivity; tailscale version that does not expose v6 addresses on the tailnet; stale VPN info returned via the vpn-info hook.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/398937e4a7c07cee. Report an issue: GitHub.