cloudflare/cloudflared · error

IPv6 bind address is specified, but edge-ip-version is IPv4

Error message

IPv6 bind address is specified, but edge-ip-version is IPv4

What it means

adjustIPVersionByBindAddress reconciles the --edge-ip-version flag with the bind address configured for cloudflared. When the bind address is an IPv6 address (or the config resolves to IPv6-only) but edge-ip-version is set to IPv4, the function returns IPv6Only and this error because the two settings are contradictory. Cloudflared cannot connect to the edge over IPv4 while bound to an IPv6 local address.

Source

Thrown at cmd/cloudflared/tunnel/configuration.go:350

		return err
	}
	_ = listener.Close()
	return nil
}

func adjustIPVersionByBindAddress(ipVersion allregions.ConfigIPVersion, ip net.IP) (allregions.ConfigIPVersion, error) {
	if ip == nil {
		return ipVersion, nil
	}
	// https://pkg.go.dev/net#IP.To4: "If ip is not an IPv4 address, To4 returns nil."
	if ip.To4() != nil {
		if ipVersion == allregions.IPv6Only {
			return allregions.IPv4Only, fmt.Errorf("IPv4 bind address is specified, but edge-ip-version is IPv6")
		}
		return allregions.IPv4Only, nil
	} else {
		if ipVersion == allregions.IPv4Only {
			return allregions.IPv6Only, fmt.Errorf("IPv6 bind address is specified, but edge-ip-version is IPv4")
		}
		return allregions.IPv6Only, nil
	}
}

func newICMPRouter(c *cli.Context, logger *zerolog.Logger) (ingress.ICMPRouterServer, error) {
	ipv4Src, ipv6Src, err := determineICMPSources(c, logger)
	if err != nil {
		return nil, err
	}

	icmpRouter, err := ingress.NewICMPRouter(ipv4Src, ipv6Src, logger, icmpFunnelTimeout)
	if err != nil {
		return nil, err
	}
	return icmpRouter, nil
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Change --edge-ip-version to 6 (or remove it) so it matches the IPv6 bind address
  2. Change the bind address to an IPv4 address (e.g. 0.0.0.0) to match edge-ip-version=4
  3. Remove the explicit bind address so cloudflared auto-selects a source matching edge-ip-version

Example fix

// before (config.yml)
edge-ip-version: 4
protocol: quic
bind-address: "[::]:0"

// after
edge-ip-version: 6
protocol: quic
bind-address: "[::]:0"
Defensive patterns

Strategy: validation

Validate before calling

ipV4, _ := netip.ParseAddr(bindAddr)
edgeIsV4 := edgeIPVersion == "4"
if ipV4.Is6() && edgeIsV4 {
    return fmt.Errorf("bind address %s is IPv6 but edge-ip-version is 4", bindAddr)
}

Type guard

func isIPv6Bind(addr string) bool { a, err := netip.ParseAddr(addr); return err == nil && a.Is6() }

Try / catch

if err := adjustIPVersionByBindAddress(ipVersion, bindAddr); err != nil {
    logger.Error().Err(err).Msg("conflicting edge-ip-version and bind-address")
    return err
}

Prevention

When it happens

Trigger: Running `cloudflared tunnel` with `--edge-ip-version 4` while the `--bind-address` (or equivalent config field) points at an IPv6 address such as `::` or `::1`, or while the resolved region set is IPv6Only.

Common situations: Operators copy an IPv6 bind address from a dual-stack host into a config file that also pins edge-ip-version to 4; automation scripts set edge-ip-version for IPv4-only networks but inherit a stale IPv6 bind address; Kubernetes/host networking defaults change underneath the config.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/af034991977beb05. Report an issue: GitHub.