Kafka Bootstrap Servers: How Clients Connect

Stéphane Derosiaux October 1, 2026 14 min read

Kafka bootstrap servers are the host:port addresses a client contacts first to discover a cluster. The client sends one Metadata request to any of them, receives the list of every broker with the address each one advertises, and from then on connects directly to partition leaders. The bootstrap list is an entry point, not the broker list.

How a Kafka client uses bootstrap.servers: it connects to any address in the list (kafka-1), receives Metadata listing every broker with its advertised address and the partition leaders, then produces and fetches directly to each leader, including kafka-3, which is not in the bootstrap list.

What is bootstrap.servers?

bootstrap.servers is a client setting used by producers, consumers, admin clients, Kafka Streams, and Kafka Connect. It contains a comma-separated list of host:port pairs.

Any broker can be a bootstrap server. Every broker holds the full cluster metadata, so the list does not need to include a partition leader or a "primary" node. The list is only used to reach a first broker: once metadata arrives, the client sends data traffic to the addresses the brokers advertise.

# one address is enough to work; two or three survive a broker outage
bootstrap.servers=kafka-1.payments.internal:9092,kafka-2.payments.internal:9092,kafka-3.payments.internal:9092

How Kafka clients connect through bootstrap servers

On its first connection, a client runs these steps:

  1. Connect to one entry in bootstrap.servers. Client logs use a negative node id such as node -1 because the broker is not known yet.
  2. Negotiate and authenticate. The TLS handshake completes first when TLS is configured, then the client sends ApiVersions to learn which protocol versions the broker supports, then runs the SASL exchange if SASL is enabled.
  3. Fetch metadata. The response lists broker addresses and the leader and replicas of each requested partition.
  4. Connect to leaders. Produce and fetch requests go to the brokers named in the metadata. Consumers also discover their group coordinator. What's really inside Kafka's poll() covers the full consumer startup sequence.

Kafka bootstrap servers handshake: bootstrap connection, metadata response, direct connections to partition leaders

A client can therefore bootstrap fine and still fail at step 4: every address in the metadata must also be reachable from the client network.

Why the bootstrap list is not the broker list

List two or three entries. One entry fails at startup when that broker is down; listing every broker adds nothing after the first connection. Avoid per-broker or vendor-specific hostnames hardcoded in application code: with them, every broker replacement, migration, or provider change means editing every client; hardcoded bootstrap servers are the fastest path to lock-in. Use a DNS name or proxy endpoint the platform team controls, and inject it from configuration rather than compiling it in.

Failure cases:

  • A bootstrap broker is down at startup. The client shuffles the list and tries entries until one answers, so the order in the config is not a priority order. Only when no entry answers does it fail with a timeout.
  • A bootstrap broker is removed later. Running clients already have metadata and do not notice, but new or restarted clients fail if the list only named that broker. An owned DNS name is repointed once instead of editing every client config.
  • All known brokers become unreachable. Since Kafka 4.0, metadata.recovery.strategy defaults to rebootstrap (KIP-1102). A running client returns to bootstrap.servers when every broker it knows is unreachable, when no broker returns metadata for metadata.recovery.rebootstrap.trigger.ms (5 minutes by default), or when a broker asks it to rebootstrap. The rebootstrap option itself was introduced by KIP-899 in Kafka 3.9.
  • The bootstrap DNS record is repointed during a migration or failover. Running clients stay where they are. They read bootstrap.servers only at startup and on rebootstrap; until then they keep using the broker addresses from their last metadata, over open connections that a DNS change does not close. Clients move when they restart or when the old brokers become unreachable and they rebootstrap, which is why DR plans built on a DNS flip also need a way to force reconnections.

A single DNS name can resolve to several broker IPs. With the default client.dns.lookup=use_all_dns_ips, the client tries each returned IP until one connects.

Clients also refresh metadata every metadata.max.age.ms (5 minutes by default) and after any leadership error, from brokers they already know. Picking up leader moves and new partitions does not need the bootstrap list.

listeners vs advertised.listeners

  • listeners is what the broker binds to: interface and port. PLAINTEXT://0.0.0.0:9092 accepts connections on every interface.
  • advertised.listeners is what the broker puts in the metadata response: the address clients and other brokers should use. It must be a real, reachable address, not 0.0.0.0.

