apache/pulsar · critical · IOException
Failed to bind Pulsar Proxy on port
Error message
Failed to bind Pulsar Proxy on port ${servicePort} What it means
ProxyService.start attempts to bind a Netty ServerBootstrap to the configured servicePort. If the bind (or the sync wait) throws — port already in use, insufficient privileges, or the port is not present in config — it wraps the cause in an IOException with the concrete port so the operator knows which listener failed. The proxy cannot serve clients without this listener, so startup aborts.
Solutions
- Check what occupies the port (e.g. ss -ltnp or netstat) and stop the conflicting process, or change servicePort in the proxy config to a free port
- Ensure servicePort is present and valid in ProxyConfiguration; note bind() is only attempted when servicePort is present
- Verify the bindAddress configured for the proxy resolves to a local interface and you have permission to bind (root/CAP_NET_BIND_SERVICE for ports <1024)
- In containers, fix duplicate port mappings/hostPort conflicts so only one process binds the port
Example fix
// before (proxy.conf) servicePort=6650 # already held by another proxy // after servicePort=6651
Defensive patterns
Strategy: validation
Validate before calling
int port = proxyConfig.getServicePort().orElseThrow(
() -> new IllegalArgumentException("servicePort is required"));
try (ServerSocket ss = new ServerSocket()) {
ss.bind(new InetSocketAddress(proxyConfig.getBindAddress(), port));
} catch (IOException e) {
throw new IllegalStateException("Port " + port + " unavailable before proxy start: " + e.getMessage());
} Type guard
boolean servicePortConfigured(ProxyConfiguration cfg) {
return cfg.getServicePort().isPresent();
} Try / catch
try {
proxyService.start();
} catch (IOException e) {
if (e.getCause() instanceof BindException) {
LOG.error("Port {} in use; stop the conflicting process or change servicePort",
proxyConfig.getServicePort());
}
throw e;
} Prevention
- Check port availability with ss -ltnp before starting the proxy
- Give each proxy instance a unique servicePort per host
- Use unprivileged ports (>1024) or grant CAP_NET_BIND_SERVICE
- In Kubernetes, avoid hostPort collisions across pods on the same node
When it happens
Trigger: Calling proxyService.start() and the bootstrap.bind(bindAddress, servicePort) inside the try block throws, typically BindException: Address already in use because another Pulsar proxy or process holds the port, or servicePort is absent from config (Optional.get() on an empty port), or binding to a privileged port (<1024) without permissions.
Common situations: Running two proxy instances on the same host (test environment left running in background); another service occupying the default port 6650; container port collisions in Kubernetes with multiple replicas on one node; missing servicePort entry after config refactoring; IPv6/IPv4 bind-address mismatch.
Related errors
- Failed to bind extension
- Failed to start HTTP server on ports
- Failed to load the extension for extension name
- Failed to resolve localhost name.
- Failed to start zookeeper
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/20b3a67afd3dbc5a.
Report an issue: GitHub.
Appendix: source
Thrown at pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyService.java:356
// brokerClientPurpose=true: this lookup transport reuses tlsFactoryClassName to carry the
// brokerClientTlsFactoryClassName selection, so a custom by-name factory is wrapped to resolve the
// transport's CLIENT_DEFAULT request under the fixed BROKER_CLIENT purpose (matches the direct path
// above, which requests BROKER_CLIENT).
this.lookupClientTlsFactory = ClientTlsFactorySupport.resolveClientTlsFactory(
lookupClientConf, statsExecutor, statsExecutor, openTelemetry.getOpenTelemetry(), true);
}
bootstrap.childHandler(new ServiceChannelInitializer(this, proxyConfig, false, null));
// Bind and start to accept incoming connections.
if (proxyConfig.getServicePort().isPresent()) {
try {
listenChannel = bootstrap.bind(proxyConfig.getBindAddress(),
proxyConfig.getServicePort().get()).sync().channel();
log.info()
.attr("localAddress", listenChannel.localAddress())
.log("Started Pulsar Proxy at");
} catch (Exception e) {
throw new IOException("Failed to bind Pulsar Proxy on port " + proxyConfig.getServicePort().get(), e);
}
}
if (proxyConfig.getServicePortTls().isPresent()) {
this.sslContextRefresher = Executors
.newSingleThreadScheduledExecutor(
new DefaultThreadFactory("proxy-ssl-context-refresher"));
ServerBootstrap tlsBootstrap = bootstrap.clone();
this.tlsServiceChannelInitializer = new ServiceChannelInitializer(this, proxyConfig, true,
sslContextRefresher);
tlsBootstrap.childHandler(this.tlsServiceChannelInitializer);
listenChannelTls = tlsBootstrap.bind(proxyConfig.getBindAddress(),
proxyConfig.getServicePortTls().get()).sync().channel();
log.info()
.attr("localAddress", listenChannelTls.localAddress())
.log("Started Pulsar TLS Proxy on");
}
View on GitHub (pinned to 820761864e)