derailed/k9s · error

port %s is not available on host

Error message

port %s is not available on host

What it means

PFAnns.ToTunnels builds one host tunnel per parsed annotation and, before accepting it, asks the injected PortChecker (which wraps IsPortFree, a net.Listen bind test) whether address:LocalPort can be bound. The error means the local port is already taken on the host, so a port-forward would fail.

Source

Thrown at internal/port/pfs.go:44

			}
		}
		if a.LocalPort != "" {
			lps = append(lps, a.LocalPort)
		}
	}

	return strings.Join(specs, ","), strings.Join(lps, ",")
}

func (aa PFAnns) ToTunnels(address string, _ ContainerPortSpecs, available PortChecker) (PortTunnels, error) {
	pts := make(PortTunnels, 0, len(aa))
	for _, a := range aa {
		pt, err := a.ToTunnel(address)
		if err != nil {
			return pts, err
		}
		if !available(context.Background(), pt) {
			return pts, fmt.Errorf("port %s is not available on host", pt.LocalPort)
		}
		pts = append(pts, pt)
	}

	return pts, nil
}

// ParsePFs hydrates a collection of portforward annotations.
func ParsePFs(ann string) (PFAnns, error) {
	ss := strings.Split(ann, ",")
	pp := make(PFAnns, 0, len(ss))
	for _, s := range ss {
		f, err := ParsePF(s)
		if err != nil {
			return nil, err
		}
		pp = append(pp, f)
	}

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Free the port: lsof -ti :<port> | xargs kill (or stop the local service using it)
  2. Change the local port in the annotation, e.g. '8080:80' -> '18080:80'
  3. Stop the duplicate port-forward in k9s (port-forward view, Ctrl-D / delete) before starting another
  4. Bind to a different address or pick an ephemeral high port that nothing else uses

Example fix

# before: local 8080 already in use
k9scli.io/port-forwards: "myapp::8080:80"
# after: remap to free local port
k9scli.io/port-forwards: "myapp::18080:80"
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check each local port before building tunnels:
if !port.IsPortFree(ctx, port.NewPortTunnel("127.0.0.1", "", localPort, "")) {
	localPort = nextFreePort(localPort) // pick 18080, 18081, ...
}

Try / catch

On 'port %s is not available on host', free the port or remap the local port and rebuild PFAnns with the substituted value; treat as retryable-with-change, not retry-as-is.

Prevention

When it happens

Trigger: Forwarding the same pod/service twice in one k9s session; a local dev server already listening on the annotation's local port; a stale k9s process holding the port; two annotations claiming the same local port.

Common situations: Fixed local ports in k9scli.io/port-forwards colliding with local services (e.g. 3000, 8080, 5000 on macOS where AirPlay listens); resuming a session whose tunnels were never torn down; Docker/other container runtimes bound to the same port.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/8908120c2e425730. Report an issue: GitHub.