This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-site.git


The following commit(s) were added to refs/heads/main by this push:
     new 437b8982add Improve advertised listener docs
437b8982add is described below

commit 437b8982addc490c146578a545c515271fe53eb7
Author: Lari Hotari <[email protected]>
AuthorDate: Mon May 4 12:17:18 2026 +0300

    Improve advertised listener docs
---
 docs/concepts-multiple-advertised-listeners.md     | 46 ++++++++++++++++------
 .../concepts-multiple-advertised-listeners.md      | 46 ++++++++++++++++------
 .../concepts-multiple-advertised-listeners.md      | 46 ++++++++++++++++------
 3 files changed, 105 insertions(+), 33 deletions(-)

diff --git a/docs/concepts-multiple-advertised-listeners.md 
b/docs/concepts-multiple-advertised-listeners.md
index 35499479c88..cf20ad7aad0 100644
--- a/docs/concepts-multiple-advertised-listeners.md
+++ b/docs/concepts-multiple-advertised-listeners.md
@@ -114,17 +114,42 @@ While Pulsar Proxy is generally recommended for 
Kubernetes deployments, multiple
 The Apache Pulsar Helm chart doesn't currently support this type of 
deployment. These instructions are provided as general guidance for using the 
`advertisedListeners` feature in Kubernetes deployments.
 There are multiple ways to handle this in Kubernetes deployments. Advertised 
listeners are also required in some service mesh configurations.
 
-NodePort deployment suggestions:
+> **Security notice**: Pulsar's design assumes the cluster sits behind network 
perimeter security; broker client and admin APIs are not safe to publish on the 
public internet. Use internal load balancers on private networks whenever 
possible. If external exposure is unavoidable, restrict access to authorized IP 
ranges with the Service's 
[`loadBalancerSourceRanges`](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip)
  [...]
 