The classic bug is a broker in Docker or Kubernetes that advertises localhost:9092. A client on the host can connect, but a client in another container or pod receives localhost in the metadata and tries to connect to itself. A positive node id in the error, such as node 1, confirms that bootstrap worked and the advertised address failed. Advertise a name the client can resolve and reach:

# broker: bind everywhere, advertise the name clients will use
listeners=PLAINTEXT://0.0.0.0:9092
advertised.listeners=PLAINTEXT://kafka-1.payments.internal:9092

A broker that serves both other containers and the Docker host needs two listeners. The Compose service name does not resolve on the host, and localhost inside a container is the container itself. The settings are explained in the next section:

# single-node KRaft broker in Docker Compose, service "kafka", port 29092 published to the host
listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093
advertised.listeners=INTERNAL://kafka:9092,EXTERNAL://localhost:29092
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT
inter.broker.listener.name=INTERNAL
controller.listener.names=CONTROLLER

Containers use bootstrap.servers=kafka:9092; applications on the host use bootstrap.servers=localhost:29092.

For a step-by-step walkthrough of this setting on Docker and cloud hosts, see Kafka advertised host setting.

Multiple listeners and security protocols

To serve other brokers on a private interface, applications inside the VPC, and clients outside it, a broker declares several named listeners. Each has its own port, advertised address, and security protocol.

listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:9093
advertised.listeners=INTERNAL://kafka-1.payments.internal:9092,EXTERNAL://kafka-1.payments.example.com:9093
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:SASL_SSL,CONTROLLER:PLAINTEXT
inter.broker.listener.name=INTERNAL
controller.listener.names=CONTROLLER

listener.security.protocol.map maps each listener name to a protocol such as PLAINTEXT or SASL_SSL. inter.broker.listener.name selects the listener used between brokers. controller.listener.names names the listener used to reach the KRaft controllers. On a KRaft broker, every name in controller.listener.names must also appear in listener.security.protocol.map once that map is set explicitly; otherwise the broker refuses to start with Controller listener with name CONTROLLER defined in controller.listener.names not found in listener.security.protocol.map.

A client receives addresses for the listener it used during bootstrap. A client connecting on port 9093 gets the EXTERNAL hostnames; a client on 9092 gets the INTERNAL ones. Each network only sees addresses it can reach. See Kafka authentication and mTLS for Kafka for the security settings.

Kafka behind a load balancer, NAT, or proxy

A route between two networks (VPC peering, a transit gateway) is not the same as Kafka reachability. The client must resolve and reach every advertised broker address from where it sits, so connecting Kafka across networks is an addressing problem, not a routing one. A load balancer can provide a stable bootstrap address because any broker can answer the metadata request, but a single TCP load balancer cannot carry the traffic after that: internal advertised addresses are unreachable from outside, and one shared advertised address sends connections to random brokers.

Three setups give each client a path to a specific broker:

  • One routable address per broker. Each broker advertises its own external hostname, backed by a load balancer or NAT rule. Each broker then needs its own DNS record, certificate name, and load balancer or NAT entry. On KRaft clusters advertised.listeners is a static setting, so every address change means a rolling restart. Managed services often do not let users change advertised.listeners; this MSK cross-VPC guide covers that case.
  • SNI routing. All brokers share one address and one TLS port. Each broker is advertised under a distinct hostname (b1.kafka.example.com, b2.kafka.example.com). The client puts that hostname in the TLS ClientHello (Server Name Indication), and a TLS-passthrough router in front sends the connection to the right broker without decrypting it. This needs TLS on that listener and a certificate that covers every broker name. SNI is a routing hint sent in cleartext, not authentication: SASL or mTLS still decides who may connect.
  • A Kafka-protocol proxy. The proxy rewrites broker addresses in Metadata and FindCoordinator responses, mapping each internal broker to a client-facing address. Conduktor Gateway supports one port per broker or one hostname per broker with SNI routing; see Kafka proxies compared.

Kafka behind a load balancer vs a Kafka-protocol proxy rewriting advertised addresses

KRaft: bootstrap.servers vs bootstrap.controllers

