This is the multi-page printable view of this section. .
Module: KAFKA
- 1: Quick Start
- 2: Configuration
- 3: Parameters
- 4: Administration
- 5: Playbook
- 6: Monitoring
- 7: Metrics
- 8: FAQ
Kafka is a distributed event-streaming platform. Pigsty’s KAFKA module installs Apache Kafka 4.1+ dynamic KRaft clusters on managed nodes from RPM/DEB packages. It manages security, resources, lifecycle, and observability.
Kafka is a Beta module. Before production use, test it thoroughly and verify that it meets your requirements. Test dynamic KRaft, strict rolling restarts, TLS/SCRAM/ACL, declared topics and users, credential and certificate rotation, and the complete monitoring pipeline.
Module Capabilities
The KAFKA module currently provides:
- Native dynamic KRaft, without ZooKeeper or static
controller.quorum.voters. - Native roles
combined,broker, andcontroller, with combined or separate control and data planes. - Random Cluster ID and Controller Directory IDs for new clusters. A minimal bootstrap manifest freezes these values and blocks conflicting operations.
- Lifecycle selection from live health: cold start or repair, serial broker admission, dynamic controller join, or strict single-node rolling restart.
- Checks before and after each rolling step: controller majority, voter catch-up, offline partitions, under-min-ISR, and ISR catch-up.
- Member retirement and failed-node replacement through playbooks.
kafka-rm.ymlretires a strict subset, including dead nodes. Node replacement uses three commands. plaintextand productionscramprofiles. The latter provides TLS, SCRAM-SHA-512, controller mTLS, ACLs, and default-deny authorization.- Declarative topics, credentials, ACLs, and quotas without implicit deletion. Protected internal credential and certificate rotation.
- JMX and protocol exporters, 19 recording rules, 15 alert rules, four Grafana dashboards, and VictoriaLogs storage.
Module Architecture
NODE provides node management, package repositories, and base monitoring. INFRA provides VictoriaMetrics, VictoriaLogs, Grafana, and Alertmanager. KAFKA depends on both.
flowchart LR
admin["Pigsty admin node"] -->|"kafka.yml / exact cluster"| kafka["Kafka 4.1+ / dynamic KRaft"]
kafka --> jmx["Each Kafka JVM / JMX :9404"]
kafka --> exporter["Up to two brokers / kafka_exporter :9308"]
kafka --> journal["Journald"]
jmx --> vm["VictoriaMetrics"]
exporter --> vm
journal --> vector["Vector"] --> vl["VictoriaLogs"]
vm --> grafana["Grafana"]
vl --> grafana
vm --> alert["Alertmanager"]
style kafka fill:#70C1B3,stroke:#4f968b,color:#fff
style vm fill:#E66B7A,stroke:#b84e5c,color:#fff
style vl fill:#C98367,stroke:#9e634e,color:#fff
style grafana fill:#F29C64,stroke:#c77845,color:#fffEvery Kafka JVM includes JMX Exporter and registers as job=kafka. kafka_exporter runs on the first two broker-capable nodes, ordered by kafka_seq. A single broker runs one exporter; pure controllers run none. Both exporters report the same logical cluster. Recording rules deduplicate their metrics before aggregation.
Documentation
| Document | Contents |
|---|---|
| Quickstart | From a single node to a three-node secure cluster: client access, parameter changes, and go-live checks |
| Cluster Config | Topology, dynamic KRaft, network, storage, security, and resource declarations |
| Parameters | 15 persistent public parameters plus transient operational variables |
| Administration | Status checks, topics, messages, consumer groups, and topology changes |
| Playbook | kafka.yml lifecycle, task tags, rotation, and teardown safeguards |
| Monitoring | Metrics pipeline, dashboards, log queries, and alert rules |
| Metrics | Metric dictionary for JMX, the protocol exporter, and recording rules |
| FAQ | Questions on roles, identity, security, exporters, and scaling |
First Time
Follow Quickstart to build a single-node development cluster, then a three-node TLS/SCRAM/ACL cluster. Continue with client access, parameter and resource changes, and deployment checks.
If you know Kafka and Pigsty, start with Cluster Config or Parameters.
Default Ports
| Port | Service | Deployment scope | plaintext | scram |
|---|---|---|---|---|
9092 | Kafka Broker | Broker-capable nodes | PLAINTEXT | SASL_SSL + SCRAM-SHA-512 |
9093 | KRaft Controller | Controller-capable nodes | PLAINTEXT | Mutual TLS |
9308 | kafka_exporter | Up to two broker-capable nodes | HTTP metrics | HTTP metrics, TLS/SCRAM on the backend |
9404 | JMX Exporter | All Kafka nodes | HTTP metrics | HTTP metrics |
All four ports must be different. Parameters can change each port. Restrict JMX and protocol exporter HTTP ports to the monitoring network through firewalls.
Current Boundaries
This role provides a core Kafka deployment baseline. A complete streaming platform or managed service requires additional components or procedures for:
- Existing partition reassignment and replica balancing after expansion. Playbooks manage member join, retirement, and replacement, but data movement needs an explicit plan.
- Increasing the frozen
default.replication.factorafter expansion. Kafka 4.3 requires explicit data migration and a static-configuration maintenance window. - Changing existing topic replication factors, deleting topics, and deleting users.
- Migrating formatted clusters online from
plaintexttoscram. - Kafka upgrades, feature-level finalization, backup, restore, and disaster exercises.
- Multiple listeners, NAT or public addresses, multiple client networks per broker, and tiered storage.
- Kafka Connect, Schema Registry, MirrorMaker 2, Cruise Control, and web interfaces.
Document these limits in production plans, approval processes, and exercises. Running the ordinary inventory again does not perform these operations.
1 - Quick Start
First build a single-node cluster, create a topic, and read and write messages. Then build a separate secure three-node cluster with users, ACLs, quotas, and production topics. Finally, change parameters, connect clients, verify monitoring, and perform pre-launch checks.
Start before deploying Kafka. You need a working Pigsty administration node with INFRA installed. Otherwise, complete quick installation first.
Target nodes need SSH/sudo access and must be manageable by NODE.
Learning Path
| Stage | Goal | End Result |
|---|---|---|
| 1 | Deploy a single-node dev cluster | One combined node, PLAINTEXT, an RF=1 topic, CLI read/write |
| 2 | Deploy a three-node secure HA demo baseline | Three combined nodes, dynamic KRaft, TLS/SCRAM/ACL, RF=3/minISR=2 |
| 3 | Connect application clients | Produce/consume using an application principal, the Pigsty CA, and SASL_SSL |
| 4 | Change core parameters | Walk through heap, broker parameters, topic partitions/retention, and a secure rolling restart |
| 5 | Launch acceptance | Check quorum, ISR, end-to-end read/write, monitoring, capacity, and runbooks |
kf-dev and kf-main are separate new clusters. You can also expand kf-dev to three controllers in place.
Declare two combined nodes and rerun ./kafka.yml -l kf-dev. Each new node formats, catches up as an observer, and promotes with add-controller, one at a time.
A fresh cluster is simpler for this demonstration. See Expand Cluster.
Before You Begin
Unless stated otherwise, run commands from the project directory on the Pigsty administration node:
Before you start, confirm that:
- Confirm
pigsty.ymldescribes this environment. Back it up and review existing contents. - Ensure all members and clients can resolve and route directly to each
inventory_hostname. - Synchronize administration and Kafka node clocks.
- Check
9092,9093,9308, and9404for conflicts. - Use
/data/kafkaexclusively for Kafka data. - Select exactly all members of one cluster with
-lon everykafka.ymlrun. - Before changes, run
--check, review results, and obtain change approval.
Retain the all.children inventory structure. Merge these groups into pigsty.yml. Do not overwrite existing all.vars, infra, etcd, pgsql, or other configuration.
Part 1: Deploy a Single-Node Kafka
1. Define the Cluster
Add kf-dev under all.children. Omitting kafka_role selects combined, so this node runs broker and controller:
This configuration yields:
- a random cluster ID;
- a single dynamic KRaft combined node;
- default RF=1 and minISR=1;
- a single-partition topic named
quickstart.events; - a JMX exporter on
:9404and a protocol exporter on:9308.
plaintext provides no encryption, authentication, or ACLs. Use it only for local development or trusted isolated networks.
2. Bring the Node Under Management
If this host has not yet been initialized by NODE, run check mode first:
After reviewing the results and getting approval, bring the node under management:
Skip this step if Pigsty already manages the node and its repository access and time synchronization work. See Node Administration for preparation and operations.
3. Deploy Kafka
Run a check against the full cluster first:
Verify that the target includes exactly every kf-dev member. Review data paths, packages, ports, and configuration changes, then run:
The role installs Java and kafka-stack, generates random identity and manifest, formats KRaft storage, starts Kafka, creates topics, and registers monitoring.
4. Verify the Service and Quorum
Log in to the Kafka node and check the services:
Run the role’s own health check:
JSON should include "healthy": true. Then check quorum and the topic:
Expect a valid LeaderId, this node in CurrentVoters, and quickstart.events with RF=1 and ISR=1.
5. Produce and Consume Messages
Start a console producer:
Enter several messages and press Ctrl-D. Read them from another terminal:
Single-node deployment, topic convergence, and message read/write are complete. See Day-to-Day Administration for more checks.
Part 2: Deploy a Three-Node Production Baseline
The new kf-main cluster has three combined nodes and tolerates one controller failure. Business topics use RF=3/minISR=2. Security uses scram.
1. Define the Secured Cluster and Its Resources
Add the following group to your existing all.children:
Supply vault_kafka_quickstart_password through Ansible Vault, KMS, or another secret injection method. It must contain at least 12 characters. Never put real passwords in Git, logs, or tickets.
This configuration has these effects:
- All nodes omit
kafka_roleand usecombined. - Bootstrap uses dynamic KRaft directly.
scramenables node TLS, controller mTLS, SCRAM-SHA-512, ACLs, and default denial together.- Three brokers set initial RF=3 and minISR=2.
quickstart.eventsexplicitly has 12 partitions and three replicas.quickstart-appcan read/writequickstart.*topics, readquickstart.*groups, and use idempotent production.- At most two brokers run
kafka_exporter. All three JVMs run JMX Exporter.
If brokers occupy separate failure domains, set kafka_rack: az-a/az-b/az-c respectively on all nodes. Fictitious labels cannot provide disaster protection. See Rack configuration.
2. Bring Under Management and Deploy
If the nodes are not yet under management:
When deploying Kafka, you must select all three members:
Do not select only -l 10.10.10.11. Partial clusters are rejected. You can select several complete clusters with -l kf-dev,kf-main, or all clusters without a limit.
3. Verify the Health of All Three Nodes
From the administration node, check all three Kafka services:
Run the full health check on any broker:
Query the quorum and the topic:
Before launch, verify one active controller, three voters, and three available brokers. Every quickstart.events partition must have three replicas and ISR=3. No partitions should be offline, under-replicated, or under-min-ISR.
Part 3: Connect Application Clients
1. Distribute the CA Public Certificate
Securely copy the public CA certificate from the admin node to the application host:
ca.crt is public and safe to distribute. Never copy, expose, or distribute files/pki/ca/ca.key. On application hosts, make the CA file root-owned and read-only. Pigsty-managed hosts already have it at /etc/pki/ca.crt; clients can use that path directly.
2. Create the Client Configuration
On the application host, create /etc/kafka-client/client.properties:
Kafka Java clients support SASL_SSL, SCRAM, and PEM truststores. Applications should inject passwords from secret management at runtime. Do not commit password files to repositories. See Kafka 4.3 SASL/SCRAM and producer settings for all fields.
3. Why Applications Should Connect Directly to Multiple Brokers
Clients use bootstrap.servers only for initial metadata. They then connect directly to partition leaders and refresh routing after leadership changes. In production:
- Set at least two, usually three,
bootstrap.serversaddresses in different failure domains. - Permit application access to
9092on all brokers. Ensure each advertisedinventory_hostnameresolves and routes correctly. - Use client retry, metadata refresh, idempotence, and consumer-group protocols.
- Do not put HAProxy, Keepalived VIP, L4 load balancers, or L7 reverse proxies in the Kafka data plane.
A VIP or load balancer cannot replace metadata broker addresses or transparently select partition leaders. It adds connection-state, diagnostic, and capacity-planning complexity. A DNS name or TCP load balancer can provide bootstrap discovery if required. However, every advertised.listeners address must remain directly reachable, not only the load balancer. NAT, public internet, Kubernetes, and multiple networks need per-broker external addresses and additional listeners. The module fixes advertised addresses to inventory addresses and does not support these mappings.
4. Authenticate and Read/Write with the Application Identity
On an application host with the Kafka 4.3 CLI installed, run:
When consuming, use a group prefix permitted by the ACL:
Explicitly review these production client settings:
| Client Setting | Suggested Starting Point | Notes |
|---|---|---|
acks | all | Pairs with RF=3/minISR=2 to avoid waiting on the leader alone |
enable.idempotence | true | Reduces the risk of duplicate writes from retries; requires the IdempotentWrite ACL |
group.id | A distinct, stable name | Do not reuse a group across different business or consumption semantics |
| Offset commit | Choose per workload | Auto-commit is simple; manual commit ties commits to business processing results more reliably |
client.id | An identifiable instance name | Helps with logs, quotas, and client diagnostics |
Set acks, retries, idempotence, batching, compression, and offset strategy in applications. Do not put them in broker kafka_parameters.
Part 4: Change Core Parameters
Declare persistent changes in pigsty.yml. Never edit /etc/kafka/server.properties directly. Use these parameters:
| Goal | Parameter | Behavior |
|---|---|---|
| Adjust the JVM heap | kafka_heap_opts | A static change; a healthy cluster enters a strict one-node-at-a-time rolling restart |
| Adjust threads, retention, segments | kafka_parameters | Broker parameters not owned by the role; static changes require a rolling restart |
| Adjust topic partitions/retention | kafka_topics | Online resource convergence; partitions can only increase, never decrease |
| Adjust application passwords/ACLs/quotas | kafka_users | Online resource convergence; passwords come from a secret system |
| Declare failure domains | kafka_rack | All-or-nothing across every broker-capable node; a change triggers a rolling restart but does not relocate data |
| Choose a security profile | kafka_security | Decided only when bootstrapping a new cluster; it cannot be switched online by an ordinary rerun |
Example: Adjust the Heap and Broker Default Parameters
This example raises heap to 6G, increases threads, and sets new-topic default retention to 72 hours after load testing:
Do not copy 6G/8/24 without testing. Choose values from CPU, memory, connections, message size, partitions, disk, and page-cache measurements.
Example: Add Partitions and Shorten Topic Retention
Increase quickstart.events from 12 partitions to 24 and change the retention to three days:
Partitions cannot decrease. If replication_factor differs from live state, ordinary convergence fails and requires explicit reassignment. The role never relocates replicas automatically.
Apply the Change
Whether you change static parameters or dynamic resources, run the full state machine:
Do not run only -t kafka_config. The role rolls nodes for static changes. Topic or user changes alone do not restart Kafka.
The following keys belong to the role itself and must not be placed in kafka_parameters:
For all 15 public parameters, their defaults, and the reserved keys, see the Parameter Reference.
Part 5: Key Pre-Launch Checks
Topology and Data Safety
- Use at least three production brokers and an odd controller count. Consider three separate controllers plus N brokers for critical or large clusters.
- Align topic RF, minISR, and producer
ackswith one failure model. - Set
kafka_rackonly for real failure domains and verify replica placement. - Load-test capacity, throughput, latency, retention, peak writes, and recovery time.
- Plan reassignment after broker admission. Existing topic RF does not increase automatically.
- Define backup/rebuild and disaster recovery procedures. Rehearse failed-node replacement and retirement.
Security and Network
- Bootstrap new production clusters with
kafka_security: scram. - Inject passwords through Vault, KMS, or secret management. Keep them out of Git and logs.
- Distribute only the public CA certificate, never its private key.
- Verify direct client resolution and routing to every
inventory_hostname. - Restrict
9092/9093to authorized participants and9308/9404to monitoring. - Review principals, topic/group/cluster ACLs, and quotas through a checklist.
- Schedule protected rotation of internal credentials and certificates.
Operations and Monitoring
- Verify
/usr/local/bin/pigsty-kafka-health clusterreports health. - Verify exactly one quorum leader and all expected controllers in current voters.
- Confirm no offline, under-replicated, or under-min-ISR partitions.
- Test production and consumption from the real application network with a real principal.
- Check healthy data in Kafka Overview, Kafka Instance, Kafka Topic, and Kafka Consumer.
- Confirm alert routing, log search, capacity thresholds, on-call duties, and rollback conditions.
- Define separate approvals for upgrades, feature changes, topic/user deletion, and cluster teardown.
For detailed alerts and PromQL, see Monitoring and Alerting; for metric semantics, see Metric Definitions.
Documentation Index and Next Steps
Use these pages for the next task:
| What You Want to Do Next | Document |
|---|---|
| Plan a combined or separated controller/broker topology, network, rack, storage, and security | Cluster Configuration |
| Look up the 15 public parameters, their defaults, schema, and reserved keys | Parameter Reference |
| Look up quorum, topic, user, message, consumer-group, and scaling operations | Day-to-Day Administration |
Understand the kafka.yml lifecycle, strict rolling restart, rotation, and cluster teardown | Playbook |
| Use the dashboards, alerts, PromQL, and VictoriaLogs | Monitoring and Alerting |
| Understand every JMX/exporter/recording-rule metric | Metric Definitions |
| Troubleshoot identity conflicts, connectivity, SCRAM, exporters, lag, and scaling issues | FAQ |
| Return to the overview of module capabilities, default ports, and boundaries | Kafka Module Home |
Recommended reading order: Quick Start → Cluster Configuration → Parameter Reference → Day-to-Day Administration → Playbook → Monitoring and Alerting → FAQ.
2 - Configuration
KAFKA uses 15 persistent public parameters. The role derives topology, listeners, storage subdirectories, replication safety, authorization, and exporter placement consistently. Start with Quickstart. See Parameters for the field reference.
kafka_seq becomes KRaft node.id. A new cluster’s bootstrap manifest freezes the random Cluster ID, initial controller identities, security profile, and initial replication policy.
After formatting, do not casually change identities, security profile, or controllers. The role checks live state against the manifest and blocks conflicts. It never silently overwrites or reformats data.
Pre-Deployment Checklist
Before writing the inventory, confirm these requirements:
NODEmanages the hosts, and the software repository is reachable. Every member and client can route directly toinventory_hostname.- Each operation uses
-lto select exactly all members of onekafka_cluster. Do not select a node, subset, or multiple clusters. kafka_seqis unique per cluster. Controller count is odd. Broker count meets failure-domain and capacity requirements.9092,9093,9308, and9404do not conflict. INFRA nodes can reach both metrics ports.kafka_datauses a dedicated filesystem. Size it for retention, peak writes, replication, recovery time, and growth.- Production uses
kafka_security: scram. Synchronize node and administration clocks. Provide the Pigsty CA and application passwords from a secret source such as Vault. - Review partitions, replica counts,
min.insync.replicas, retention, clientacks, retries, and consumer recovery. - Prepare separate procedures for expansion, shrink, reassignment, upgrades, backup, restore, and controller membership changes.
Roles and Topology
kafka_role accepts only three values:
| Role | Kafka process.roles | Broker Port | Controller Port | JMX | kafka_exporter |
|---|---|---|---|---|---|
combined | broker,controller | ✓ | ✓ | ✓ | eligible |
broker | broker | ✓ | - | ✓ | eligible |
controller | controller | - | ✓ | ✓ | - |
Either omit kafka_role on every member to use combined, or explicitly set it on every member. Identity checks reject mixed declarations. A cluster needs at least one controller-capable and one broker-capable node. Even controller counts trigger a warning. Production usually uses three controllers.
Single-Node Development Cluster
A single node runs both broker and controller. It cannot survive node failure. Use it only for development, testing, and feature validation:
The initial broker count sets RF=1 and minISR=1. Do not use this single-node topology or default plaintext profile directly in production.
Three-Node Combined Deployment
Three combined broker/controller nodes provide a compact production starting point. Omit all role fields to use combined:
Three initial brokers set RF=3 and minISR=2. The role owns this policy. Do not override internal-topic RF, default.replication.factor, or min.insync.replicas in kafka_parameters. The example 4G heap is illustrative. Use load tests to balance JVM heap, OS page cache, and other host processes.
Separating Controllers and Brokers
For critical or larger clusters, separate control and data planes. Explicit roles require a role declaration on every member:
Controller-only nodes have no 9092 listener or protocol exporter. JMX still exposes KRaft and JVM metrics. At most two kafka_exporter instances run on broker-capable nodes with the lowest kafka_seq.
Dynamic KRaft and the Bootstrap Manifest
New clusters use dynamic quorum. Each node renders controller.quorum.bootstrap.servers, without static controller.quorum.voters. During first formatting:
- Generate a random Cluster ID rather than hashing the cluster name.
- Generate and freeze random initial controller Directory IDs.
- Format each node explicitly with
--initial-controllersor--no-initial-controllers. - After bootstrap startup, wait for leader election. Verify that every initial controller Directory ID entered live quorum.
The bootstrap-only facts live on every cluster member:
Each scram member also stores /etc/kafka/secrets.yml. Every run retrieves the manifest and secrets from any member copy. The administration node keeps no Kafka state. Certificates reside in shared PKI at files/pki/kafka/, with CSRs in files/pki/csr/. Missing certificates are signed again by the Pigsty CA. The manifest stores only cluster identity, initial controller identities, security profile, and initial RF/minISR.
Live cluster state is authoritative for runtime facts:
- Manifest conflicts with live identity or security profile block ordinary playbooks.
- An old manifest with all data disks empty cannot revive the old cluster.
- Formatted storage without any member manifest blocks execution. Restore a manifest copy on any member first.
- A formatted
scramcluster without secret material on any member also blocks execution.
The manifest records bootstrap facts. After commissioning, live Raft state controls membership. Playbooks join new combined/controller members by fresh formatting, observer catch-up, and add-controller promotion. A strict-subset kafka-rm.yml run retires members with remove-controller and broker unregistration. See Expand Cluster and Shrink Cluster.
Identity Parameters
| Identity | Source | Example | Constraint |
|---|---|---|---|
| Cluster name | kafka_cluster | kf-main | Starts with a letter or digit; only letters, digits, underscores, and hyphens |
| Node number | kafka_seq | 1 | Non-negative integer, unique within the cluster |
| Instance name | Auto-generated | kf-main-1 | ${kafka_cluster}-${kafka_seq} |
| Node role | kafka_role | combined | One of the three native roles |
| KRaft Cluster ID | Randomly generated at bootstrap | 22-character Kafka UUID | kafka_cluster_id is only a takeover/recovery assertion |
A formatted node reads cluster.id and node.id from ${kafka_data}/metadata/meta.properties. It checks them against the manifest and inventory. After startup, it checks initial controller Directory IDs against live quorum. Identity mismatches stop execution for protection. Never bypass this by deleting meta.properties or erasing data.
Network and Listeners
Public parameters expose ports only. They do not expose bind addresses, advertised addresses, or listener maps:
| Parameter | Default | Purpose |
|---|---|---|
kafka_port | 9092 | Broker, client, and inter-broker communication |
kafka_controller_port | 9093 | KRaft controller quorum |
kafka_exporter_port | 9308 | Protocol Exporter HTTP metrics |
kafka_jmx_exporter_port | 9404 | JMX Exporter HTTP metrics |
The listener conventions are fixed:
- Brokers bind
0.0.0.0; controllers bindinventory_hostname. - Broker
advertised.listenersusesinventory_hostname. - Controller bootstrap addresses also use
inventory_hostname. plaintextuses PLAINTEXT for BROKER and CONTROLLER.scramuses SASL_SSL + SCRAM-SHA-512 for BROKER and mutual TLS for CONTROLLER.
Clients must resolve and directly reach every broker’s inventory_hostname. Version 1 does not support NAT, public-address mapping, multiple client networks per broker, or arbitrary listener overrides. kafka_parameters cannot bypass these limits.
Kafka clients connect directly to brokers. Configure several seeds in bootstrap.servers. After fetching metadata, clients connect to partition leaders. Do not use HAProxy, Keepalived VIP, or a cloud load balancer as the regular data-plane entry. They cannot interpret Kafka metadata or remove the need to reach every advertised.listeners address.
DNS or a TCP load balancer can optionally provide bootstrap discovery only. Clients must still reach every broker directly. See Connecting Application Clients.
Permit these minimum network flows:
| Source | Destination | Port | Purpose |
|---|---|---|---|
| Kafka clients, other brokers | All brokers | 9092 | Produce, Fetch, metadata, and inter-broker communication |
| All Kafka members | All controllers | 9093 | KRaft metadata quorum |
| Infra/VictoriaMetrics | All Kafka nodes | 9404 | JVM/Kafka metrics |
| Infra/VictoriaMetrics | Selected Exporter nodes | 9308 | Cluster/Topic/Consumer metrics |
Metrics use HTTP even with scram. Restrict these ports to the monitoring network through firewalls.
Storage, Heap, and Rack
You set only the root directory:
The role uses ${kafka_data}/data for topics and ${kafka_data}/metadata for KRaft. Set kafka_data to a dedicated absolute path. Forbidden paths are /, /data, /var, /etc, /opt, /usr, /home, /root, and /pg.
Plan production capacity for retention, peak messages, replication, partition/segment counts, disk latency and throughput, file descriptors, recovery time, heap, and page cache. The role generates one log.dirs. Multi-disk JBOD, disk replacement, and automatic data migration require separate procedures.
For deployments spanning fault domains, declare kafka_rack consistently on all broker-capable nodes:
Either set racks on all broker-capable nodes or omit them on all. Rack changes trigger safe rolling restarts without moving existing replicas.
Replication Policy
At the first bootstrap, the policy is derived from the initial broker count:
The manifest freezes initial topic-default RF, internal-topic RF, and cluster minISR. After expansion:
default.replication.factorretains its initial value. Kafka 4.3 cannot update it through dynamic broker configuration.- Existing internal and business topics retain their RF.
- Broker admission does not imply balanced data.
- RF changes require a reviewed
kafka-reassign-partitions.shplan. Increasing the static default also requires controller HA or an explicit maintenance window. A safe full-cluster rolling restart applies it.
Configure producer acks, idempotence, retries, batching, and compression on clients. They are not broker-role parameters.
kafka_parameters
kafka_parameters is the only custom broker configuration interface. It defaults to {} and renders only on broker-capable nodes. Use keys that the role does not own, such as num.partitions, threads, buffers, retention, and segments.
The following patterns are owned by the role and must not be overridden:
Any reserved key causes identity preflight to fail before writing files.
Security and Declarative Resources
kafka_security: scram selects a complete production profile. Its controls cannot be combined individually. It enables:
- Pigsty CA certificates for each node.
- Mutual TLS on controllers.
- SASL_SSL + SCRAM-SHA-512 for broker/client and inter-broker traffic.
StandardAuthorizer, default denial, and role-managed administration and monitoring identities.- Minimum monitoring ACLs before starting the protocol exporter.
Application resources are declared through two domain objects:
Topic creation is idempotent. Partitions can only increase, and only declared settings change. RF changes fail with a reassignment instruction. User passwords, ACLs, and specified quota fields converge idempotently. Removing topic or user entries does not delete those resources.
Ordinary playbooks cannot change the security profile after bootstrap. Protected rotation changes internal credentials or certificates. Online plaintext to scram migration still needs a separate state machine planned for future work.
Packages and File Layout
Platform mappings install java-runtime and kafka-stack. The payload verified on 2026-07-16 contained Kafka 4.3.1, kafka_exporter 1.9.0, and JMX Exporter 1.6.0. Actual versions depend on target-platform repositories and installed packages.
| Path | Purpose |
|---|---|
/opt/kafka/ | Kafka programs and CLI |
/etc/kafka/server.properties | Role-generated service configuration |
/etc/kafka/admin.properties | Role-generated broker admin channel; the CLI should always use it |
/etc/kafka/controller.properties | Role-generated controller admin channel |
/etc/kafka/log4j2.yaml | Journald logging configuration |
/etc/kafka/jmx_exporter.yml | Bounded JMX metrics rules |
/etc/kafka/manifest.yml | Authoritative copy of the bootstrap manifest on the node |
/etc/kafka/secrets.yml | Copy of the internal secrets on a scram node |
/etc/kafka/.pigsty-applied-static.sha256 | Fingerprint of the static config proven live; the rolling-restart trigger |
/etc/kafka/pki/kafka.pem | PEM private key and certificate on a scram node; the trust anchor uses the system /etc/pki/ca.crt |
${kafka_data}/data/ | Topic log data |
${kafka_data}/metadata/ | KRaft metadata and meta.properties |
files/pki/kafka/ | Issued node certs on the admin node (<cluster>-<seq>.key/.crt, CSRs under files/pki/csr/) |
The role manages these files. Put persistent settings in pigsty.yml, not generated node files. Never copy passwords, private keys, or role-managed secrets into inventory, logs, or tickets.
3 - Parameters
KAFKA exposes 15 persistent parameters. The role derives topology, listeners, security, storage subdirectories, replication safety, and exporter placement consistently. Additional persistent variables cannot override these derived settings.
Parameter Overview
| Parameter | Level | Default | Description |
|---|---|---|---|
kafka_cluster | Cluster | required | Kafka cluster identity |
kafka_seq | Instance | required | Cluster-unique KRaft node.id |
kafka_role | Instance | combined | combined, broker, or controller |
kafka_cluster_id | Cluster | unset | Takeover/recovery assertion; randomly generated for a new cluster |
kafka_data | Instance | /data/kafka | Role-owned data root directory |
kafka_heap_opts | Instance | -Xms1G -Xmx1G | Kafka JVM heap |
kafka_port | Instance | 9092 | Broker/client port |
kafka_controller_port | Instance | 9093 | KRaft controller port |
kafka_rack | Instance | unset | Broker fault-domain label |
kafka_parameters | Cluster/Instance | {} | Non-role-owned broker parameters |
kafka_jmx_exporter_port | Instance | 9404 | JMX Exporter HTTP port |
kafka_exporter_port | Instance | 9308 | Protocol Exporter HTTP port |
kafka_security | Cluster | plaintext | plaintext or the production scram profile |
kafka_users | Cluster | [] | User credentials, ACLs, and quotas |
kafka_topics | Cluster | [] | Declarative topics |
Set kafka_cluster and kafka_seq. kafka_role has a default. Omit it on every member or declare it on every member.
Identity and Topology
kafka_cluster
Required cluster identity. Start with a letter or digit. Use only letters, digits, underscores, and hyphens:
This identifies all cluster members, generates instance names, and locates the manifest. Every kafka.yml lifecycle operation must select all cluster members with precise -l.
kafka_seq
Required non-negative integer, unique within kafka_cluster. It directly becomes KRaft node.id:
The instance name is ${kafka_cluster}-${kafka_seq}. After formatting, do not change or reuse a sequence number that still has data.
kafka_role
Defaults to combined, and accepts only:
| Value | Kafka process.roles | Semantics |
|---|---|---|
combined | broker,controller | Broker and controller co-located |
broker | broker | Broker only |
controller | controller | Controller only |
If all members omit the role, they use combined. If one declares it, all must declare it. Legacy aliases are unavailable.
kafka_cluster_id
Unset by default. Use it only to assert an existing cluster identity for takeover or recovery. It requires a 22-character Kafka UUID:
Omit it for ordinary new clusters. The role generates a random Cluster ID and writes /etc/kafka/manifest.yml on every member. This parameter cannot relabel data. Conflicts with the manifest or meta.properties block execution.
kafka_rack
An optional broker fault-domain label, rendered as broker.rack:
Set this on all broker-capable nodes or none. Controller-only nodes ignore it. Changes require a strict rolling restart but do not reassign replicas.
Storage, JVM, and Network
kafka_data
The data root directory, defaulting to /data/kafka:
The role derives ${kafka_data}/data and ${kafka_data}/metadata. Use a dedicated absolute path. Forbidden roots are /, /data, /var, /etc, /opt, /usr, /home, /root, and /pg. kafka-rm.yml deletes the whole root by default. Do not store other service or business files there.
kafka_heap_opts
The Kafka JVM heap, defaulting to:
Set production heap size through load and memory tests. Usually keep Xms equal to Xmx. Reserve memory for OS page cache and other processes.
kafka_port
Broker/client port, default 9092. Only broker-capable nodes listen here. plaintext uses PLAINTEXT; scram uses SASL_SSL + SCRAM-SHA-512.
kafka_controller_port
Controller port, default 9093, the conventional KRaft port. Only controller-capable nodes listen here. Check for conflicts with other services yourself; the role does not detect them.
All four public ports must differ. Brokers bind 0.0.0.0. Controller listeners, broker advertised addresses, and controller bootstrap addresses use inventory_hostname. No separate address parameters exist.
kafka_parameters
Default {}. This is the only custom broker parameter mapping, rendered only on broker-capable nodes:
The following keys or patterns are owned by the role and cannot be overridden through this mapping:
The role owns identity, listeners, security, storage, and replication policy. Reserved keys cause preflight failure.
Observability
kafka_jmx_exporter_port
JMX HTTP port, default 9404. Every Kafka JVM receives the agent and registers as job=kafka; no toggle exists. Lifecycle checks use a separate Kafka CLI/metadata channel, not JMX. INFRA nodes must reach this port. kafka_security: scram does not enable HTTPS here. Protect the endpoint with the monitoring network and firewall.
kafka_exporter_port
Protocol kafka_exporter HTTP port, default 9308. The role starts and registers exporters on the first two broker-capable nodes by kafka_seq. A single broker runs one. Full runs refresh discovery targets for current placement. Ordinary playbooks do not stop stale exporter services on previously selected nodes.
Exporter protocol version, TLS/SCRAM settings, and placement are internal conventions. No additional public toggle or options parameters exist.
Security and Resources
kafka_security
Defaults to plaintext, and accepts only:
| Value | Broker/client | Controller | Authorization | Purpose |
|---|---|---|---|---|
plaintext | PLAINTEXT | PLAINTEXT | none | Development or a trusted, isolated network |
scram | SASL_SSL + SCRAM-SHA-512 | mutual TLS | StandardAuthorizer, deny-by-default | Production security baseline |
scram configures Pigsty CA certificates, internal/administration/monitoring identities, and TLS/SCRAM/ACL activation order together. The manifest records this profile. After formatting, ordinary reruns cannot switch between plaintext and scram in either direction.
Certificates use shared cert_validity, default 7300d. KAFKA has no separate validity parameter.
kafka_users
Default []; permitted only in scram mode. Supply a list of mappings with only name, password, acls, and quota. Non-mappings or unknown top-level keys fail before resource convergence:
Constraints:
namemust be unique in the list. Requiredpasswordmust contain at least 12 characters and should reference secret management.- ACL
resource:topic,group,transactional_id, orcluster. pattern:literal(default) orprefixed.- Operations:
Read,Write,Create,Delete,Alter,Describe,ClusterAction,DescribeConfigs,AlterConfigs,IdempotentWrite. - Quotas:
producer_byte_rate,consumer_byte_rate,request_percentage,controller_mutation_rate.
The role applies each declared user’s SCRAM password, full ACL set, and specified quota fields. Removing entries does not delete principals or credentials. Deletion and revocation require separate audited operations.
kafka_topics
Default []. Supply mappings with only name, partitions, replication_factor, and config. Non-mappings or unknown top-level keys fail before resource convergence:
Identity prechecks validate only unique name values. Kafka checks partitions and RF at creation: both must be at least 1; RF cannot exceed current broker count. These errors appear during resource convergence, not --check. The rules are:
- Create missing topics idempotently.
- Increase partitions only; decreases fail.
- Reject RF differences from live state and require explicit reassignment.
- Change only keys declared in
config. - Never delete a topic because its list entry disappeared.
Transient Protected Operational Variables
Use these variables only with command-line -e for individual operations. They are outside the 15 persistent parameters. Do not put them in pigsty.yml:
| Action | Playbook | Transient Variable | Protection Condition |
|---|---|---|---|
| Rotate internal credentials | kafka.yml | kafka_rotate_credentials=true, kafka_rotate_confirm=<cluster> | A healthy, fully-formatted scram cluster |
| Rotate certificates | kafka.yml | kafka_rotate_certificates=true, kafka_rotate_confirm=<cluster> | A healthy, fully-formatted scram cluster |
| Tear down the cluster | kafka-rm.yml | kafka_rm_data (default true), kafka_rm_pkg (default false), kafka_safeguard (default false) | Explicit -l required; kafka_safeguard=true aborts all deletion |
Rotation actions are mutually exclusive and require one exact complete cluster. kafka-rm.yml deletes data and node-local /etc/kafka recovery state by default. kafka_rm_data=false retains both. Before removal, confirm the target and backup or rebuild intent. See Playbooks for commands and details.
kafka_safeguard
For kafka-rm.yml only. Default false. When true, abort before deregistration, retirement, service shutdown, or deletion. This boolean switch does not test whether the cluster is alive.
kafka_rm_data
For kafka-rm.yml only. Default true. Delete all of kafka_data and /etc/kafka, including the manifest, credentials, and recovery state needed to readopt retained storage. false keeps both paths. It still deregisters targets, stops services, and removes runtime integration configuration.
kafka_rm_pkg
For kafka-rm.yml only. Default false. When true, uninstall mapped kafka-stack packages: Kafka, Kafka Exporter, and JMX Exporter. Never uninstall the shared Java runtime.
4 - Administration
KAFKA installs Kafka in /opt/kafka and manages it with Systemd. Declare persistent settings in pigsty.yml. Do not edit generated node files manually.
These CLI examples use the generated /etc/kafka/admin.properties. Keep --command-config even with plaintext. Commands then retain the same structure if the administration channel changes to scram. Replace <broker>:9092 with a reachable inventory_hostname and port.
Starting with Kafka 4.2, KIP-1147 standardized --command-config for files and --command-property for key-value arguments across all CLIs.
/opt/kafka/bin comes from the Pigsty repository, currently with a 4.3.x payload. It supports these examples.
External 4.1 or older console tools require --producer.config and --consumer.config.
Administration tools such as kafka-topics.sh, kafka-configs.sh, kafka-acls.sh, kafka-consumer-groups.sh, and kafka-metadata-quorum.sh already used --command-config. They are unaffected.
Quick Reference
| Operation | Command | Description |
|---|---|---|
| Create Cluster | ./kafka.yml -l <cls> | Create or converge kafka clusters; a bare run covers all |
| Expand Cluster | ./kafka.yml -l <cls> | Declare new members: broker admission, controller join |
| Shrink Cluster | ./kafka-rm.yml -l <ip> | Retire a member: remove voter entry & broker registration |
| Remove Cluster | ./kafka-rm.yml -l <cls> | Tear down a whole cluster; deletes data by default |
| Replace Failed Node | retire → provision → rejoin | Three commands; replicas are re-inherited automatically |
| Config Cluster | ./kafka.yml -l <cls> | Edit the inventory, then roll under safety gates |
| Manage Topics | ./kafka.yml -l <cls> | Declaratively create topics, grow partitions, set configs |
| Manage Users | ./kafka.yml -l <cls> | Declaratively converge users, ACLs, and quotas |
| Rotate Credentials | ./kafka.yml -e kafka_rotate_... | Protected internal credential / certificate rotation |
See Configuration for cluster definitions and parameters, Playbooks for execution behavior, and Monitoring for observability.
Status Check
Check the service and recent logs on any Kafka node:
The protocol exporter runs on at most two broker-capable nodes with the lowest kafka_seq. On those nodes, also check:
Check listeners and metric endpoints:
kafka_up and kafka_exporter_up are VictoriaMetrics recording metrics. They may not appear at raw endpoints. JMX should expose jmx_scrape_error 0.0, JVM metrics, and role-appropriate kafka_ metrics.
Health Check
Lifecycle checks use the administration channel, not JMX. They check dynamic quorum, unavailable partitions, under-replication, and under-min-ISR:
The check passes only if JSON returns healthy: true. Use it for read-only diagnostics. It does not replace end-to-end business validation.
Every playbook run executes the parser regression suite, pigsty-kafka-health selftest, immediately after installation. If it fails, stop changes and investigate. The health result is then untrustworthy.
KRaft Quorum Status
Query the dynamic quorum from any available broker:
Verify these conditions:
LeaderIdexists and identifies an expected controller.CurrentVoterscontains the expected members. A joining node first appears inCurrentObservers.MaxFollowerLagandMaxFollowerLagTimeMsdo not grow continuously.- Dashboards show exactly one active controller.
To confirm the dynamic quorum (KIP-853) feature level, inspect kraft.version with /opt/kafka/bin/kafka-features.sh ... describe.
Inspect controller replication state:
Stop other changes if no leader exists, a member persistently lags, or voters differ from expectations. Preserve logs, the manifest, and meta.properties before analysis. Remove dead voters through shrink or replacement. Never rewrite quorum state manually.
Manage Topics
Production topics should be declared in kafka_topics in pigsty.yml:
Converge after editing the declaration:
The role creates topics idempotently, increases partitions only, and changes only declared configuration keys. RF changes fail and require explicit partition reassignment. Removing an inventory entry does not delete the topic.
Read-only topic inspection:
You can create ad hoc or externally managed topics with the CLI. These are not written back to pigsty.yml. Never manage the same topic both manually and declaratively. Topic deletion deletes business data. It requires separate approval, exact-name confirmation, and a recovery plan. This page therefore gives no generic deletion command.
Manage Users and ACLs
With kafka_security: scram, application identities should be managed through kafka_users:
A full run idempotently applies the password, user ACL set, and explicitly declared quota fields. Never commit plaintext passwords or print them in logs. Removing an entry does not delete the principal or credentials. Deletion and full revocation need a separately reviewed procedure.
Verify Read/Write
Validate end-to-end operation with a test topic. The console producer and consumer use the same client configuration:
Consume in another terminal:
Run production acceptance from the actual client network. Check DNS, advertised.listeners, certificates, ACLs, producer ACKs, consumer commits, and end-to-end latency. Broker-local tests alone are insufficient.
Manage Consumer Groups
List and inspect consumer groups:
Assess lag against consumption rate and business SLOs. A short backlog can be normal batch processing. Sustained growth with consumption below production means the group cannot catch up. Offset resets can duplicate or skip messages. They require separate approval, exact group/topic confirmation, and a replay plan.
Config Cluster
After editing pigsty.yml, run against complete clusters:
Live health and the static fingerprint determine the execution path:
- Unhealthy or stopped: Start only stopped controllers. Restore quorum and wait for catch-up, then start brokers. If static settings also changed, roll still-online members afterward.
- Controller join pending: Join each node as an observer, wait for catch-up, then promote with
add-controller. Process one node at a time. - Healthy with new pure brokers: Format, start, and verify registration one node at a time.
- Healthy with static changes: Restart one node at a time. Before and after each restart, check controller majority, voter catch-up, offline partitions, under-min-ISR, and ISR catch-up.
- No static changes: Do not restart Kafka.
Do not bypass the full state machine with -t kafka_config. kafka_provision applies topics, users, ACLs, and quotas. The role determines whether static changes require restarts.
Expand Cluster
Add new kafka_role: broker, combined, or controller members to a healthy cluster through the inventory. Assign each a never-used kafka_seq. A host can belong to only one Kafka cluster. Ensure that Pigsty manages the node. Run against complete clusters:
The role processes one new node at a time according to its type:
- Pure broker: Format, start, and verify registration and an unfenced state (
admit). - Combined/controller: Format with
--no-initial-controllers. Start as an observer, catch up on metadata, and promote withadd-controller. Verify voter membership and full cluster health (join).
quorum-join-hosts and broker-admission-hosts list processed nodes at completion. Note these behaviors:
- A new controller-capable node changes
controller.quorum.bootstrap.serverson all members. Existing nodes then undergo one strict rolling restart with health checks. - An even controller count triggers a warning. It adds no fault tolerance; keep the count odd.
Joining does not move existing partitions to the new broker. Separately generate, review, and monitor a kafka-reassign-partitions.sh plan. Limit disk and network load, and prepare rollback. Registration alone does not complete expansion.
More brokers do not automatically increase the replication policy. Kafka 4.3 cannot change default.replication.factor dynamically. After expansion from one broker to three, the initial RF=1 remains. New topics without explicit RF still use RF=1. First reassign existing partitions. Then plan controller HA or a maintenance window.
Apply the new static default with a safe full-cluster rolling restart. Do not bypass downtime checks to change this default.
Shrink Cluster
kafka-rm.yml retires a strict subset of cluster members. Selecting all members performs teardown. Retirement uses a surviving member to remove the target from live metadata:
Retirement first deregisters monitoring targets and stops services. For voters, it runs remove-controller, strictly serializing multiple removals. Next, kafka-cluster.sh unregister removes broker registration. Finally, it cleans local configuration and data according to kafka_rm_data. Broker unregistration tolerates failure so the procedure can resume or handle unreachable nodes. Remove the inventory entry from pigsty.yml only after checking live quorum, broker registrations, replica health, and target local state.
Before retirement, confirm that remaining controllers form a majority and have an odd count. Remaining brokers must support the highest topic RF. If the target still holds replicas, the role warns. Those partitions remain under-replicated until explicit reassignment or replacement with the same kafka_seq. Such a replacement inherits assignments and resynchronizes automatically. For planned shrink, drain replicas through reassignment before retirement.
Replace Failed Node
For permanent node loss, such as destroyed disks or retired hardware, retain the IP and kafka_seq. Replace it in three steps:
Step 1 delegates metadata operations to a surviving member, so it works for unreachable nodes. It also removes monitoring targets to stop KafkaDown alerts. In step 3, the same kafka_seq makes the broker inherit its previous partition assignment. It resynchronizes from surviving replicas without manual reassignment.
If you skip step 1, kafka.yml stops during configuration on the re-imaged node. It reports the stale voter directory ID and exact kafka-rm.yml command. Run that command, then retry. The join procedure can resume safely: another kafka.yml run continues from live state.
Change Address or Port
inventory_hostname supplies broker advertised addresses and controller bootstrap addresses. Changing it, kafka_port, or kafka_controller_port is a high-risk static change. These changes affect client metadata, broker communication, or quorum. Coordinate DNS, certificate SANs, routing, firewalls, bootstrap addresses, monitoring targets, and every cluster member.
Rotate Credentials and Certificates
Healthy, formatted scram clusters support two mutually exclusive protected actions: internal credential rotation and certificate rotation. Both require exact complete clusters and matching kafka_rotate_confirm. Run --check first where possible. The same Pigsty CA reissues certificates, so old and new certificates trust each other. Strict rolling restarts apply rotation one node at a time.
See Protected Rotation for commands and failure behavior. The security profile remains a bootstrap-only setting. Rotation does not support online migration from plaintext to scram.
Data Protection and Recovery
Kafka protects data through replicas in separate failure domains, correct min-ISR, producer ACKs, and tested recovery procedures. The role provides no data backup, automatic broker drain, or cross-region disaster recovery. Planned shrink requires manual reassignment first.
When a disk or node fails:
- Inspect Kafka Overview/Node dashboards, quorum, ISR, offline partitions, and under-min-ISR partitions.
- Preserve
journalctl -u kafka, node metrics, the manifest,server.properties, andmeta.properties. - Confirm the node role,
node.id, cluster ID, directory ID, and remaining replica availability. - If the node is unrecoverable, follow Replace Failed Node:
kafka-rm.ymlretirement,node.ymlprovisioning, thenkafka.ymljoin. If its disk survives and only the service fails, first try ordinary convergence. Do not immediately retire it or deletemeta.properties. - Use a separately reviewed procedure for data movement, including reassignment and RF changes.
Log Diagnostics
VictoriaLogs/Grafana queries:
Check service logs, listener ports, administration-channel health, quorum, broker/partition/ISR state, client addresses and certificates/ACLs, then consumer lag. See Monitoring for dashboards and alerts.
5 - Playbook
kafka.yml deploys Apache Kafka 4.1+ dynamic KRaft and applies security, resources, and monitoring. kafka-rm.yml removes members or tears down clusters.
Each selected kafka_cluster must include every member. Partial selection fails before writes. One cluster, several complete clusters, and a bare whole-inventory run are permitted.
First run --check against the identical target. Before execution, manually verify backup or rebuild intent, capacity, business window, rollback, and change approval.
kafka.yml
Every selected cluster must be complete. Select one cluster, several clusters, or the whole inventory without a limit. Execution is serial within each cluster and concurrent across clusters. Partial cluster selection is rejected.
Check mode validates the API, cluster completeness, roles, racks, ports, manifest, and inspectable file changes. It skips formatting, startup, and live acceptance checks. Passing --check does not guarantee runtime success.
Execution Stages
A single kafka.yml play runs node_id, then kafka, like pgsql.yml. The role has six stages. Launch controls cross-node ordering: parallel bootstrap, serial controller join, serial broker admission, and strict rolling restarts.
| Stage | Tag | Purpose |
|---|---|---|
| Identity | kafka-id | Derive and assert identity, cluster completeness, roles, racks, ports, and reserved keys |
| Install | kafka_install | Create the kafka system user, install the java-runtime and kafka-stack packages |
| Config | kafka_config | Read/restore/create the manifest, issue security material, render config, compute the static fingerprint, format empty storage, decide the lifecycle path |
| Launch | kafka_launch | Converge an unhealthy cluster, join controllers and admit brokers one at a time, strict rolling, commit the manifest and applied static state |
| Provision | kafka_provision | Converge dynamic min-ISR, user credentials, ACLs, quotas, and declarative topics; report internal-topic RF drift |
| Monitor | kafka_monitor | Configure the protocol exporter and register VictoriaMetrics targets |
any_errors_fatal: true stops further dangerous work after a stage failure. Fix the cause, then rerun on the full cluster. The role uses live state and persistent fingerprints to resume without blindly reformatting.
Lifecycle Paths
Configuration uses the role’s administration channel to assess health and select one execution path:
Cold Start, First Deployment, or Repair
Stopped or unhealthy clusters enter convergence:
- Start controller-capable nodes.
- Wait for the controller listener and a quorum leader.
- At first bootstrap, verify that initial controller Directory IDs entered live quorum.
- Start pure brokers.
- Wait for broker listeners and full cluster health.
- Save the static fingerprint only after proving that the configuration runs successfully.
Startup, admission, and rolling decisions use only the role’s Kafka CLI/metadata administration channel. JMX does not participate.
Adding Brokers or Controllers to a Healthy Cluster
Newly formatted kafka_role: broker nodes enter through admit, one at a time. Each must start, register, and become unfenced before the next proceeds.
New combined/controller nodes use serial join. A commissioned cluster formats each with --no-initial-controllers, starts it as an observer, and waits for metadata catch-up. The role then promotes it with add-controller and verifies voter membership and full health. Interrupted runs resume from live state. If node.id retains a dead predecessor’s voter entry, configuration fails with the exact kafka-rm.yml retirement command.
Admission and join prove membership only. They do not move existing partitions to new brokers. Run explicit reassignment separately.
Static Changes on a Healthy Cluster
When the rendered static fingerprint changes, the strict rolling restart handles one node at a time:
- Before restart, check controller majority, zero voter lag, recent catch-up, offline partitions, under-replication, and under-min-ISR. Also check effective partition ISR with the target excluded.
- After restart, require controller voter membership and catch-up, broker registration and unfenced state, and replica reentry into ISR.
- Any failed check immediately stops work on remaining nodes.
When repairs and static changes coincide, convergence starts only stopped members. It does not restart online members in parallel. After quorum recovery and catch-up, strict rolling restarts apply remaining static changes.
An unchanged static fingerprint causes no restart. Dynamic resources still update online during resource convergence.
Task Tags
| Tag | Stage / Purpose |
|---|---|
kafka-id | The identity, full-cluster, and topology-derivation assertions that always run |
kafka_install | The overall entry point for the install stage |
kafka_user | Create the kafka system user and group |
kafka_pkg | Install the java-runtime and kafka-stack packages per platform mapping |
kafka_config | Manifest, security material, config rendering, static fingerprint, storage formatting, and path decision |
kafka_launch | Converge, serialized controller join and broker admission, strict rolling, and manifest commission |
kafka_provision | Convergence of dynamic min-ISR, topics, users, ACLs, and quotas |
kafka_monitor / monitor | The overall entry point for protocol-exporter configuration and monitoring registration |
kafka_register / register / add_metrics | Refresh only the VictoriaMetrics file-discovery targets |
Run full kafka.yml for ordinary configuration changes. Let the role select the lifecycle path. Tags mainly support development, diagnostics, and controlled repair. Do not bypass the state machine with -t kafka_config or single-node limits.
Identity, Formatting, and Manifest
Before writing configuration, the role verifies:
- Every selected cluster includes all members.
kafka_seqis unique; roles are all omitted or all explicit.- At least one controller and one broker exist.
- All broker-capable nodes have racks, or none do.
- Ports are valid and distinct;
kafka_parametersdoes not override role-managed keys. - The manifest, security profile,
meta.properties, and live identity agree.
New clusters generate random Cluster and initial controller Directory IDs, then format nodes explicitly for dynamic quorum. Existing ${kafka_data}/metadata/meta.properties triggers local Cluster/Node ID validation. Only first bootstrap compares initial controller Directory IDs with live quorum. After commissioning, live Raft state controls membership. The role never reformats existing storage automatically.
The authoritative bootstrap manifest lives on every cluster member:
scram members also store /etc/kafka/secrets.yml. The administration node keeps no Kafka state and retrieves both files from member copies each run. Live state controls runtime facts. On conflict, ordinary playbooks do not silently overwrite either side:
- Formatted storage without a member manifest copy blocks execution. Restore the file on any member first.
- A manifest with all data disks empty blocks execution.
- Conflicting Cluster ID, security profile, or controller identity blocks execution.
- A new node with a stale predecessor voter entry for its
node.idmust first be retired withkafka-rm.yml.
Do not delete meta.properties, the manifest, or the secrets to bypass these protections.
Static Fingerprint and Recoverable Re-runs
The role fingerprints static files that affect Kafka. It writes /etc/kafka/.pigsty-applied-static.sha256 only after one of these conditions:
- Convergence starts the cluster successfully and passes global health checks.
- Strict rolling restarts the node, completes catch-up, and passes post-restart checks.
Interrupted runs do not mark unverified changes as applied. The next full run still detects the pending static restart.
Resource Convergence and Monitoring Registration
After full cluster health, resource and monitoring stages run in order:
- Apply role-managed dynamic cluster min-ISR.
- Idempotently apply
kafka_userscredentials, ACLs, and declared quotas. - Idempotently create
kafka_topics, increase partitions, and apply declared settings. - Check internal-topic RF drift without automatic reassignment.
- Start protocol exporters on the first two broker-capable nodes by
kafka_seq. - Refresh discovery targets on all INFRA nodes.
Each instance has one target file. It contains JMX and any selected protocol target under the kafka scrape job:
Full runs refresh targets for current exporter placement. kafka-rm.yml deregistration deletes targets.
Protected Rotation
Rotation variables are one-time extra-vars; do not put them in pigsty.yml. Run only one of the mutually exclusive actions. Every member must be formatted. The cluster must be healthy, use scram, and retain role-managed secrets. kafka_rotate_confirm must exactly match the cluster name.
Internal Credential Rotation
The role maintains active and standby internal identities. It updates the inactive credential through the live administration channel, then atomically switches the protected local record. A strict rolling restart follows. The old active identity becomes standby for the next rotation. Reruns can therefore recover after interruption.
Certificate Rotation
The role removes issued node certificates from shared PKI. The same Pigsty CA issues new node keys and certificates. It updates node PEM bundles, then performs strict rolling restarts. Old and new certificates share trust, so staged CA changes are unnecessary. If the health precheck fails, rotation never starts and deployed certificates stay unchanged.
kafka-rm.yml
Removal uses separate kafka-rm.yml, never kafka.yml. It requires non-empty -l/--limit and fails before roles if omitted. Selecting all members with -l tears down a cluster. A strict subset retires members. Both follow this order:
- Deregister VictoriaMetrics targets (
kafka_deregister). - Stop and disable
kafkaandkafka_exporter(kafka). - Remove voter entries and broker registrations through survivors (
kafka_retire). Survivors exist only for strict-subset selection. - Delete exporter configuration, Systemd environments/units, and helpers (
kafka_config). - Delete data and node-local
/etc/kafkarecovery state (kafka_data), controlled bykafka_rm_data. - Optionally uninstall packages (
kafka_pkg), controlled bykafka_rm_pkg.
Before deregistration or shutdown, the role validates a dedicated absolute kafka_data path. It must contain no ./.. segments. Forbidden roots are /, /data, /var, /etc, /opt, /usr, /home, /root, and /pg. kafka_safeguard set to true in inventory or the command line immediately stops removal without deletion. Identity conflicts, exporter errors, and ordinary startup failures do not justify data deletion. First run kafka.yml and read the failure reason.
Cluster Teardown
kafka_rm_data defaults to true. One default kafka-rm.yml run deletes selected nodes’ data, KRaft metadata, and /etc/kafka recovery state.
There is no additional confirmation string. Before execution, manually verify the -l target, backups or explicit rebuild intent, and producer/consumer impact.
Member Retirement
Partial retirement needs a broker-capable (combined/broker) survivor and a controller-capable (combined/controller) survivor outside -l. One combined node can satisfy both. Missing either stops execution before deregistration or shutdown. Survivors run remove-controller, strictly serializing multiple targets, then broker unregister, before local cleanup. Delegation also handles dead, unreachable targets: step one of Replace Failed Node.
Broker unregistration tolerates failure for repeatable execution. After a real run, verify live quorum, broker registrations, and replica health. Playbook success alone does not prove retirement.
Plan retirement even with automation. Remaining controllers should have an odd count and live majority. Remaining brokers must support the highest topic RF. The playbook warns if departing brokers still hold replicas. For planned shrink, drain them through reassignment first.
Playbook Boundaries
Neither playbook reassigns partitions, balances data, deletes topics/users, or migrates online from plaintext to scram. They do not upgrade versions, finalize features, back up data, or perform disaster recovery. They do not deploy Connect, Schema Registry, MirrorMaker, or Cruise Control. See Module Boundaries and Administration.
6 - Monitoring
Kafka monitoring combines metrics, logs, dashboards, and alerts. It covers JVM internals and the Kafka protocol view. You can inspect partitions, ISR, and consumer lag together with JVM state, request queues, and KRaft controller health.
Scrape Architecture
The KAFKA module uses two complementary exporters:
| Scrape Surface | Service / Method | Job | Node Scope | Main Content |
|---|---|---|---|---|
| JVM and Kafka internals | JMX Exporter Java agent :9404 | kafka (with role label) | All Kafka nodes | JVM, broker throughput, replication, request path, KRaft, controller |
| Kafka protocol view | kafka_exporter :9308 | kafka (no role label) | The one or two broker-capable nodes with the smallest kafka_seq | Broker, topic, partition, offset, consumer group, lag |
| Host resources | node_exporter | node | Managed nodes | CPU, memory, disk, network, filesystem |
| Logs | Journald → Vector → VictoriaLogs | syslog | All Kafka nodes | Structured, searchable Kafka and exporter logs |
On each INFRA node, the role creates one discovery file per instance. JMX and any selected protocol target share that file and kafka scrape job:
Single-broker clusters run one protocol exporter; larger clusters run at most two. Controllers register only JMX. Controllers and unselected brokers have no protocol target by design. Full runs refresh target files for current placement. kafka-rm.yml deregistration removes instance targets.
Label Model
Both targets use job=kafka. JMX has a role label; protocol targets do not.
JMX Target
| Label | Meaning | Example |
|---|---|---|
job | Scrape job | kafka |
cls | Kafka cluster name | kf-main |
ins | Kafka instance name | kf-main-1 |
ip | Inventory host address | 10.10.10.11 |
instance | JMX scrape endpoint | 10.10.10.11:9404 |
role | Pigsty Kafka role | combined, broker, or controller |
node_id | KRaft node ID | 1 |
Protocol Exporter Target
Protocol targets carry cls, ins, ip, and instance (10.10.10.11:9308), without role or node_id. vmagent-side rules distinguish availability by those labels. kafka_up is up{job="kafka",role=~".+"}. kafka_exporter_up is up{job="kafka",role=""}.
Each exporter queries the whole cluster through a broker. Both can return identical topic, partition, and consumer-group state. Recording rules deduplicate exporters before aggregating cluster rates. In scram mode, the role generates exporter TLS/SCRAM settings from its monitoring identity.
Grafana Dashboards
Pigsty ships four complementary dashboards:
Kafka Overview
cls=All shows all Kafka clusters. Selecting one cls changes the same panels to that cluster’s overview.
Main content:
- Inventory of clusters, brokers, topics, partitions, and consumer groups
- Broker availability, exporter health, and cluster workload
- Leaderless, under-replicated, ISR deficit, and non-preferred replica
- Topic offset progress, consumer commit progress, and total lag
- Consumer group members, lag ranking, and topic/group drill-down
- Kafka/exporter log volume, firing alerts, and log detail
Common variables: cls, members, topic, group, topk.
Kafka Instance
Use ins to select a broker or controller JVM, including controller-only nodes. The dashboard also shows related host resources.
Main content:
- Instance identity, role, JMX availability, and scrape quality
- JVM heap, GC, threads, buffer pool, CPU, FD, and uptime
- Broker throughput, replication state, request errors/latency/queues, and handler/network idle
- KRaft member state, metadata log, controller health, and event latency
- Node CPU/memory, disk I/O, network, filesystem, and Kafka logs
Common variables: cls, ins, ip.
Kafka Topic
Use cls and topic to select a logical topic and inspect its topic/partition protocol state.
Main content:
- Topic and partition inventory, leader, replicas, ISR, and preferred leader
- Current offset, retention span, and message append rate
- Leaderless, ISR deficit, and non-preferred replica
- Associated consumer groups, commit progress, and lag
Common variables: cls, topic, topk.
Kafka Consumer
Use cls and group to inspect consumer-group members, committed offsets, progress, and backlog.
Main content:
- Consumer group inventory and member count
- Committed offsets by group/topic/partition
- Commit rate, total lag, maximum partition lag, and backlog trend
- Drill-down from group to topic/partition
Common variables: cls, group, topic, topk.
Choosing a Dashboard
| Question | Preferred Dashboard | Drill-Down Path |
|---|---|---|
| Which cluster or topic is misbehaving? | Kafka Overview | Select cls, topic, group |
| Why is a consumer group falling behind? | Kafka Consumer | Group → Topic → Partition offset |
| Is a particular topic/partition unhealthy? | Kafka Topic | Topic → Partition → Consumer |
| Is a particular broker overloaded? | Kafka Instance | Request path → JVM → Node resources |
| Is the KRaft controller healthy? | Kafka Instance | KRaft metadata plane → Controller health |
| Are there leaderless/URP/ISR problems? | Kafka Overview | Cluster → Kafka Instance / Topic |
| Is the exporter missing data, or is Kafka itself unhealthy? | Overview + Instance | Compare kafka_exporter_up with kafka_up |
Recording Rules
The Kafka rule file lives at /infra/rules/kafka.yml. The main recorded metrics are:
| Metric | Meaning |
|---|---|
kafka:topic:msg_rate1m/5m | 1m/5m forward change rate of a topic’s current offset |
kafka:cls:msg_rate1m/5m | Deduplicated cluster message append rate |
kafka:csg_topic:commit_rate5m | 5-minute commit progress rate per consumer group/topic |
kafka:csg_topic:lag | Total lag per consumer group/topic |
kafka:csg:lag | Total lag of a consumer group across topics |
kafka:cls:lag | Total lag of all consumer groups in a Kafka cluster |
kafka:ins:jvm_heap_used_ratio | Kafka JVM heap usage ratio |
kafka:ins:jvm_cpu_cores | Number of CPU cores consumed by the Kafka JVM |
kafka:ins:load / kafka:cls:load | Saturation of the busiest request thread pool, and the cluster average |
kafka:ins:jvm_gc_time_rate5m | 5-minute GC time rate |
kafka:ins:messages_in_rate5m | Broker 5-minute message receive rate |
kafka:ins:bytes_in_rate5m | Broker 5-minute inbound client byte rate |
kafka:ins:bytes_out_rate5m | Broker 5-minute outbound client byte rate |
kafka:ins:request_error_rate5m | Broker 5-minute request error rate |
kafka:cls:under_replicated_partitions | Total under-replicated partitions in the cluster |
kafka:cls:offline_partitions | Offline partitions in the cluster |
Offset-change rates measure progress, not client requests. Truncation, offset rollback, and exporter restarts can briefly produce negative changes. Rules use clamp_min(..., 0) to retain forward progress only.
Alert Rules
| Alert | Condition | Duration | Severity | Preferred Drill-Down |
|---|---|---|---|---|
KafkaDown | up{job="kafka",role=~".+"} < 1 | 1m | CRIT | Kafka Instance / ins |
KafkaExporterDown | up{job="kafka",role=""} < 1 | 1m | CRIT | Kafka Instance / ins |
KafkaJmxScrapeError | jmx_scrape_error{job="kafka"} > 0 | 3m | WARN | Kafka Instance / JMX Collector |
KafkaJvmHeapHigh | Heap usage > 90% | 15m | WARN | Kafka Instance / JVM Memory |
KafkaJvmDeadlock | JVM deadlocked threads > 0 | 1m | CRIT | Kafka Instance / JVM Threads |
KafkaRequestHandlerSaturated | Handler idle < 10% | 10m | WARN | Kafka Instance / Request Path |
KafkaNetworkProcessorSaturated | Network processor idle < 10% | 10m | WARN | Kafka Instance / Request Path |
KafkaUnderReplicatedPartitions | URP > 0 | 5m | WARN | Kafka Instance / Replication |
KafkaUnderMinISR | Under min ISR > 0 | 1m | CRIT | Kafka Instance / Replication |
KafkaOfflineLogDirectory | Offline log directory > 0 | 1m | CRIT | Kafka Instance / Disk Pressure |
KafkaOfflinePartitions | Controller offline partitions > 0 | 1m | CRIT | Kafka Overview / cls |
KafkaControllerCountMismatch | Active controller count is not 1 | 1m | CRIT | Kafka Overview / cls |
KafkaFencedBrokers | Fenced brokers > 0 | 5m | WARN | Kafka Overview / cls |
KafkaUncleanLeaderElection | An unclean leader election in the last 5 minutes | immediate | CRIT | Kafka Overview / cls |
KafkaConsumerLagGrowing | Group lag > 100000 and still growing after 30m | 30m | WARN | Kafka Consumer / group |
An unclean leader election can indicate data loss. Immediately preserve controller and broker logs. Identify affected topics and replicas before choosing recovery actions.
Common PromQL
Check scrape targets:
Check the replication health of a cluster:
Check consumer lag:
Check request saturation and latency:
Log Queries
Kafka writes stdout and stderr to Journald. Vector forwards these records to VictoriaLogs under job:syslog.
Kafka Instance log panels use similar queries and show time, level, Systemd unit, and message. Compare the same time window across logs, KRaft, ISR, request queues, GC, disk I/O, and network metrics.
Verifying the Monitoring Chain
Verify the raw endpoints on a Kafka node:
Check discovery files on an INFRA node. Each instance has one file; selected nodes include both JMX and protocol targets:
Query up{job="kafka"} in VictoriaMetrics, or recording metrics kafka_up and kafka_exporter_up. Use native Prometheus up for endpoint availability; custom exporter samples can remain briefly after failed scrapes. If raw endpoints work but recording metrics are absent, check discovery files, target state, network, rule loading, and labels in order. If JMX HTTP works but jmx_scrape_error is 1, inspect Kafka logs and MBean matching in /etc/kafka/jmx_exporter.yml.
For complete metric semantics, see Metric Definitions.
7 - Metrics
Both metric sources use job=kafka. JMX targets have a role label and collect each JVM’s internal state. Protocol targets lack role and collect logical cluster, topic, partition, and consumer-group state through Kafka protocol. Protocol exporters run on the one or two broker-capable nodes with the lowest kafka_seq. Single-broker clusters run one.
JMX uses an allowlist for standard JVM and selected broker, replication, request-path, and KRaft metrics. It excludes per-client and per-partition MBeans to limit series counts. Protocol exporters provide partition details.
Common Labels
| Metric Source | Common Labels |
|---|---|
JMX target (:9404) | job, cls, ins, ip, instance, role, node_id |
Protocol exporter target (:9308) | job, cls, ins, ip, instance |
Both target types use job value kafka. Presence of the role label distinguishes JMX from protocol targets.
Some metrics also carry dimensions such as topic, partition, broker, consumergroup, request, version, error, quantile, state, or operation.
Availability and Scrape Metrics
| Metric | Type | Meaning |
|---|---|---|
kafka_up | Gauge/Recording | JMX target scrape availability: up{job="kafka",role=~".+"} |
kafka_exporter_up | Gauge/Recording | Protocol exporter target scrape availability: up{job="kafka",role=""} |
up | Gauge | VictoriaMetrics scrape status for the raw target |
jmx_scrape_error | Gauge | Whether the JMX Exporter’s last scrape errored; healthy value is 0 |
jmx_scrape_duration_seconds | Gauge | JMX scrape duration |
jmx_scrape_cached_beans | Gauge | Number of MBeans cached by the JMX Exporter |
scrape_duration_seconds | Gauge | Time VictoriaMetrics took to scrape the exporter |
scrape_samples_scraped | Gauge | Number of samples in this scrape |
Protocol Exporter Metrics
Protocol exporters report the same logical cluster state. Before cluster aggregation, deduplicate according to metric meaning. Do not simply sum every ins.
Broker and Topic
| Metric | Type | Key Dimensions | Meaning |
|---|---|---|---|
kafka_brokers | Gauge | Cluster | Number of brokers discovered by the exporter |
kafka_broker_info | Gauge | id, address, etc. | Broker info, carried on labels with value 1 |
kafka_topic_partitions | Gauge | topic | Number of partitions in a topic |
kafka_topic_partition_current_offset | Gauge | topic, partition | Partition’s current log end offset |
kafka_topic_partition_oldest_offset | Gauge | topic, partition | Partition’s current earliest readable offset |
kafka_topic_partition_leader | Gauge | topic, partition | Current leader broker ID; used to spot anomalies when there is no leader |
kafka_topic_partition_replicas | Gauge | topic, partition, broker | The replica set assigned to a partition |
kafka_topic_partition_in_sync_replica | Gauge | topic, partition, broker | Current ISR members |
kafka_topic_partition_under_replicated_partition | Gauge | topic, partition | Whether the partition is under-replicated |
kafka_topic_partition_leader_is_preferred | Gauge | topic, partition | Whether the current leader is the preferred replica |
current_offset - oldest_offset estimates the retained offset span. It measures neither bytes nor, for compacted topics, an exact message count.
Consumer Group
| Metric | Type | Key Dimensions | Meaning |
|---|---|---|---|
kafka_consumergroup_members | Gauge | consumergroup | Current member count of the group |
kafka_consumergroup_current_offset | Gauge | consumergroup, topic, partition | Group’s committed offset |
kafka_consumergroup_current_offset_sum | Gauge | consumergroup, topic | Sum of committed offsets |
kafka_consumergroup_lag | Gauge | consumergroup, topic, partition | Partition-level consumer lag |
kafka_consumergroup_lag_sum | Gauge | consumergroup, topic | Consumer lag aggregated per group/topic |
These series may be absent for consumers that never commit offsets, use external offset storage, or have not consumed the topic.
Exporter Itself
| Metric | Type | Meaning |
|---|---|---|
kafka_exporter_build_info | Gauge | Exporter version, revision, and build info |
process_* | Gauge/Counter | Exporter process CPU, memory, FD, start time, etc. |
go_* | Gauge/Counter | Exporter Go runtime, GC, goroutine, and memory state |
promhttp_metric_handler_* | Counter | /metrics request handling status |
JMX: JVM Baseline
excludeJvmMetrics: false makes the JMX Exporter expose the standard JVM/process metrics. The Kafka Instance dashboard mainly uses:
| Metric | Meaning |
|---|---|
jvm_memory_used_bytes | Used memory, split by heap/non-heap and memory pool |
jvm_memory_committed_bytes | JVM committed memory |
jvm_memory_max_bytes | Maximum memory available to the JVM |
jvm_gc_collection_seconds_count | GC count |
jvm_gc_collection_seconds_sum | Cumulative GC time |
jvm_threads_state | Thread count by thread state |
jvm_threads_deadlocked | Number of detected deadlocked thread cycles |
jvm_buffer_pool_used_bytes | Direct/mapped buffer pool usage |
process_cpu_seconds_total | Cumulative CPU time of the Kafka JVM |
process_open_fds / process_max_fds | Open and maximum file descriptors |
process_start_time_seconds | Kafka JVM start time |
JMX: Broker Traffic
| Metric | Type | Meaning |
|---|---|---|
kafka_server_broker_messages_in_total | Counter | Total messages received by the broker |
kafka_server_broker_bytes_in_total | Counter | Total client bytes received by the broker |
kafka_server_broker_bytes_out_total | Counter | Total client bytes sent by the broker |
kafka_server_broker_replication_bytes_in_total | Counter | Total replication bytes received by the broker |
kafka_server_broker_replication_bytes_out_total | Counter | Total replication bytes sent by the broker |
kafka_server_broker_produce_requests_total | Counter | Total produce requests |
kafka_server_broker_failed_produce_requests_total | Counter | Total failed produce requests |
kafka_server_broker_fetch_requests_total | Counter | Total fetch requests |
kafka_server_broker_failed_fetch_requests_total | Counter | Total failed fetch requests |
Broker totals have no topic dimension. Their JMX series count therefore does not grow with topic count. Protocol exporters provide topic offsets and progress.
JMX: Replication and Storage
| Metric | Type | Meaning |
|---|---|---|
kafka_server_replica_manager_under_replicated_partitions | Gauge | Number of partitions whose ISR is smaller than the assigned replica set |
kafka_server_replica_manager_under_min_isr_partitions | Gauge | Number of partitions whose ISR is below min.insync.replicas |
kafka_server_replica_manager_at_min_isr_partitions | Gauge | Number of partitions whose ISR is exactly min.insync.replicas |
kafka_server_replica_manager_offline_replicas | Gauge | Number of offline replicas on the current broker |
kafka_server_replica_manager_partitions | Gauge | Number of replicas hosted by the current broker |
kafka_server_replica_manager_leaders | Gauge | Number of partitions led by the current broker |
kafka_server_replica_manager_isr_shrinks_total | Counter | Total ISR shrink events |
kafka_server_replica_manager_isr_expands_total | Counter | Total ISR expand events |
kafka_server_replica_manager_failed_isr_updates_total | Counter | Total failed ISR updates |
kafka_server_replica_manager_reassigning_partitions | Gauge | Number of leader partitions currently being reassigned |
kafka_server_delayed_operation_purgatory_size | Gauge | Number of delayed operations waiting, split by operation |
kafka_log_manager_offline_log_directories | Gauge | Number of log directories Kafka has marked offline |
Under Replicated means some replicas are not synchronized. Under Min ISR means write availability or durability has fallen below the configured minimum. At Min ISR still meets that minimum but has no remaining replica margin.
JMX: Request Path
| Metric | Type | Extra Labels | Meaning |
|---|---|---|---|
kafka_network_request_total | Counter | request, version | Total requests per Kafka API |
kafka_network_request_errors_total | Counter | request, error | Total error responses per API/error code |
kafka_network_request_total_time_seconds | Gauge | request, version, quantile | Total API time at P50/P95/P99 |
kafka_network_request_queue_size | Gauge | — | Number of requests waiting for a request handler |
kafka_network_response_queue_size | Gauge | — | Number of responses waiting for a network processor |
kafka_server_request_handler_idle_ratio | Gauge | — | Average request-handler idle ratio |
kafka_network_processor_idle_ratio | Gauge | — | Average network-processor idle ratio |
For high latency, examine request volume, errors, P95/P99, both queues, thread idle ratios, GC, CPU, disk I/O, and network. A low idle ratio alone cannot locate the bottleneck.
JMX: KRaft and Broker Metadata
| Metric | Type | Meaning |
|---|---|---|
kafka_server_raft_state | Gauge | The current member’s KRaft state, expressed via the state label |
kafka_server_raft_current_leader | Gauge | Current KRaft leader node ID; -1 means unknown |
kafka_server_raft_current_epoch | Gauge | Current KRaft epoch |
kafka_server_raft_high_watermark | Gauge | Metadata log high watermark |
kafka_server_raft_log_end_offset | Gauge | Metadata log end offset |
kafka_server_broker_metadata_last_applied_record_lag_seconds | Gauge | Time lag of the broker applying metadata records |
kafka_server_broker_metadata_load_errors_total | Counter | Total broker metadata load errors |
kafka_server_broker_metadata_apply_errors_total | Counter | Total broker metadata image apply errors |
kafka_server_metadata_snapshot_bytes | Gauge | Size of the most recently generated or loaded metadata snapshot |
kafka_server_metadata_snapshot_age_seconds | Gauge | Age of the most recent metadata snapshot |
log_end_offset - high_watermark helps estimate metadata commit lag. Also check member role, leader, epoch, and controller event latency.
JMX: Controller
These MBeans exist only in Kafka processes that carry the controller role:
| Metric | Type | Meaning |
|---|---|---|
kafka_controller_active_controller_count | Gauge | 1 on the active controller, 0 on the others |
kafka_controller_fenced_broker_count | Gauge | Number of fenced brokers observed by the active controller |
kafka_controller_active_broker_count | Gauge | Number of active brokers |
kafka_controller_global_topic_count | Gauge | Number of topics observed by the controller |
kafka_controller_global_partition_count | Gauge | Number of partitions observed by the controller |
kafka_controller_offline_partition_count | Gauge | Number of offline non-internal partitions |
kafka_controller_preferred_replica_imbalance_count | Gauge | Number of partitions whose leader is not the preferred replica |
kafka_controller_metadata_errors_total | Counter | Total controller metadata processing errors |
kafka_controller_last_applied_record_lag_seconds | Gauge | Time lag of the controller applying metadata records |
kafka_controller_timed_out_broker_heartbeats_total | Counter | Total broker heartbeat timeouts |
kafka_controller_elections_total | Counter | Total new active-controller elections observed by this node |
kafka_controller_unclean_leader_elections_total | Counter | Total unclean leader elections |
kafka_controller_event_queue_time_seconds | Gauge | Controller event queue time at P50/P95/P99 |
kafka_controller_event_processing_time_seconds | Gauge | Controller event processing time at P50/P95/P99 |
Healthy clusters should have exactly one active controller. Prioritize increases in offline_partition_count, metadata_errors_total, and unclean_leader_elections_total.
Recording Rule Metrics
Offset Progress
| Metric | Aggregation Level | Window | Meaning |
|---|---|---|---|
kafka:topic:msg_rate1m | Topic | 1m | Forward growth rate of current offset, deduplicated across exporters |
kafka:topic:msg_rate5m | Topic | 5m | Forward growth rate of current offset, deduplicated across exporters |
kafka:cls:msg_rate1m | Logical cluster | 1m | Message append rate, deduplicated across exporters |
kafka:cls:msg_rate5m | Logical cluster | 5m | Message append rate, deduplicated across exporters |
kafka:csg_topic:commit_rate5m | Group/Topic | 5m | Forward growth rate of commit offset |
kafka:csg_topic:lag | Group/Topic | current | Partition lag, deduplicated and summed |
kafka:csg:lag | Consumer group | current | Total group lag across topics |
kafka:cls:lag | Logical cluster | current | Total cluster lag across consumer groups |
JVM and Broker
| Metric | Meaning |
|---|---|
kafka:ins:jvm_heap_used_ratio | Heap used / heap max |
kafka:ins:jvm_cpu_cores | 5-minute JVM CPU core consumption |
kafka:ins:load | Saturation of the instance’s busiest request thread pool |
kafka:cls:load | Average load across the cluster’s instances |
kafka:ins:jvm_gc_time_rate5m | 5-minute GC time rate |
kafka:ins:messages_in_rate5m | Broker 5-minute message receive rate |
kafka:ins:bytes_in_rate5m | Broker 5-minute inbound client byte rate |
kafka:ins:bytes_out_rate5m | Broker 5-minute outbound client byte rate |
kafka:ins:request_error_rate5m | 5-minute rate of non-NONE request errors |
kafka:cls:under_replicated_partitions | Total under-replicated partitions in the cluster |
kafka:cls:offline_partitions | Offline partitions in the cluster |
Cardinality and Interpretation Notes
- Do not directly sum multiple
kafka_exporterresults for onecls. They can duplicate cluster state. kafka_topic_partition_current_offsetmeasures offsets, not exact bytes, requests, or business events.- Consumer lag covers only visible groups with committed offsets.
- Controller-only nodes normally lack broker and protocol metrics. Unselected brokers normally lack protocol metrics.
- A JMX series is absent if its MBean does not exist for the Kafka version or role. Interpret this with
role. - The allowlist excludes per-client and per-partition JMX metrics to limit unpredictable series counts.
For how to use the dashboards and alerts, see Monitoring.
8 - FAQ
How mature is the current KAFKA module?
The v1 baseline provides dynamic KRaft, full-cluster checks, cold start and repair, serial broker admission, and dynamic controller join. It supports retirement of live or dead members and three-command failed-node replacement. It also provides strict rolling restarts, TLS/SCRAM/ACL, declared topics and users, protected credential and certificate rotation, and complete monitoring.
Production requires kafka_security: scram, an odd controller count, and sufficient brokers, RF, and minISR. You must plan capacity, reassignment, balancing, upgrades, backup, restore, and failure exercises. This module does not provide a managed Kafka service. Default plaintext suits only development or trusted isolated networks.
Why is there no ZooKeeper and no controller.quorum.voters?
The module targets Kafka 4.1+ with native dynamic KRaft. It installs no ZooKeeper and creates no static quorum. Every member renders controller.quorum.bootstrap.servers. New clusters format explicitly with --initial-controllers/--no-initial-controllers. After startup, the role verifies that initial controller Directory IDs entered live quorum.
The manifest records initial controller identities. After commissioning, live Raft quorum defines membership. kafka.yml adds controllers through observer catch-up and add-controller. Strict-subset kafka-rm.yml runs retire them through remove-controller. Edit the inventory and run the matching playbook.
What is the difference between combined, broker, and controller?
combined: acts as both broker and controller, listening on9092and9093; this is the default;broker: pure data plane, listening only on9092;controller: pure control plane, listening only on9093.
Omit all roles to use combined, or declare roles on every member. Legacy aliases are unavailable.
Will controller port 9093 collide with Alertmanager?
No. Alertmanager uses alertmanager_port 9059 and cluster port 9094. These do not conflict with KRaft 9093. If custom settings conflict, change kafka_controller_port. The role checks only that Kafka ports 9092, 9093, 9308, and 9404 differ. It does not detect conflicts with other services.
The service is up, but a remote client cannot connect?
advertised.listeners always uses inventory_hostname. After bootstrap, clients must resolve and reach every broker address returned in metadata.
Check in order:
For scram, also check the CA, SASL mechanism, credentials, and ACLs. Version 1 lacks custom advertised addresses, multiple listeners, and NAT/public mapping. Clients must route directly to inventory_hostname. Raw listener overrides in kafka_parameters cannot bypass this requirement.
Why does it report a Cluster ID, Node ID, or Directory ID mismatch?
The role compares the manifest, ${kafka_data}/metadata/meta.properties, inventory, and live quorum. Common mismatch causes include:
kafka_clusterorkafka_seqwas changed;- another cluster’s data disk was mounted on the current node;
- an incorrect
kafka_cluster_idwas given during restore/takeover; - the controller data directory or directory ID does not match the live voter records;
- the wrong target cluster was selected, or a stale manifest was used.
This failure protects data. Do not delete meta.properties or the manifest, and do not run kafka-rm.yml. First verify data ownership, surviving replicas, actual Cluster/Node/Directory identities, and the recovery target.
What happens if the manifest is lost or only an old one remains?
Each member stores /etc/kafka/manifest.yml; scram members also store /etc/kafka/secrets.yml. Every run retrieves these from any member copy. Administration nodes keep no Kafka state, so replacing one or losing its checkout does not prevent management. Formatted storage without any manifest copy blocks execution until you restore a copy on one member. A formatted scram cluster also blocks execution if no member retains secrets. Certificates cached in files/pki/kafka/ are signed again by the Pigsty CA if missing.
An existing manifest with all Kafka data disks empty blocks execution. This prevents accidental reuse of a lost cluster’s identity. For an intended rebuild, first run kafka-rm.yml, then follow an explicit rebuild procedure.
Why are some keys in kafka_parameters rejected?
The role owns identity, quorum, listeners, storage, replication, rack, and security settings. Overriding any reserved key fails identity checks before writes. See kafka_parameters for the complete list.
Use the corresponding public parameters instead. The role provides no variables for advertised addresses, path subdirectories, the listener map, or exporter options.
How do I enable TLS, SCRAM, and ACLs?
Set on a new cluster:
This enables Pigsty CA certificates, controller mTLS, broker/client SASL_SSL + SCRAM-SHA-512, StandardAuthorizer, and default denial together. Declare application passwords, ACLs, and optional quotas in kafka_users.
Security mode is a bootstrap-only setting. Ordinary playbooks cannot migrate formatted clusters online from plaintext to scram. That requires a separate migration state machine. Healthy scram clusters support protected internal credential or certificate rotation.
Do kafka_topics and kafka_users delete resources?
No. Removing an entry from the inventory never implicitly deletes a topic or a user.
Topics are created idempotently. Partitions only increase, declared settings alone change, and RF changes require explicit reassignment. Declared users receive their password, complete ACL set, and specified quota fields. Topic deletion, user deletion, and full privilege revocation remain separate audited operations.
What is the difference between the JMX Exporter and kafka_exporter?
JMX Exporter runs inside every Kafka JVM. It collects JVM, broker, replication, request-path, and KRaft metrics under job=kafka, with a role label.
kafka_exporter queries cluster, topic, partition, offset, consumer-group, and lag data through Kafka protocol. It uses job=kafka without a role label. It runs on the first two broker-capable nodes by kafka_seq. A single broker runs one; pure controllers run none.
The exporters provide complementary views. Lifecycle health checks use a separate Kafka CLI/metadata channel and depend on neither exporter.
Why does a particular broker or pure controller have no kafka_exporter?
This placement is expected. Protocol exporters report whole-cluster state rather than node metrics. Two copies avoid a single monitoring failure point while limiting duplicate scrape costs.
Check the current targets (one file per instance; the files of selected nodes contain the :9308 protocol exporter target):
Full runs refresh target files for current exporter placement. Do not run on one node only to register labels. If placement changes, ordinary playbooks do not stop the old kafka_exporter service. Remove it manually or with kafka-rm.yml.
Why is the JMX endpoint reachable but jmx_scrape_error=1?
HTTP access proves only that the Java agent loaded. jmx_scrape_error=1 means the latest MBean scrape failed:
Check /etc/kafka/jmx_exporter.yml against installed Kafka and JMX Exporter versions. Confirm that startDelaySeconds has elapsed. Successful startup requires jmx_scrape_error 0.0, JVM metrics, and at least one role-appropriate kafka_ metric.
Why is there no Consumer Lag data?
Possible causes include consumers without groups, no committed Kafka offsets, external offset storage, or groups that have not consumed the topic. Exporter TLS, SCRAM, ACL, and network problems can also cause missing metrics.
Check kafka_exporter_up, exporter logs, dashboard variables, and raw kafka_consumergroup_* metrics. Use native Prometheus up to assess endpoint availability. Custom metrics can briefly remain after scrape failure.
Why can’t the cluster metrics from two kafka_exporters be added together?
Both exporters can return identical topic, partition, and consumer-group state. Summing them counts data twice. kafka:cls:* rules deduplicate exporter copies before cluster aggregation.
Should applications go through HAProxy, a Keepalived VIP, or an LB?
No. Producers and consumers fetch metadata from a bootstrap.servers seed, then connect directly to partition leaders. A VIP or TCP load balancer cannot identify leaders or rewrite broker addresses. Adding it to the data plane adds long-lived connection state, another failure point, and diagnostic complexity.
DNS or a TCP load balancer can provide a single bootstrap discovery endpoint if required. advertised.listeners must still return reachable addresses for every broker. NAT, public internet, multiple networks, and Kubernetes exposure require dedicated external addresses and extra listeners per broker. The module advertises inventory addresses only and does not support this mapping.
See Quickstart: why applications should connect directly to multiple brokers and Cluster Config: network and listeners.
Can I just add or remove a broker or controller?
Yes. Edit the inventory and use playbooks for each KRaft membership change:
- Add: Declare a
broker,combined, orcontrollermember. Run./kafka.yml -l <cls>on the complete cluster, not just the new node. Pure brokers format, start, and verify registration one at a time. Combined/controller members format with--no-initial-controllers, catch up as observers, and promote throughadd-controller. Every step checks health. - Remove: Run
./kafka-rm.yml -l <ip>on a strict subset. A surviving member performsremove-controllerand broker unregistration, even if the target is unreachable. Then remove the inventory entry.
Plan each membership change. Retain an odd controller count with a live majority. Change one member at a time. Drain departing broker partitions first, or replace it with the same kafka_seq. New brokers do not receive existing partitions automatically. Run and monitor reassignment separately; registration does not imply balanced capacity.
Which parameter controls the package version?
The role uses package_map['java-runtime'] and package_map['kafka-stack']. It has no kafka_version, scala_version, or exporter version parameters. Target-platform repositories and installed packages determine versions.
The payload verified on 2026-07-16 contained Kafka 4.3.1, kafka_exporter 1.9.0, and JMX Exporter 1.6.0. Before upgrades, separately review compatibility, backup, rollback, rolling order, and feature level. Replacing packages alone is insufficient.
How do I safely wipe Kafka data?
kafka.yml never performs cleanup. kafka-rm.yml owns deletion. Selecting a whole cluster with -l, or all clusters without it, performs teardown. A strict subset performs retirement. Default kafka_rm_data=true permanently deletes data, KRaft metadata, node-local /etc/kafka recovery state, and monitoring targets. kafka_rm_data=false retains data and recovery state; kafka_safeguard=true blocks all deletion.
Removal commands execute immediately without an additional confirmation string. Before running, verify the exact -l target, recoverable backups or explicit rebuild intent, and business decommissioning. Retirement tolerates broker unregistration failure. After execution, inspect quorum, registrations, and replica health. See Playbook: kafka-rm.yml.