cloudflare/cloudflared · error

failed to listen to default metrics address: %w

Error message

failed to listen to default metrics address: %w

What it means

This error is returned by metrics server listener creation in cloudflared when binding to the *default* metrics address fails. Before falling back, cloudflared probes a default address (e.g. localhost:20241 or the configured metrics port); if listeners.Listen on that default address fails, the underlying net error is wrapped with this message. It almost always means the default port is already in use or the address is unbindable.

Source

Thrown at metrics/metrics.go:128

//
// In case the provided address is not the default one then it will be used
// as is.
func CreateMetricsListener(listeners *gracenet.Net, laddr string) (net.Listener, error) {
	if laddr == GetMetricsDefaultAddress(Runtime) {
		// On the presence of the default address select
		// a port from the known set of addresses iteratively.
		addresses := GetMetricsKnownAddresses(Runtime)
		for _, address := range addresses {
			listener, err := listeners.Listen("tcp", address)
			if err == nil {
				return listener, nil
			}
		}

		// When no port is available then bind to a random one
		listener, err := listeners.Listen("tcp", laddr)
		if err != nil {
			return nil, fmt.Errorf("failed to listen to default metrics address: %w", err)
		}

		return listener, nil
	}

	// Explicitly got a local address then bind to it
	listener, err := listeners.Listen("tcp", laddr)
	if err != nil {
		return nil, fmt.Errorf("failed to bind to address (%s): %w", laddr, err)
	}

	return listener, nil
}

func ServeMetrics(
	l net.Listener,
	ctx context.Context,
	config Config,

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check what holds the default metrics port (lsof -i :20241 / ss -ltnp) and stop the conflicting process
  2. Start cloudflared with an explicit free --metrics 127.0.0.1:<port> so the default-address path is skipped
  3. If no port is intended to be shared, note the code falls back to a random port only when the address probe fails intentionally — verify firewall/SELinux is not blocking the bind
  4. On constrained environments, ensure localhost resolution works (correct /etc/hosts) so the default address can be bound

Example fix

// before
cloudflared tunnel run --metrics localhost:20241
// after
cloudflared tunnel run --metrics localhost:20341  # free port chosen after checking ss -ltnp
Defensive patterns

Strategy: validation

Validate before calling

const defaultMetricsAddr = "localhost:20241"
conn, err := net.DialTimeout("tcp", defaultMetricsAddr, time.Second)
if err == nil {
    conn.Close()
    // port busy — configure --metrics to a free port before starting cloudflared
}
if ln, err := net.Listen("tcp", "127.0.0.1:0"); err != nil {
    // no bindable port at all — fix host networking/firewall
} else {
    ln.Close()
}

Prevention

When it happens

Trigger: Starting cloudflared with metrics enabled (metricsServeClient / UpdateConfiguration / RegisterConnection etc. trigger listener creation) while the default metrics port is already occupied by another process or a previous cloudflared instance, or when the default address cannot be resolved/bound (e.g. IPv6-only loopback issues, privileged port).

Common situations: Running multiple cloudflared instances on one host without changing --metrics; a stale cloudflared process holding the metrics port; containers/Podman restricting loopback binds; setting metrics to a port below 1024 without privileges.

Related errors


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