-- Create individual NodePort (or LoadBalancer) services for each broker pod in 
a broker stateful set, mapping to the external listener binding ports.
-- Create a single NodePort (or LoadBalancer) service for the cluster to be 
used as the serviceUrl for clients, mapping to the external listener binding 
ports.
+A complete deployment requires two kinds of services:
+
+1. **A cluster-wide service URL.** A LoadBalancer service (for example, 
`external-brokers.example.com`) that fronts all healthy broker pods. The 
service's external port can be the standard Pulsar TLS port `6651`, which the 
LoadBalancer maps to the brokers' external listener bind port (for example, 
`16651`). Clients use this address as their `serviceUrl` for topic lookups; the 
lookup response then redirects each client to the specific broker that owns the 
topic, which is accessed via the  [...]
+
+2. **A per-broker-pod service.** One service per broker pod that targets only 
that pod and exposes its external listener bind port. The external FQDN/IP and 
port of this per-pod service must exactly match the address the broker 
advertises for its `external` listener in `advertisedListeners`; otherwise the 
lookup response will return an address that clients cannot reach. Two options 
are described below — pick one based on your TLS and cost requirements.
+
+### Option 1: NodePort service per broker pod
+
+Create one NodePort service for each broker pod, mapping to the external 
listener binding ports for that pod. Clients reach brokers via 
`<node-ip-or-hostname>:<node-port>`.
+
+- Cost: no extra cloud infrastructure is required — traffic enters through 
ports on existing cluster nodes.
+- Network reachability: this option is suitable when clients reach broker 
nodes over a private network (for example, a VPC, on-prem network, or VPN). 
Because public internet exposure is not a supported configuration for Pulsar, 
firewall rules require special attention with NodePort: the chosen ports are 
opened on every node, so node-level firewalls, cloud security groups, and 
network policies must be reviewed to ensure those ports are reachable only from 
authorized client networks.
+- TLS challenges: the certificate presented by the broker must match the 
hostname or IP that clients actually connect to. Node IPs are often unstable in 
self-managed clusters, and hostname verification fails unless you provision 
stable DNS pointing to the nodes (or include IP SANs in the certificate). 
Reusing a single wildcard certificate across brokers requires every 
client-facing entry point to resolve under that wildcard domain, which is 
awkward when clients connect directly to node IPs.
+
+### Option 2: LoadBalancer service per broker pod
+
+Create one LoadBalancer service for each broker pod, mapping to the external 
listener binding ports for that pod.
+
+- Cost: each LoadBalancer is a separate cloud resource with a recurring cost. 
For N brokers you pay for N load balancers.
+- TLS is easier: you control a stable DNS name per LoadBalancer (for example, 
`pulsar-broker-0.example.com`) and issue certificates against those names. 
Clients connect by hostname, so standard hostname verification works without 
special configuration.
+
+### Targeting a specific broker pod (applies to both options)
+
+Regardless of which service type you choose, each per-pod service must select 
exactly one broker pod by including the `statefulset.kubernetes.io/pod-name` 
label in its selector (for example, `statefulset.kubernetes.io/pod-name: 
pulsar-broker-0`). This label is automatically applied by the StatefulSet 
controller to every pod and ensures traffic for a given broker's external 
listener port reaches the broker pod whose `advertisedListeners` configuration 
matches the service's external addres [...]
+
+### Example broker configuration for the LoadBalancer per broker pod case
 
 ```properties
-# Advertised listeners for internal and external access
-advertisedListeners=internal:pulsar://192.168.1.11:6650,internal:pulsar+ssl://192.168.1.11:6651,external:pulsar://198.51.100.17:30650,external:pulsar+ssl://198.51.100.17:31650
+# Advertised listeners — "internal" uses the StatefulSet headless service DNS 
name,
+# "external" uses the stable DNS of this broker's LoadBalancer
+advertisedListeners=internal:pulsar://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6650,internal:pulsar+ssl://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6651,external:pulsar://pulsar-broker-0.example.com:6650,external:pulsar+ssl://pulsar-broker-0.example.com:6651
 
-# Bind addresses with different ports for internal and external access
-bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar+ssl://0.0.0.0:16651
+# Bind addresses — "external" binds use a different local port range than 
"internal"
+bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar://0.0.0.0:16650,external:pulsar+ssl://0.0.0.0:16651
 
 # Specify the internal listener name
 internalListenerName=internal
@@ -132,7 +157,6 @@ internalListenerName=internal
 
 In the above example:
 
-- `192.168.1.11` is the pod IP for the particular broker pod. The IP or 
hostname should be dynamically set in this configuration at broker startup.
-- `198.51.100.17` is the external IP of the k8s node. In some cases this can 
be dynamically set based on the `status.hostIP` Kubernetes information.
-- `30650` and `31650` are the specific NodePorts allocated for this broker 
pod. This can be dynamically calculated from a base port number by adding the 
StatefulSet pod index 
(`metadata.labels['statefulset.kubernetes.io/pod-index']`) to it.
-
+- `pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local` is the 
per-pod FQDN provided by the StatefulSet's headless service (format: 
`<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local`). It is 
stable across pod restarts and re-resolves to the current pod IP, so it should 
be used for the `internal` listener instead of the pod IP. The pod's own FQDN 
is set dynamically per pod (typically via 
`metadata.labels['statefulset.kubernetes.io/pod-name']` and a known headle [...]
+- `pulsar-broker-0.example.com` is the stable DNS name assigned to the 
LoadBalancer service that targets this broker pod (using a 
`statefulset.kubernetes.io/pod-name: pulsar-broker-0` selector). Each broker 
pod has its own DNS name, set dynamically per pod.
+- The per-broker LoadBalancer maps external ports `6650` and `6651` to the 
broker's external listener bind ports `16650` and `16651`. Using the standard 
Pulsar ports externally lets the same DNS name and certificate work for both 
the cluster-wide service URL and the per-pod LoadBalancer.
\ No newline at end of file
diff --git 
a/versioned_docs/version-4.0.x/concepts-multiple-advertised-listeners.md 
b/versioned_docs/version-4.0.x/concepts-multiple-advertised-listeners.md
index 35499479c88..cf20ad7aad0 100644
--- a/versioned_docs/version-4.0.x/concepts-multiple-advertised-listeners.md
+++ b/versioned_docs/version-4.0.x/concepts-multiple-advertised-listeners.md
@@ -114,17 +114,42 @@ While Pulsar Proxy is generally recommended for 
Kubernetes deployments, multiple
 The Apache Pulsar Helm chart doesn't currently support this type of 
deployment. These instructions are provided as general guidance for using the 
`advertisedListeners` feature in Kubernetes deployments.
 There are multiple ways to handle this in Kubernetes deployments. Advertised 
listeners are also required in some service mesh configurations.
 
-NodePort deployment suggestions:
+> **Security notice**: Pulsar's design assumes the cluster sits behind network 
perimeter security; broker client and admin APIs are not safe to publish on the 
public internet. Use internal load balancers on private networks whenever 
possible. If external exposure is unavoidable, restrict access to authorized IP 
ranges with the Service's 
[`loadBalancerSourceRanges`](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip)
  [...]
 
-- Create individual NodePort (or LoadBalancer) services for each broker pod in 
a broker stateful set, mapping to the external listener binding ports.
-- Create a single NodePort (or LoadBalancer) service for the cluster to be 
used as the serviceUrl for clients, mapping to the external listener binding 
ports.
+A complete deployment requires two kinds of services:
+
+1. **A cluster-wide service URL.** A LoadBalancer service (for example, 
`external-brokers.example.com`) that fronts all healthy broker pods. The 
service's external port can be the standard Pulsar TLS port `6651`, which the 
LoadBalancer maps to the brokers' external listener bind port (for example, 
`16651`). Clients use this address as their `serviceUrl` for topic lookups; the 
lookup response then redirects each client to the specific broker that owns the 
topic, which is accessed via the  [...]
+
+2. **A per-broker-pod service.** One service per broker pod that targets only 
that pod and exposes its external listener bind port. The external FQDN/IP and 
port of this per-pod service must exactly match the address the broker 
advertises for its `external` listener in `advertisedListeners`; otherwise the 
lookup response will return an address that clients cannot reach. Two options 
are described below — pick one based on your TLS and cost requirements.
+
+### Option 1: NodePort service per broker pod
+
+Create one NodePort service for each broker pod, mapping to the external 
listener binding ports for that pod. Clients reach brokers via 
`<node-ip-or-hostname>:<node-port>`.
+
+- Cost: no extra cloud infrastructure is required — traffic enters through 
ports on existing cluster nodes.
+- Network reachability: this option is suitable when clients reach broker 
nodes over a private network (for example, a VPC, on-prem network, or VPN). 
Because public internet exposure is not a supported configuration for Pulsar, 
firewall rules require special attention with NodePort: the chosen ports are 
opened on every node, so node-level firewalls, cloud security groups, and 
network policies must be reviewed to ensure those ports are reachable only from 
authorized client networks.
+- TLS challenges: the certificate presented by the broker must match the 
hostname or IP that clients actually connect to. Node IPs are often unstable in 
self-managed clusters, and hostname verification fails unless you provision 
stable DNS pointing to the nodes (or include IP SANs in the certificate). 
Reusing a single wildcard certificate across brokers requires every 
client-facing entry point to resolve under that wildcard domain, which is 
awkward when clients connect directly to node IPs.
+
+### Option 2: LoadBalancer service per broker pod
+
+Create one LoadBalancer service for each broker pod, mapping to the external 
listener binding ports for that pod.
+
+- Cost: each LoadBalancer is a separate cloud resource with a recurring cost. 
For N brokers you pay for N load balancers.
+- TLS is easier: you control a stable DNS name per LoadBalancer (for example, 
`pulsar-broker-0.example.com`) and issue certificates against those names. 
Clients connect by hostname, so standard hostname verification works without 
special configuration.
+
+### Targeting a specific broker pod (applies to both options)
+
+Regardless of which service type you choose, each per-pod service must select 
exactly one broker pod by including the `statefulset.kubernetes.io/pod-name` 
label in its selector (for example, `statefulset.kubernetes.io/pod-name: 
pulsar-broker-0`). This label is automatically applied by the StatefulSet 
controller to every pod and ensures traffic for a given broker's external 
listener port reaches the broker pod whose `advertisedListeners` configuration 
matches the service's external addres [...]
+
+### Example broker configuration for the LoadBalancer per broker pod case
 
 ```properties
