istio/istio · critical

unable to listen on socket: %v

Error message

unable to listen on socket: %v

What it means

startMonitor binds istiod's monitoring HTTP endpoint with net.Listen("tcp", addr) — the addr from --monitoringAddr, default :15014. The listen syscall failed: the address is already in use, invalid, or the port is privileged/unavailable. It propagates up as "error initializing monitor" and istiod exits.

Source

Thrown at pilot/pkg/bootstrap/monitoring.go:87

			http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden)
			return
		}
		// Pass control back to the handler
		handler.ServeHTTP(w, r)
	})
}

// Deprecated: we shouldn't have 2 http ports. Will be removed after code using
// this port is removed.
func startMonitor(exporter http.Handler, addr string, mux *http.ServeMux) (*monitor, error) {
	m := &monitor{}

	// get the network stuff setup
	var listener net.Listener
	if addr != "" {
		var err error
		if listener, err = net.Listen("tcp", addr); err != nil {
			return nil, fmt.Errorf("unable to listen on socket: %v", err)
		}
	}

	// NOTE: this is a temporary solution to provide bare-bones debug functionality
	// for pilot. a full design / implementation of self-monitoring and reporting
	// is coming. that design will include proper coverage of statusz/healthz type
	// functionality, in addition to how pilot reports its own metrics.
	addMonitor(exporter, mux)
	if addr != "" {
		m.monitoringServer = &http.Server{
			Addr:        listener.Addr().String(),
			Handler:     mux,
			IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
			ReadTimeout: 30 * time.Second,
		}
	}

	version.Info.RecordComponentBuildTag("pilot")

View on GitHub (pinned to 8dc789c5cf)

Solutions

  1. Find the occupant on the node: ss -ltnp 'sport = :15014' (or kubectl describe pod to spot hostPort conflicts)
  2. Stop the duplicate process/pod holding the port
  3. Change --monitoringAddr to a free port, or set it empty to multiplex monitoring onto the httpAddr listener (server.go skips a dedicated listener when MonitoringAddr is empty)
  4. Fix a malformed --monitoringAddr value (must be host:port or :port)

Example fix

# before: port conflict -> "unable to listen on socket: listen tcp :15014: bind: address already in use"
istiod --monitoringAddr=:15014   # another istiod already binds 15014

# after: move monitoring to a free port (or leave empty to share the HTTP listener)
istiod --monitoringAddr=:15015
Defensive patterns

Strategy: validation

Validate before calling

// Reserve the monitoring port before istiod starts
func portFree(addr string) bool {
    ln, err := net.Listen("tcp", addr)
    if err != nil { return false }
    _ = ln.Close()
    return true
}
if !portFree(":15014") { return errors.New("monitoring port busy") }

Prevention

When it happens

Trigger: Another process (a second istiod/pilot, an agent, a hostPort collision) already holds 15014; --monitoringAddr is malformed; the port is outside the allowed range in the container runtime.

Common situations: Duplicate istiod replicas on one node with hostPort 15014; sidecar or node exporters grabbing the port; leftover process from a crashed istiod; typo'd monitoring address flag.

Related errors


AI-assisted analysis of istio/istio@8dc789c5cf (2026-08-15). Data as JSON: /api/errors/b7dabbf7f14d0a63. Report an issue: GitHub.