skip to content

What is the difference between listeners and advertised.listeners, and why does a misconfigured advertised.listeners break clients even when the broker starts fine?

level: middleimportance: must knowfreq 65%

answer

  1. bind vs publish
  2. bootstrap fetches metadata; real conn uses advertised
  3. never advertise 0.0.0.0
  4. Docker/K8s/NAT classic break
  5. broker boots OK; clients still fail

basics

~20 s

listeners is where the broker binds and accepts connections. advertised.listeners is the address the broker hands back to clients in metadata so they can reconnect. If the advertised address is unreachable, clients connect once for metadata, then fail to reach the broker.

solid answer

~40 s

`listeners` defines the host:port and protocol the broker actually **binds** to and listens on (e.g. PLAINTEXT://0.0.0.0:9092). `advertised.listeners` is what the broker **publishes in metadata responses** — the address clients should use to reach this broker for produce/fetch. After a client bootstraps, it asks for cluster metadata; the broker returns advertised addresses, and the client opens new connections to those. So even if a broker boots cleanly (listeners bound), a wrong advertised address (e.g. advertising an internal Docker hostname or 0.0.0.0) means clients get a metadata response pointing somewhere they can't reach, and produce/consume hangs or fails with connection errors. This is the classic Docker/Kubernetes/NAT pitfall. The fix is to advertise the externally-reachable address per listener, using distinct listener names mapped through listener.security.protocol.map for internal vs external networks.

go deeper

for a junior

Know listeners = bind address, advertised.listeners = address given back to clients.

for a middle

Explain the bootstrap-then-metadata flow and diagnose the 'broker starts but clients fail' Docker/K8s symptom.

for a senior

Design multi-listener internal/external topologies with correct advertised addresses and per-listener protocols.

for a principal

Reason about per-broker advertised addressing in NodePort/LoadBalancer/NAT topologies and templating it safely at scale.

Kafka's connection model has **two phases**, and the two listener configs serve those two phases. **Phase 1 — Bootstrap.** A client is configured with `bootstrap.servers`, a seed list of broker addresses. It connects to one of them just to fetch **cluster metadata**: which brokers exist, which broker leads each topic-partition, and crucially *what address to use to reach each broker*. **Phase 2 — Real traffic.** Using that metadata, the client opens **new connections directly to the leader broker** for each partition it produces to or consumes from. It does NOT keep using the bootstrap address; it uses whatever address the metadata told it. Now the two configs: - **`listeners`** — the set of endpoints the broker process actually **binds a socket to** and accepts connections on. Format: `name://host:port`, e.g. `PLAINTEXT://0.0.0.0:9092`. Binding to `0.0.0.0` means 'all local interfaces' and is fine *for binding*. - **`advertised.listeners`** — the endpoints the broker **reports in metadata** so clients know how to reach it in Phase 2. This MUST be an address that clients can actually resolve and route to. You must NOT advertise `0.0.0.0` (clients would try to connect to 0.0.0.0). If unset, it defaults to the value of `listeners` (which is why advertising 0.0.0.0 accidentally happens). **Why a broker can start fine yet clients fail:** Broker startup only validates that it can bind `listeners`. It never checks whether `advertised.listeners` is reachable from clients. So a broker advertising `kafka-internal:9092` (a Docker-internal hostname) starts perfectly. A client outside Docker bootstraps via `localhost:9092`, succeeds, receives metadata saying 'reach me at kafka-internal:9092', tries to open a Phase-2 connection, and fails to resolve or route there. Symptoms: bootstrap works, then produce/consume times out or throws connection-refused/unknown-host. **The standard solution (multi-listener):** Define separate listeners for separate networks, each advertised correctly: ``` listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:9094 advertised.listeners=INTERNAL://kafka:9092,EXTERNAL://broker.example.com:9094 listener.security.protocol.map=INTERNAL:SSL,EXTERNAL:SASL_SSL inter.broker.listener.name=INTERNAL ``` Internal clients/brokers use the INTERNAL address; external clients get the routable EXTERNAL hostname. Each listener can also carry a different security protocol, hardening the externally-exposed path. **Edge cases:** In Kubernetes with NodePort/LoadBalancer per broker, you typically need per-broker advertised addresses (often templated). With NAT, the advertised address must be the public/NATed address, not the broker's private IP. Port and host in advertised.listeners can differ from the bind port (useful behind a proxy/load balancer doing port mapping).

  • A broker starts cleanly but external producers time out after a successful bootstrap. What's your first hypothesis?
    advertised.listeners points to an address the producer can't reach (e.g. an internal hostname or 0.0.0.0). Bootstrap succeeded but the Phase-2 metadata address is unroutable from the client.
  • Why must you never set advertised.listeners to 0.0.0.0?
    0.0.0.0 is a bind-only wildcard meaning 'all local interfaces'. If advertised, clients literally try to connect to 0.0.0.0, which is not a routable destination, so all post-bootstrap connections fail.

saying these in an interview costs you the question

  • Saying clients keep talking to the bootstrap server for all traffic (they switch to advertised addresses after metadata)
  • Claiming the broker validates advertised.listeners reachability at startup (it does not)
  • Treating listeners and advertised.listeners as interchangeable
  • Advertising 0.0.0.0 or an internal Docker hostname to external clients

context