-# Advertised listeners for internal and external access
-advertisedListeners=internal:pulsar://192.168.1.11:6650,internal:pulsar+ssl://192.168.1.11:6651,external:pulsar://198.51.100.17:30650,external:pulsar+ssl://198.51.100.17:31650
+# Advertised listeners — "internal" uses the StatefulSet headless service DNS 
name,
+# "external" uses the stable DNS of this broker's LoadBalancer
+advertisedListeners=internal:pulsar://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6650,internal:pulsar+ssl://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6651,external:pulsar://pulsar-broker-0.example.com:6650,external:pulsar+ssl://pulsar-broker-0.example.com:6651
 
-# Bind addresses with different ports for internal and external access
-bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar+ssl://0.0.0.0:16651
+# Bind addresses — "external" binds use a different local port range than 
"internal"
+bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar://0.0.0.0:16650,external:pulsar+ssl://0.0.0.0:16651
 
 # Specify the internal listener name
 internalListenerName=internal
@@ -132,7 +157,6 @@ internalListenerName=internal
 
 In the above example:
 
-- `192.168.1.11` is the pod IP for the particular broker pod. The IP or 
hostname should be dynamically set in this configuration at broker startup.
-- `198.51.100.17` is the external IP of the k8s node. In some cases this can 
be dynamically set based on the `status.hostIP` Kubernetes information.
-- `30650` and `31650` are the specific NodePorts allocated for this broker 
pod. This can be dynamically calculated from a base port number by adding the 
StatefulSet pod index 
(`metadata.labels['statefulset.kubernetes.io/pod-index']`) to it.
-
+- `pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local` is the 
per-pod FQDN provided by the StatefulSet's headless service (format: 
`<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local`). It is 
stable across pod restarts and re-resolves to the current pod IP, so it should 
be used for the `internal` listener instead of the pod IP. The pod's own FQDN 
is set dynamically per pod (typically via 
`metadata.labels['statefulset.kubernetes.io/pod-name']` and a known headle [...]
+- `pulsar-broker-0.example.com` is the stable DNS name assigned to the 
LoadBalancer service that targets this broker pod (using a 
`statefulset.kubernetes.io/pod-name: pulsar-broker-0` selector). Each broker 
pod has its own DNS name, set dynamically per pod.
+- The per-broker LoadBalancer maps external ports `6650` and `6651` to the 
broker's external listener bind ports `16650` and `16651`. Using the standard 
Pulsar ports externally lets the same DNS name and certificate work for both 
the cluster-wide service URL and the per-pod LoadBalancer.
\ No newline at end of file
diff --git 
a/versioned_docs/version-4.2.x/concepts-multiple-advertised-listeners.md 
b/versioned_docs/version-4.2.x/concepts-multiple-advertised-listeners.md
index 35499479c88..cf20ad7aad0 100644
--- a/versioned_docs/version-4.2.x/concepts-multiple-advertised-listeners.md
+++ b/versioned_docs/version-4.2.x/concepts-multiple-advertised-listeners.md
@@ -114,17 +114,42 @@ While Pulsar Proxy is generally recommended for 
Kubernetes deployments, multiple
 The Apache Pulsar Helm chart doesn't currently support this type of 
deployment. These instructions are provided as general guidance for using the 
`advertisedListeners` feature in Kubernetes deployments.
 There are multiple ways to handle this in Kubernetes deployments. Advertised 
listeners are also required in some service mesh configurations.
 
-NodePort deployment suggestions:
+> **Security notice**: Pulsar's design assumes the cluster sits behind network 
perimeter security; broker client and admin APIs are not safe to publish on the 
public internet. Use internal load balancers on private networks whenever 
possible. If external exposure is unavoidable, restrict access to authorized IP 
ranges with the Service's 
[`loadBalancerSourceRanges`](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip)
  [...]
 
-- Create individual NodePort (or LoadBalancer) services for each broker pod in 
a broker stateful set, mapping to the external listener binding ports.
-- Create a single NodePort (or LoadBalancer) service for the cluster to be 
used as the serviceUrl for clients, mapping to the external listener binding 
ports.
+A complete deployment requires two kinds of services:
+
+1. **A cluster-wide service URL.** A LoadBalancer service (for example, 
`external-brokers.example.com`) that fronts all healthy broker pods. The 
service's external port can be the standard Pulsar TLS port `6651`, which the 
LoadBalancer maps to the brokers' external listener bind port (for example, 
`16651`). Clients use this address as their `serviceUrl` for topic lookups; the 
lookup response then redirects each client to the specific broker that owns the 
topic, which is accessed via the  [...]
+
+2. **A per-broker-pod service.** One service per broker pod that targets only 
that pod and exposes its external listener bind port. The external FQDN/IP and 
port of this per-pod service must exactly match the address the broker 
advertises for its `external` listener in `advertisedListeners`; otherwise the 
lookup response will return an address that clients cannot reach. Two options 
are described below — pick one based on your TLS and cost requirements.
+
+### Option 1: NodePort service per broker pod
+
+Create one NodePort service for each broker pod, mapping to the external 
listener binding ports for that pod. Clients reach brokers via 
`<node-ip-or-hostname>:<node-port>`.
+
+- Cost: no extra cloud infrastructure is required — traffic enters through 
ports on existing cluster nodes.
+- Network reachability: this option is suitable when clients reach broker 
nodes over a private network (for example, a VPC, on-prem network, or VPN). 
Because public internet exposure is not a supported configuration for Pulsar, 
firewall rules require special attention with NodePort: the chosen ports are 
opened on every node, so node-level firewalls, cloud security groups, and 
network policies must be reviewed to ensure those ports are reachable only from 
authorized client networks.
+- TLS challenges: the certificate presented by the broker must match the 
hostname or IP that clients actually connect to. Node IPs are often unstable in 
self-managed clusters, and hostname verification fails unless you provision 
stable DNS pointing to the nodes (or include IP SANs in the certificate). 
Reusing a single wildcard certificate across brokers requires every 
client-facing entry point to resolve under that wildcard domain, which is 
awkward when clients connect directly to node IPs.
+
+### Option 2: LoadBalancer service per broker pod
+
+Create one LoadBalancer service for each broker pod, mapping to the external 
listener binding ports for that pod.
+
+- Cost: each LoadBalancer is a separate cloud resource with a recurring cost. 
For N brokers you pay for N load balancers.
+- TLS is easier: you control a stable DNS name per LoadBalancer (for example, 
`pulsar-broker-0.example.com`) and issue certificates against those names. 
Clients connect by hostname, so standard hostname verification works without 
special configuration.
+
+### Targeting a specific broker pod (applies to both options)
+
+Regardless of which service type you choose, each per-pod service must select 
exactly one broker pod by including the `statefulset.kubernetes.io/pod-name` 
label in its selector (for example, `statefulset.kubernetes.io/pod-name: 
pulsar-broker-0`). This label is automatically applied by the StatefulSet 
controller to every pod and ensures traffic for a given broker's external 
listener port reaches the broker pod whose `advertisedListeners` configuration 
matches the service's external addres [...]
+
+### Example broker configuration for the LoadBalancer per broker pod case
 
 ```properties
-# Advertised listeners for internal and external access
-advertisedListeners=internal:pulsar://192.168.1.11:6650,internal:pulsar+ssl://192.168.1.11:6651,external:pulsar://198.51.100.17:30650,external:pulsar+ssl://198.51.100.17:31650
+# Advertised listeners — "internal" uses the StatefulSet headless service DNS 
name,
+# "external" uses the stable DNS of this broker's LoadBalancer
+advertisedListeners=internal:pulsar://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6650,internal:pulsar+ssl://pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local:6651,external:pulsar://pulsar-broker-0.example.com:6650,external:pulsar+ssl://pulsar-broker-0.example.com:6651
 
-# Bind addresses with different ports for internal and external access
-bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar+ssl://0.0.0.0:16651
+# Bind addresses — "external" binds use a different local port range than 
"internal"
+bindAddresses=internal:pulsar://0.0.0.0:6650,internal:pulsar+ssl://0.0.0.0:6651,external:pulsar://0.0.0.0:16650,external:pulsar+ssl://0.0.0.0:16651
 
 # Specify the internal listener name
 internalListenerName=internal
@@ -132,7 +157,6 @@ internalListenerName=internal
 
 In the above example:
 
-- `192.168.1.11` is the pod IP for the particular broker pod. The IP or 
hostname should be dynamically set in this configuration at broker startup.
-- `198.51.100.17` is the external IP of the k8s node. In some cases this can 
be dynamically set based on the `status.hostIP` Kubernetes information.
-- `30650` and `31650` are the specific NodePorts allocated for this broker 
pod. This can be dynamically calculated from a base port number by adding the 
StatefulSet pod index 
(`metadata.labels['statefulset.kubernetes.io/pod-index']`) to it.
-
+- `pulsar-broker-0.pulsar-broker-headless.pulsar.svc.cluster.local` is the 
per-pod FQDN provided by the StatefulSet's headless service (format: 
`<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local`). It is 
stable across pod restarts and re-resolves to the current pod IP, so it should 
be used for the `internal` listener instead of the pod IP. The pod's own FQDN 
is set dynamically per pod (typically via 
`metadata.labels['statefulset.kubernetes.io/pod-name']` and a known headle [...]
+- `pulsar-broker-0.example.com` is the stable DNS name assigned to the 
LoadBalancer service that targets this broker pod (using a 
`statefulset.kubernetes.io/pod-name: pulsar-broker-0` selector). Each broker 
pod has its own DNS name, set dynamically per pod.
+- The per-broker LoadBalancer maps external ports `6650` and `6651` to the 
broker's external listener bind ports `16650` and `16651`. Using the standard 
Pulsar ports externally lets the same DNS name and certificate work for both 
the cluster-wide service URL and the per-pod LoadBalancer.
\ No newline at end of file

Reply via email to