Skill 56 · Managing Amazon Msk
Subchapter 56.2
references/configure-cluster.mdMarkdown11 KBView on GitHub
Broker-level and cluster-level configuration for Amazon MSK Provisioned clusters: creating and applying MSK configurations (server.properties), and setting custom domain names on the brokers with .
Scripts
Msk Sizingcustom.advertised.listenersFor the client-side layer that fronts a custom domain (NLB, ACM certificate, Route 53, TLS handshake, mTLS), see configure-clients.md. For per-instance-size thread tuning and durability defaults, see size-and-choose-cluster.md.
Note: The AWS MCP server is the recommended way to run the AWS API interactions in this reference (
kafkadescribe/update calls,kafka-configs.sh, etc.) — it provides sandboxed execution, audit logging, and observability. It is not a hard requirement; when the MCP server is unavailable, fall back to the AWS CLI or shell.
Use a custom domain name (e.g., b-1.example.com) when clients need a static, customer-controlled endpoint that survives cluster recreation, migration, or DR failover, or must align with organizational DNS/naming. MSK brokers otherwise advertise AWS-generated addresses (b-1.<cluster>.<id>.kafka.<region>.amazonaws.com) that change when a cluster is recreated. This section covers the cluster configuration that sets the advertised address; the NLB / certificate / DNS layer that must exist first is in configure-clients.md.
Recommended approach: a single, validated, cluster-wide MSK configuration property. Set it once and MSK resolves it per broker and reapplies it automatically as the cluster scales. This replaces the per-broker kafka-configs.sh --alter --add-config advertised.listeners=... override.
advertised.listeners override could not be applied on KRaft clusters; custom.advertised.listeners works on both. If asked whether custom domains are possible on a KRaft cluster, the answer is yes — via this property. Do NOT recommend the per-broker kafka-configs.sh override or self-managed Kafka as the current solution, and do NOT claim custom domains are impossible on KRaft.Value format:
custom.advertised.listeners=<LISTENER>://<hostname>:<port>Example (three-broker IAM cluster fronted by an NLB on ports 9001-9003):
custom.advertised.listeners=CLIENT_IAM://b-{broker_id}.example.com:9000+{broker_id}CLIENT, CLIENT_SECURE, CLIENT_SECURE_PUBLIC, CLIENT_SASL_SCRAM, CLIENT_SASL_SCRAM_PUBLIC, CLIENT_IAM, CLIENT_IAM_PUBLIC. Internal listeners REPLICATION and CONTROLLER are rejected — this protects the listeners MSK manages. A rejected REPLICATION://... config is not a syntax problem with the + separator or offset; it is rejected because internal listeners are not allowed.CLIENT_IAM on an IAM cluster). Specifying an unbound listener (e.g., CLIENT_SECURE on an IAM-only cluster) is rejected, and the error lists the valid client listeners for your cluster.{broker_id} template + uniqueness. {broker_id} is replaced with each broker’s numeric ID at apply time. In a port expression like 9000+{broker_id}, the broker ID is added to the base port, so broker 1 -> 9001, broker 2 -> 9002, broker 10 -> 9010 (9000 is just an example base). Each broker’s resolved host:port must be unique. {broker_id} may appear in the hostname, the port, or both — so a shared hostname with a per-broker port (CLIENT_IAM://example.com:9000+{broker_id}) is valid, but a shared hostname AND shared port for every broker fails validation. The resulting ports must match the TLS listeners provisioned on your NLB.Put the property in a file (leave {broker_id} literal — MSK resolves it per broker; do not substitute IDs yourself). It can live alongside other broker properties in a single configuration revision.
Create/update the configuration, passing the file with fileb:// (not file://) so the CLI reads it as bytes and base64-encodes it — passing inline or with file:// is fragile because of the {broker_id} braces:
aws kafka create-configuration --name custom-domain-iam \
--server-properties fileb://custom-domain-config.txt(For an existing configuration, add the property and create a new revision with update-configuration.)
Apply the returned configuration ARN and revision to the cluster:
aws kafka update-cluster-configuration \
--cluster-arn <arn> \
--configuration-info arn=<config-arn>,revision=<revision> \
--current-version <current-cluster-version>MSK validates, resolves the pattern per broker, and applies it via a rolling restart.
Track the rollout with describe-cluster-operation-v2 (or the DescribeOperation API): states go UPDATE_IN_PROGRESS -> UPDATE_COMPLETE/SUCCESS or UPDATE_FAILED/FAILED.
Auditability: Ensure CloudTrail is logging MSK API calls (
kafka:UpdateClusterConfiguration,kafka:UpdateConfiguration) so configuration changes are auditable.
Rollout failure recovery. If a broker fails to start, the rollout halts at that broker and the remaining brokers keep their previous configuration — the cluster is not left half-configured, and no recreation is needed. Fix the property (common cause: the resolved host:port isn’t resolvable/reachable/trusted via your NLB+DNS+cert) and re-apply.
Reverting. Fully reversible: remove custom.advertised.listeners from the configuration (new revision without it) and re-apply. MSK reverts the listener to its original AWS-generated advertised address via a rolling restart. Only the advertised address of the named listener is affected — REPLICATION, authentication, multi-VPC (CLIENT_IAM_VPCE), and PrivateLink connectivity are unaffected.
Existing custom-domain setups configured the advertised address dynamically, per broker, with kafka-configs.sh --alter --add-config advertised.listeners=[...] — repeated on every broker and re-run whenever a broker was added, hand-preserving the REPLICATION/REPLICATION_SECURE (and any multi-VPC) entries each time. Migrating to the static custom.advertised.listeners property removes that per-broker toil, adds up-front validation, works on KRaft, and flows through IaC. The networking layer you already built (NLB, DNS, certificate) stays exactly as is — only how the advertised address is configured changes.
Capture the current pattern. On each broker, read the existing dynamic value so you can reproduce it exactly:
kafka-configs.sh --bootstrap-server $BS --entity-type brokers --entity-name <broker-id> \
--command-config client.properties --all --describe | grep advertised.listenersNote the client listener name and the host:port each broker advertises (e.g., CLIENT_SASL_SCRAM://b-1.example.com:9001) and confirm it fits a {broker_id} template such as b-{broker_id}.example.com:9000+{broker_id}.
Include only the client listener in the property. Unlike the dynamic override — where you had to include and preserve REPLICATION/REPLICATION_SECURE (and multi-VPC) entries yourself — custom.advertised.listeners manages only the named client listener, and MSK preserves the internal, multi-VPC (CLIENT_IAM_VPCE), and PrivateLink listeners for you. Do NOT put REPLICATION/CONTROLLER in the property (they are rejected).
Match the existing address for a zero-cutover migration. Choose the template so each broker’s resolved host:port equals what it already advertises. Then the advertised address does not change and clients keep connecting with no cutover. If you deliberately change the pattern, clients cut over on their next metadata refresh — build and verify the new NLB listeners, target groups, and DNS records first (see configure-clients.md).
Apply via the configuration, not kafka-configs.sh. Add custom.advertised.listeners to a new MSK configuration or, if you already manage one for the cluster, add it and create a new revision with update-configuration; then apply with update-cluster-configuration (see Apply workflow above). From now on, manage the advertised address only through the MSK configuration, not with per-broker dynamic overrides.
Clear any stale dynamic override and verify the effective value. A per-broker dynamic config can take precedence over broker configuration in Kafka, so after applying, confirm the effective advertised.listeners with the --describe command from step 1. If a leftover dynamic override remains, delete it with kafka-configs.sh --alter --entity-type brokers --entity-name <broker-id> --delete-config advertised.listeners — do this only after step 3 guarantees the resolved address is identical, so removal changes nothing for clients.
Confirm and finish. Ensure the operation reaches SUCCESS (describe-cluster-operation-v2) and clients still connect (kafka-topics.sh --list --bootstrap-server <custom-bootstrap>). Going forward MSK reapplies the property to new/replaced brokers automatically, so you no longer re-run the per-broker override (you still add NLB/DNS entries for new brokers).
KRaft note: if the dynamic override was never an option because the cluster is in KRaft mode, the static property is the supported path and works on KRaft. The migration is reversible — remove the property and re-apply to return to the AWS-generated address.
When you add brokers (or a broker is replaced during automated healing), MSK automatically applies the configuration to the new broker, resolving {broker_id} for its ID — nothing to do on the cluster side. The networking layer does NOT auto-scale, however: you must add the corresponding NLB listener, target group, and DNS record for each new broker. See configure-clients.md.
Applying this property changes what the brokers advertise, so clients cut over to the custom domain on their next metadata refresh. If the NLB, DNS, and trusted certificate are not already in place, connected clients cannot reconnect. Build that layer first and follow the safe two-phase cutover documented in configure-clients.md.
custom.advertised.listeners property