In KRaft mode, controllers keep cluster metadata but do not serve producers or consumers. Regular clients must use broker listeners in bootstrap.servers; a controller listener returns The node does not support METADATA.

Admin tools can use --bootstrap-controller (config key bootstrap.controllers) for a small set of controller operations, such as describing the quorum:

kafka-metadata-quorum.sh --bootstrap-controller localhost:9093 describe --status
# (excerpt)
# LeaderId: 1
# CurrentVoters: [{"id": 1, "endpoints": ["CONTROLLER://localhost:9093"]}]

Do not set bootstrap.servers and bootstrap.controllers on the same admin client. See KRaft mode for the controller architecture.

Troubleshooting connection errors

The Java client logs most network problems as the same Connection to node ... warning. Read the node id (negative = bootstrap, positive = advertised address) and the address in it. Messages below were captured on Apache Kafka 4.3.1 unless marked otherwise.

MessageStageLikely cause
Couldn't resolve server ... from bootstrap.servers as DNS resolution failed for , then No resolvable bootstrap urls given in bootstrap.serversClient startupNo hostname in bootstrap.servers resolves: a typo, or a Docker service name used from the host. The client fails before any connection attempt.
Connection to node -1 (host/ip:port) could not be established. Node may not be available.Bootstrap (negative node id)Wrong host or port in bootstrap.servers, broker down, firewall, or connection refused. Nothing Kafka-specific yet.
Connection to node 1 (localhost/127.0.0.1:9092) could not be established. after a successful startAfter bootstrap (real broker id)advertised.listeners names an address the client cannot reach: localhost, a Docker service name, a private IP.
Timed out waiting for a node assignment. / Expiring N record(s) for orders-0After bootstrapSame cause as above; the admin or producer request never reached the leader.
Bootstrap broker kafka-1:9093 (id: -1 rack: null isFenced: false) disconnectedBootstrapThe port accepts the TCP connection but closes it instead of answering: PLAINTEXT client on an SSL or SASL listener, or a non-Kafka service on that port.
The node does not support METADATABootstrapThe address is a KRaft controller listener, not a broker listener.
SSLHandshakeException ... No subject alternative DNS name matching ... (JVM message, not reproduced)Any TLS connectionThe advertised hostname is not in the broker certificate's SANs; frequent with SNI routing or per-broker external names.
NOT_LEADER_OR_FOLLOWER on most produce and fetch requests (not reproduced)After bootstrapAll advertised addresses point at one shared load balancer address; connections land on arbitrary brokers.
What is a bootstrap server in Kafka?

A bootstrap server is any broker address a client uses to make its first connection and fetch cluster metadata. Any broker can play this role, since every broker knows the full cluster layout. After that first exchange the client connects directly to partition leaders using the addresses brokers advertise.

Is the bootstrap server the same as a broker?

A bootstrap server is a broker, but the bootstrap list is not the broker list. The client only uses bootstrap.servers to find one live broker; the actual set of brokers, and the addresses used for produce and fetch traffic, come from the metadata response and from each broker's advertised.listeners.

How many bootstrap servers should I list?

Two or three. One is enough for the handshake, but the client cannot start if that broker is down. Listing every broker adds nothing after the first connection and makes client configs churn each time a broker is replaced. A DNS name resolving to several brokers works as a single entry.

Why does my client connect on localhost but time out from Docker or Kubernetes?

The broker advertises localhost:9092 in its metadata. Clients on the same host can reach that; clients in another container or pod cannot. The client log shows "Connection to node 1 (localhost/127.0.0.1:9092) could not be established". Set advertised.listeners to a hostname every client can resolve.

Can I put a load balancer in front of Kafka?

Not a plain TCP load balancer on its own. Clients must reach specific brokers after bootstrap, so a single shared address either exposes unreachable internal names or sends requests to the wrong leader. Use one address per broker, SNI routing over TLS, or a Kafka-protocol proxy that rewrites the addresses in the metadata response.

What is the difference between --bootstrap-server and --bootstrap-controller?

--bootstrap-server targets broker listeners and is what producers, consumers and most admin commands use. --bootstrap-controller (Kafka 3.7+, KIP-919) targets the KRaft controller quorum for a small set of admin operations, such as describing the quorum, and is meant for diagnostics when brokers are unavailable.

Sources and References