<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Factor House blog</title><description>Factor House is the personal finance app that turns spending, saving and investing into one calm, clear view. Track every dollar and grow every goal — without the spreadsheet headache.</description><link>https://factorhouse.io/</link><item><title>Clone to topic for Dead Letter Queues in Apache Kafka</title><link>https://factorhouse.io/articles/clone-to-topic-for-kafka-dlq/</link><guid isPermaLink="true">https://factorhouse.io/articles/clone-to-topic-for-kafka-dlq/</guid><description>Learn about Clone to Topic, the latest feature available in Kpow 96.2, enabling you to replay Dead Letter Queue (DLQ) records inside a governed UI.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kpow 96.2 adds Clone to topic, a byte-level clone of records to a retry topic directly from Data Inspect search results. Cloning utilises the same Role-Based Access Control (RBAC) and audit log controls as the rest of Kpow, so replaying Dead Letter Queue (DLQ) records becomes a governed UI operation instead of bespoke CLI tooling.&lt;/p&gt;
&lt;h2 id=&quot;dlqworkflow-within-kpow&quot;&gt;DLQ workflow within Kpow&lt;/h2&gt;
&lt;p&gt;DLQs are a common approach when using Kafka to park records that cannot be processed, rather than blocking the consumer or dropping records. The problem comes later when listing those records for investigation and replaying them once the underlying issue is resolved, which can be a manual and error-prone process, even when teams build custom CLI tooling for it.&lt;/p&gt;
&lt;h3 id=&quot;listing&quot;&gt;Listing&lt;/h3&gt;
&lt;p&gt;Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview&quot;&gt;Data Inspect&lt;/a&gt; searches records across multiple topics in a single query. For listing DLQ records, Kpow’s long-lived search result tabs really shine: They maintain cursors to continue consuming records on demand. Some of our customers keep browser sessions open for multiple weeks to do just that!&lt;/p&gt;
&lt;h3 id=&quot;replaying&quot;&gt;Replaying&lt;/h3&gt;
&lt;p&gt;Kpow 96.2 introduces Clone to topic for both individual records and a batch of Data Inspect search results, via our &lt;a href=&quot;https://docs.factorhouse.io/kpow/workflow/bulk-actions&quot;&gt;Bulk actions&lt;/a&gt;, performing a byte-level clone to a retry topic in a few clicks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a3b571e48bff48deec18b0f_clone-to-topic-looped.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;When you pair this with Kpow’s RBAC policies, you can &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview#clone-to-topic&quot;&gt;restrict the source and destination topics for clones&lt;/a&gt;. For example, on a &lt;code&gt;payments&lt;/code&gt; topic you can enforce that only records from per-consumer DLQ topics may be cloned into per-consumer retry topics:&lt;/p&gt;
&lt;p&gt;Some records need correcting before replay rather than a straight clone. For those, you can &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-produce#data-inspect&quot;&gt;send Data Inspect results to Data Produce&lt;/a&gt;, preview and amend the records, then produce them to a retry topic. Malformed records are best fixed at the root cause, but this gives you a path for the cases that slip through.&lt;/p&gt;
&lt;h2 id=&quot;available-now&quot;&gt;Available now&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a3b57c31804305bad515da6_clone-to-topic-form.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Clone to topic turns DLQ replay from a manual, ungoverned task into a few clicks, with RBAC and audit logging applied to every clone. It is available now in Kpow 96.2. See the &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview#clone-to-topic&quot;&gt;Data Inspect documentation&lt;/a&gt; for setup and governance details.&lt;/p&gt;
</content:encoded><category>Product</category><author>Nicolas Venegas</author></item><item><title>Kafdrop: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/kafdrop/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafdrop/</guid><description>Kafdrop review for 2026: strengths, limitations, pricing, and the best alternatives for platform and data engineers running production Kafka clusters.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafdrop is a free, open-source Kafka UI built on Spring Boot, used most often for local development and simple single-cluster setups.&lt;/li&gt;
&lt;li&gt;It has no built-in authentication or access control; any internet-facing deployment requires an external auth proxy or gateway.&lt;/li&gt;
&lt;li&gt;KRaft mode (the default Kafka cluster mode since Kafka 3.3) is not supported, and the maintainers closed the request as “not planned.”&lt;/li&gt;
&lt;li&gt;Performance degrades sharply at scale: one reported case took over 30 minutes to load topic and partition data for a cluster with roughly 1,000 topics.&lt;/li&gt;
&lt;li&gt;If you need RBAC, reliable performance at scale, or a supported product with a maintained roadmap, Kpow from Factor House is a commercial alternative worth evaluating.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafdrop&quot;&gt;What is Kafdrop?&lt;/h2&gt;
&lt;p&gt;Kafdrop is an open-source web UI for &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt;, built on the Spring Boot framework. It provides a browser-based interface for browsing topics, viewing partition state, inspecting messages, and performing basic topic administration such as creating and deleting topics.&lt;/p&gt;
&lt;p&gt;The project is hosted at obsidiandynamics/kafdrop on GitHub and maintained by the Obsidian Dynamics team. It is widely cited as one of the easiest Kafka UIs to deploy in a local Docker Compose environment, which explains its prevalence in tutorials and development workflows.&lt;/p&gt;
&lt;p&gt;Kafdrop runs as a stateless Java process and connects to a Kafka cluster over standard broker protocols. It does not require a separate backend datastore, which keeps the local setup minimal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69f430c5edb959134c22195a_kafdrop-kafka-monitoring.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kafdrop-review&quot;&gt;Kafdrop review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Kafdrop covers the fundamentals: topic browsing, partition inspection, and message viewing. Developer cnatsis, in a monitoring tools evaluation thread on r/apachekafka, classifies it as a “Topic Administration tool” used specifically to “create/edit/delete topics &amp;amp; view messages.”&lt;/p&gt;
&lt;p&gt;The feature set beyond those basics is limited. Kafdrop does not support searching or filtering messages within a topic by key or value content. Developer felheartx, in a comparative analysis on r/devops, describes this gap directly: “Kafdrop doesn’t offer the ability to search for specific messages within a topic (e.g. filter by a provided javascript expression, or find a message where the key equals a specific string).”&lt;/p&gt;
&lt;p&gt;Message encoding is not auto-detected either. Users must manually configure the deserialization format per topic; newer tools handle this automatically, whereas Kafdrop “requires the user to actively configure that,” as felheartx notes in the same thread.&lt;/p&gt;
&lt;p&gt;Performance is a documented constraint at scale. GitHub issue #270 (obsidiandynamics/kafdrop) records a case where Kafdrop took over 30 minutes to load topic and partition information when connected to an SSL-enabled Kafka broker with approximately 1,010 topics and 2,000 partitions.&lt;/p&gt;
&lt;p&gt;In published comparisons, Kafdrop sits below richer alternatives. Duc Quoc, writing for Towards Data Science, characterises Kafdrop as “a pretty average tool” with an interface that “is not spectacular,” and recommends Kafka-UI for teams with more complex operational needs.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Kafdrop is consistently praised for being lightweight and fast to deploy. On r/dotnet, developer DotDeveloper describes it as “super lightweight and easy to spin up if you just want to see what’s flowing through your topics.” On r/apachekafka, johannz adds: “It’s not a full management tool but it’s quick and easy to troubleshoot issues with.”&lt;/p&gt;
&lt;p&gt;It runs comfortably alongside a standard local Kafka stack with minimal resource overhead. One engineer on r/dataengineering reported running four Docker containers (orchestrator, Kafka, Zookeeper, and Kafdrop) on an Apple M2 MacBook Air with 24 GB RAM alongside active editors and remote desktops without resource issues.&lt;/p&gt;
&lt;p&gt;One friction point for Kubernetes teams: Kafdrop’s Helm chart source code is available in the repository, but it is not hosted in any official Helm repository. Teams cannot install it via a standard &lt;code&gt;helm repo add&lt;/code&gt; / &lt;code&gt;helm install&lt;/code&gt; workflow. The Pi Cluster open-source project documents this gap explicitly and deploys Kafdrop using Kustomize as a workaround.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;Kafdrop has no built-in authentication or access control. Developer felheartx, in the r/devops comparative analysis, states that Kafdrop “doesn’t have an authentication &amp;amp; authorization concept (user is in charge of protecting the webapp using third party tools…).”&lt;/p&gt;
&lt;p&gt;A Help Net Security article from December 2021 describes the consequence plainly: Kafdrop provides “a UI to make it easy to review live Kafka clusters, without authentication.” An internet-facing instance exposes full cluster visibility with no login barrier.&lt;/p&gt;
&lt;p&gt;The Pi Cluster open-source project, which deploys Kafdrop in a production-adjacent Kubernetes environment, confirms that this remains true: their implementation relies on an external gateway (Envoy Gateway) to enforce security policy, since Kafdrop provides no user management natively.&lt;/p&gt;
&lt;p&gt;Connecting Kafdrop to a SASL-secured broker is also a friction point. On r/apachekafka, user Youth-Character describes the experience: “i was using kafdrop with kafka and it was working fine but when i added sasl authentication i can’t seem to find any docs on how to integrate kafdrop.” The workaround requires manually mapping external &lt;code&gt;.properties&lt;/code&gt; files inside the Docker container.&lt;/p&gt;
&lt;p&gt;Native RBAC and SSO support are not available.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;The UI is functional within its intended scope. For a quick look at a single cluster, it loads and navigates without friction.&lt;/p&gt;
&lt;p&gt;Practitioners who need more describe it as minimal rather than polished. Duc Quoc’s Towards Data Science review positions Kafdrop as suitable for teams wanting “a quick visual view of a simple cluster, not for teams that need rich operational tooling or daily-driver UI features.” No strong positive characterisation of the UI surface surfaced in the sources reviewed; neutral-to-adequate is the dominant register.&lt;/p&gt;
&lt;p&gt;Kafdrop’s visualisation can also surface symptoms without providing context for root cause analysis. One developer on r/dataengineering described observing all messages routing to a single partition in Kafdrop, where the tool had no mechanism to indicate that the root cause was a producer-side key-hashing configuration rather than a broker or UI failure.&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Kafdrop does not natively support AWS IAM MSK authentication. A community workaround exists via environment variables (&lt;code&gt;KAFKA_IAM_ENABLED&lt;/code&gt;, &lt;code&gt;KAFKA_SASL_MECHANISM=AWS_MSK_IAM&lt;/code&gt;) for connecting to IAM-authenticated MSK clusters on EKS using IRSA. The underlying pull request (PR #287, obsidiandynamics/kafdrop) remained open with unresolved merge conflicts for over a year. A collaborator stated willingness to merge the contribution but the conflicts were never resolved, indicating slow responsiveness to cloud-integration work from the community.&lt;/p&gt;
&lt;p&gt;KRaft mode (ZooKeeper-free Kafka, the default from Kafka 3.3 onward) is not supported. The topic view becomes unresponsive when connecting to a KRaft cluster. GitHub issue #670 (obsidiandynamics/kafdrop) was closed as “not planned,” meaning there is no committed roadmap to address this incompatibility.&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Kafdrop is an open-source project with no enterprise support tier, no dedicated community forum, and no documented support channel beyond GitHub issues.&lt;/p&gt;
&lt;p&gt;Maintainer responsiveness appears slow based on the available evidence. PR #287 (AWS MSK IAM support) sat open with unresolved conflicts for more than a year without a resolution. Issue #670 (KRaft support) was closed as “not planned” rather than addressed. Both cases suggest the project is not actively expanding its compatibility surface or cloud integration story.&lt;/p&gt;
&lt;p&gt;For a tool used primarily in local development environments, this may be acceptable. For teams that need timely fixes or cloud-integration support from a maintained codebase, the absence of any supported tier is a genuine constraint.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Kafdrop suits individual engineers and small teams who need a zero-cost, fast-to-deploy visual layer over a single Kafka cluster in a local or sandbox environment. It fits naturally into Docker Compose development stacks, where it can be added as a container without meaningful resource overhead and removed just as easily.&lt;/p&gt;
&lt;p&gt;It is a poor fit for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Teams running KRaft-mode Kafka clusters (Kafka 3.3+ default):&lt;/strong&gt; the incompatibility is known and closed as not planned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS MSK deployments using IAM authentication:&lt;/strong&gt; native support does not exist; the community workaround is unofficially maintained.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Any team needing authentication, RBAC, or SSO:&lt;/strong&gt; none of these are available natively, and layering them in requires platform engineering overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Large clusters with 1,000+ topics and SSL:&lt;/strong&gt; severe performance degradation is documented.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-cluster management:&lt;/strong&gt; not supported.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;kafdrop-pricing&quot;&gt;Kafdrop pricing&lt;/h2&gt;
&lt;p&gt;Kafdrop is free and open-source, released under the Apache License 2.0. There are no licensing fees, subscription plans, or commercial editions.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;There is a single tier: free. The project has no commercial offering of any kind.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;There is no trial concept because Kafdrop has no paid tier to trial. You can run it immediately from the public Docker image or build it from source at no cost.&lt;/p&gt;
&lt;h2 id=&quot;kafdrop-competitors-and-alternatives&quot;&gt;Kafdrop competitors and alternatives&lt;/h2&gt;
&lt;p&gt;Teams that outgrow Kafdrop typically move toward tools with built-in authentication, richer message search, or multi-cluster support. The market spans free open-source options, community-edition commercial tools, and fully commercial platforms with enterprise support.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment &amp;amp; ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;Enterprise teams needing native identity provider integration&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic admin, consumer group management, native Azure AD/OIDC&lt;/td&gt;
&lt;td&gt;Web-based; higher resource footprint than Kafdrop&lt;/td&gt;
&lt;td&gt;Native OIDC/OAuth2&lt;/td&gt;
&lt;td&gt;Full-featured; more complex UI than Kafdrop&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Console (formerly Kowl)&lt;/td&gt;
&lt;td&gt;Teams needing JS-based message filtering and Protobuf support&lt;/td&gt;
&lt;td&gt;OSS / Commercial&lt;/td&gt;
&lt;td&gt;JS expression search, dynamic Protobuf schema compilation, auto-decoding&lt;/td&gt;
&lt;td&gt;Go / React SPA; lightweight&lt;/td&gt;
&lt;td&gt;Available in commercial tiers&lt;/td&gt;
&lt;td&gt;Modern, polished&lt;/td&gt;
&lt;td&gt;Free OSS tier; commercial tiers via Redpanda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka-UI&lt;/td&gt;
&lt;td&gt;Teams with multi-cluster environments and moderate monitoring needs&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Multi-cluster management, topic admin, consumer group monitoring&lt;/td&gt;
&lt;td&gt;Web-based; Docker and Kubernetes&lt;/td&gt;
&lt;td&gt;Basic auth available&lt;/td&gt;
&lt;td&gt;Clean, functional&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lenses&lt;/td&gt;
&lt;td&gt;Teams requiring SQL querying over Kafka topics and connector monitoring&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;SQL Studio, topology maps, automated connector restarts, DLQ management&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;RBAC, SSO&lt;/td&gt;
&lt;td&gt;Rich, SQL-driven&lt;/td&gt;
&lt;td&gt;Tiered, with seat limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kpow by Factor House&lt;/td&gt;
&lt;td&gt;Teams needing enterprise RBAC, high performance at scale, and dedicated support&lt;/td&gt;
&lt;td&gt;Commercial, Community Edition available&lt;/td&gt;
&lt;td&gt;Topic admin, consumer group management, schema registry, advanced RBAC, audit logging&lt;/td&gt;
&lt;td&gt;Stateless; Docker, Kubernetes, ECS&lt;/td&gt;
&lt;td&gt;Advanced RBAC; WCAG-compliant UI&lt;/td&gt;
&lt;td&gt;Performant, WCAG-compliant&lt;/td&gt;
&lt;td&gt;Per-cluster; no per-seat penalty as team grows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader comparison of Kafka UI tools, see the &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Kafka UI comparison guide.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-kafdrop&quot;&gt;Frequently asked questions about Kafdrop&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-kafdrop-cost-and-is-there-a-free-tier&quot;&gt;How much does Kafdrop cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;Kafdrop is free. It is open-source software released under the Apache License 2.0. There are no paid plans, commercial editions, or usage limits. You download and run it at no cost.&lt;/p&gt;
&lt;h3 id=&quot;when-is-kafdrop-a-better-choice-than-the-alternatives&quot;&gt;When is Kafdrop a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;Kafdrop suits teams that need a zero-cost, minimal-setup Kafka UI for local or sandbox use. If you are running a single ZooKeeper-based cluster in Docker Compose and need quick topic visibility, Kafdrop installs in minutes and adds negligible resource overhead.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-kafdrop&quot;&gt;When are the alternatives a better choice than Kafdrop?&lt;/h3&gt;
&lt;p&gt;Alternatives suit you better when you need authentication or access control built in, KRaft cluster support, message search and filtering, AWS MSK with IAM auth, multi-cluster management, or reliable performance at scale. Kafdrop covers none of these natively.&lt;/p&gt;
&lt;h3 id=&quot;does-kafdrop-support-kraft-mode&quot;&gt;Does Kafdrop support KRaft mode?&lt;/h3&gt;
&lt;p&gt;No. Kafdrop fails to display topic information when connected to a KRaft cluster, and GitHub issue #670 requesting support was closed as “not planned.” Teams running Kafka 3.3 or later in KRaft mode should evaluate alternative tools before committing to Kafdrop.&lt;/p&gt;
&lt;h3 id=&quot;is-kafdrop-safe-to-deploy-outside-a-local-environment&quot;&gt;Is Kafdrop safe to deploy outside a local environment?&lt;/h3&gt;
&lt;p&gt;With careful configuration. Kafdrop has no built-in authentication, so an unprotected instance exposes full cluster visibility to anyone who can reach it. Teams running it outside a local network must place it behind an auth proxy. It also exposes write operations, so accidental topic deletion is possible.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Kafka dashboard: the features that matter in production</title><link>https://factorhouse.io/articles/kafka-dashboard/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-dashboard/</guid><description>A Kafka dashboard gives you real-time visibility into consumer lag, broker health, and partition state. Here&apos;s what to look for and how Kpow delivers it in production.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Running Apache Kafka in production means managing distributed state across brokers, topics, partitions, and consumer groups at the same time. A Kafka dashboard gives you a single interface to observe that state in real time, without assembling metrics by hand or reaching for the CLI every time something looks wrong. This article covers what a production-grade Kafka dashboard should do, what to look for when evaluating one, and how &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; by Factor House approaches the problem.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-kafka-dashboard&quot;&gt;What is a Kafka dashboard?&lt;/h2&gt;
&lt;p&gt;A Kafka dashboard is a web interface that aggregates and visualises the operational state of an Apache Kafka cluster. It typically displays broker health, consumer group lag, partition assignments, topic throughput, and schema registry state, giving platform and data engineering teams a shared view of a cluster without running CLI commands manually.&lt;/p&gt;
&lt;p&gt;Most CLI tools surface a snapshot of a single metric at a time. A Kafka dashboard aggregates across brokers and topics, renders trends over time, and makes cluster state readable to anyone on the team, not just whoever knows the right &lt;code&gt;kafka-consumer-groups.sh&lt;/code&gt; flags. At the partition level, that difference matters: a group-level lag summary can look acceptable while a single partition is falling badly behind.&lt;/p&gt;
&lt;p&gt;A Kafka dashboard typically visualises:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Broker health and JVM metrics&lt;/li&gt;
&lt;li&gt;Consumer group lag, by topic and by partition&lt;/li&gt;
&lt;li&gt;Partition assignments and ISR (in-sync replica) state&lt;/li&gt;
&lt;li&gt;Topic throughput in messages and bytes per second&lt;/li&gt;
&lt;li&gt;Schema registry contents and schema evolution state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe9961b0c8180a1e940601_kpow-cluster-management.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;An example Kafka dashboard - Kpow by Factor House&lt;/p&gt;
&lt;h2 id=&quot;why-kafka-is-hard-to-observe&quot;&gt;Why Kafka is hard to observe&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka’s architecture&lt;/a&gt; makes operational visibility harder than it initially appears. A healthy cluster requires every broker to agree on partition leadership, maintain replication, and serve consumers at acceptable latency simultaneously. When something is off, the symptom and the cause are often in different parts of the cluster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;Consumer lag&lt;/a&gt; is a particular challenge. It is a lagging indicator by nature: by the time lag accumulates to a level that looks alarming at the group level, the relevant consumer may already be hours behind on specific partitions. Without partition-level visibility, you are watching an average that conceals the problem.&lt;/p&gt;
&lt;p&gt;Schema evolution adds another layer. When a producer changes a schema and a downstream consumer has not been updated, you may not see an error until records hit the consumer’s deserialiser. A dashboard with schema registry integration makes this state visible before it causes failures.&lt;/p&gt;
&lt;p&gt;At scale, manual inspection becomes impractical. A cluster with hundreds of topics, multiple consumer groups, and several environments cannot be monitored with periodic CLI snapshots. Production operations require continuous, aggregated visibility.&lt;/p&gt;
&lt;h2 id=&quot;what-to-look-for-in-a-kafka-dashboard&quot;&gt;What to look for in a Kafka dashboard&lt;/h2&gt;
&lt;p&gt;Not all Kafka dashboards are built for production use. Some are read-only tools useful in development. Others surface cluster-level metrics but lose detail at the partition level, which is exactly where most operational problems hide. When evaluating options, the following criteria are worth assessing carefully.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time lag visibility at the partition level.&lt;/strong&gt; Group-level lag summaries are a useful starting point, but they are not sufficient for production. A partition within a topic can be stalled while others process normally, and a group-level average will not surface it. A production-grade dashboard shows lag per partition, updates continuously, and lets you drill from the group view to individual partitions without switching tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer group inspection and offset management.&lt;/strong&gt; Beyond observing lag, your team needs to act on it. That means being able to inspect individual consumer group members, see their partition assignments, and when necessary, reset or adjust offsets directly from the UI. Without this, offset management defaults back to the CLI, with all the access-control and audit implications that involves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker and partition health metrics.&lt;/strong&gt; Broker health goes beyond whether a broker is reachable. Under-replicated partitions, ISR changes, and leader imbalances are the early signals of instability. A dashboard should surface these alongside the topics and partitions affected, not just a raw count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-cluster support.&lt;/strong&gt; Platform teams routinely manage separate clusters for development, staging, and production. A dashboard that requires a separate deployment per cluster, or cannot aggregate views across environments, creates avoidable operational overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema Registry integration.&lt;/strong&gt; For teams using Avro, Protobuf, or JSON Schema, the registry is a critical part of the pipeline. A dashboard that integrates schema registry state lets you see which schemas are in use, view historical versions, and spot compatibility issues before they reach consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RBAC and access control.&lt;/strong&gt; Not everyone who needs to view the Kafka dashboard should be able to reset offsets or modify topic configuration. Role-based access control lets you assign permissions at the operation level, per team. Without it, you are typically choosing between open access and restricted access, with nothing in between. In regulated environments, audit logging matters equally: a record of who did what, and when, is often a compliance requirement rather than a convenience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operational simplicity.&lt;/strong&gt; A monitoring tool that is itself hard to maintain creates a different kind of overhead. Deployment model, configuration surface area, and the number of external dependencies all affect how much engineering time the dashboard consumes. The simpler the deployment, the less cognitive load it adds.&lt;/p&gt;
&lt;p&gt;Kpow is built to meet these criteria without requiring significant configuration effort or ongoing maintenance.&lt;/p&gt;
&lt;h2 id=&quot;kpow-a-kafka-dashboard-for-production-environments&quot;&gt;Kpow: a Kafka dashboard for production environments&lt;/h2&gt;
&lt;p&gt;Kpow is a Kafka dashboard built by Factor House. It deploys inside your own infrastructure as a single stateless JVM container, connects directly to your Kafka cluster, and requires no external data transmission. Cluster data stays within your environment.&lt;/p&gt;
&lt;p&gt;The deployment model is deliberate. For teams in regulated industries or with strict data residency requirements, sending cluster telemetry to an external SaaS platform is not always acceptable. Kpow runs on-premises, in a Docker container, in a Kubernetes pod, or on any JVM-compatible host, wherever your Kafka cluster lives.&lt;/p&gt;
&lt;p&gt;It is designed for teams running Kafka seriously in production: platform engineers managing multiple clusters, data engineering teams that need reliable observability without assembling it from individual components, and organisations that require audit logging and access control as part of their compliance posture.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe95d992a57ef02cacfcf7_kpow-topics.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Monitoring Topics in Kpow&lt;/p&gt;
&lt;h2 id=&quot;core-features-of-kpows-kafka-dashboard&quot;&gt;Core features of Kpow’s Kafka dashboard&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Real-time consumer lag visibility.&lt;/strong&gt; Kpow surfaces lag at the partition level, not just the consumer group level. From the consumer groups view, you can drill from a group summary to individual partitions in a few clicks, seeing current offset, log end offset, and lag count per partition as they update. For teams managing high-throughput pipelines, the difference between group-level and partition-level visibility is often the difference between catching a stalled consumer early and discovering it after records have accumulated for hours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker and cluster health.&lt;/strong&gt; The broker view surfaces JVM metrics alongside Kafka-specific health indicators: under-replicated partition counts, ISR state, and leader distribution across the cluster. Leader imbalance is a common source of uneven load in large clusters and is difficult to detect without partition-level visibility. Kpow aggregates this state without requiring a separate JMX exporter or Prometheus pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topic and partition management.&lt;/strong&gt; From the Kpow UI you can inspect topic configuration, view partition assignments, create new topics, and modify configuration without leaving the dashboard. Partition-level inspection shows which brokers hold which replicas and the current replication state, which is useful when investigating ISR issues or planning rebalancing operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer group inspection and offset management.&lt;/strong&gt; Kpow lets you inspect the current state of any consumer group: member assignments, partition ownership, current and committed offsets, and group coordinator. When you need to reset offsets, the operation is performed through the UI and recorded in the audit log, keeping offset management accessible to authorised users without requiring CLI access to the cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema Registry integration.&lt;/strong&gt; Kpow integrates with Confluent Schema Registry, exposing schema subjects, versions, and compatibility settings directly in the dashboard. Teams using Avro, Protobuf, or JSON Schema can browse schemas and view historical versions without switching to a separate tool. Schema Registry visibility is useful when debugging serialisation errors or planning schema evolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-cluster support.&lt;/strong&gt; A single Kpow deployment can connect to multiple Kafka clusters, including self-managed brokers and managed services such as Amazon MSK, Confluent Cloud, Redpanda, Aiven, StreamNative, Google Cloud Managed Kafka, NetApp Instaclustr, and Oracle Cloud. Each cluster appears under its own namespace in the UI with independent access control configuration. For platform teams managing several environments, this eliminates separate dashboard deployments per cluster and simplifies access management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Streams and Kafka Connect.&lt;/strong&gt; Kpow extends beyond core Kafka to cover the wider ecosystem. For teams using Kafka Streams, it surfaces stream topology state and task assignments. For teams using Kafka Connect, it shows connector state, task status, and configuration. Both are surfaces that are typically invisible in basic monitoring setups and that matter when debugging data pipeline failures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a3e32c352d23af75f5851cd_kpow-topology.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Viewing Kafka Streams in Kpow&lt;/p&gt;
&lt;h2 id=&quot;kpow-vs-open-source-alternatives&quot;&gt;Kpow vs open-source alternatives&lt;/h2&gt;
&lt;p&gt;The most common alternatives to Kpow are &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt;, &lt;a href=&quot;/articles/cmak&quot;&gt;CMAK&lt;/a&gt;, and a DIY stack built on &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Grafana&lt;/a&gt;, &lt;a href=&quot;/how-to/kafka-alerting-with-kpow-prometheus-and-alertmanager&quot;&gt;Prometheus&lt;/a&gt;, and JMX exporters. Each has a legitimate use case, but the trade-offs become significant as production requirements grow.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Kpow&lt;/th&gt;
&lt;th&gt;AKHQ&lt;/th&gt;
&lt;th&gt;Kafdrop&lt;/th&gt;
&lt;th&gt;Grafana + Prometheus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consumer lag (partition-level)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;With JMX exporter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic and partition management&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offset management&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RBAC&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit logging&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema Registry integration&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Streams visibility&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Connect visibility&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-cluster (single deployment)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (multiple scrape targets)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commercial support&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AKHQ&lt;/strong&gt; (v0.27.0, March 2025) is a capable open-source Kafka dashboard with topic browsing, consumer group inspection, and schema registry integration. It includes role-based access control with LDAP and OIDC integration, but the implementation has notable limits in production: there is no audit logging, no native data masking, and fine-grained multi-cluster RBAC requires additional configuration effort. For teams with compliance requirements around PII, HIPAA, PCI-DSS, or GDPR, these gaps are significant. AKHQ also has no commercial support, which means issue resolution depends on the open-source community and project maintainers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafdrop&lt;/strong&gt; (v4.2.0, July 2025) is a lightweight Kafka UI suited to development environments and ad-hoc inspection. It displays topic metadata, partition assignments, and message content, but it does not support consumer group offset reset, has no access control, and provides no audit trail. The project itself describes Kafdrop as a developer tool rather than team infrastructure. Most teams use it for local development convenience and look for something more capable in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Grafana with Prometheus and JMX exporters&lt;/strong&gt; can surface Kafka broker metrics effectively, but it is a metrics pipeline, not a Kafka management tool. Setting it up requires configuring the JMX exporter per broker, writing or importing dashboard JSON, and managing the Prometheus scrape configuration. It provides no topic management, no offset manipulation, and no consumer group inspection. Keeping the JMX dashboards accurate as the cluster changes is also ongoing work. The result is a metrics view rather than a Kafka dashboard in the operational sense.&lt;/p&gt;
&lt;p&gt;The core trade-off with open-source options is engineering time. The tooling is free, but configuring it, maintaining it, and covering the gaps around access control and audit logging is not. Kpow is a commercial product that runs out of the box and includes the capabilities that open-source alternatives require significant effort to approximate or cannot provide at all.&lt;/p&gt;
&lt;h2 id=&quot;who-uses-kpow-and-when&quot;&gt;Who uses Kpow and when&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Platform and infrastructure teams managing multiple environments.&lt;/strong&gt; When a team is responsible for several Kafka clusters across development, staging, and production, operational overhead compounds quickly. Kpow’s multi-cluster support and centralised access control reduce the surface area they need to manage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineering teams in regulated industries.&lt;/strong&gt; Finance, healthcare, and government organisations often need audit logging and RBAC as part of their compliance posture, not as optional extras. Kpow provides both out of the box, deployed entirely within the organisation’s own infrastructure, with no data leaving the environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Development teams who need faster feedback.&lt;/strong&gt; Even outside production, a team building against Kafka benefits from being able to inspect consumer groups, view messages, and confirm schema compatibility without reaching for the CLI. Kpow is fast to deploy in a development environment and straightforward enough that its value is immediate.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-kpow&quot;&gt;Getting started with Kpow&lt;/h2&gt;
&lt;p&gt;Kpow connects to any Kafka cluster via standard bootstrap properties. It deploys as a Docker container, a JAR, or via Helm chart, and can be running against your cluster in a few minutes. There is no agent to install on your brokers, and no cluster data leaves your environment.&lt;/p&gt;
&lt;p&gt;You can try Kpow with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. If you want to see it against your own cluster before committing, a guided demo is also available.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;
&lt;h3 id=&quot;what-is-a-kafka-dashboard-1&quot;&gt;What is a Kafka dashboard?&lt;/h3&gt;
&lt;p&gt;A Kafka dashboard is a web interface that aggregates and visualises the operational state of an Apache Kafka cluster. It typically displays broker health, consumer group lag, partition assignments, topic throughput, and schema registry state, giving platform and data engineering teams a single view of a cluster without running CLI commands manually.&lt;/p&gt;
&lt;h3 id=&quot;what-metrics-should-a-kafka-dashboard-show&quot;&gt;What metrics should a Kafka dashboard show?&lt;/h3&gt;
&lt;p&gt;A production-grade Kafka dashboard should display consumer group lag by topic and partition, broker health and JVM metrics, under-replicated partition count, ISR state, topic throughput in messages and bytes per second, and consumer group member state. Partition-level detail is important: group-level summaries alone can mask significant lag in specific partitions.&lt;/p&gt;
&lt;h3 id=&quot;can-i-use-grafana-as-a-kafka-dashboard&quot;&gt;Can I use Grafana as a Kafka dashboard?&lt;/h3&gt;
&lt;p&gt;Grafana combined with JMX exporters and Prometheus can surface Kafka broker metrics, but it requires significant setup: you need to configure the JMX exporter per broker, write or import dashboard JSON, and manage the Prometheus scrape pipeline. It also provides no topic management, offset manipulation, or consumer group inspection. For a team that needs both metrics and management, Grafana covers one part of the picture rather than acting as a complete Kafka dashboard.&lt;/p&gt;
&lt;h3 id=&quot;is-there-a-free-kafka-dashboard&quot;&gt;Is there a free Kafka dashboard?&lt;/h3&gt;
&lt;p&gt;Several open-source Kafka dashboards are available at no cost. AKHQ and Kafdrop are the most widely used. Both offer topic browsing and consumer group inspection. AKHQ includes partial RBAC via LDAP and OIDC integration; Kafdrop has no access control at all. Neither provides audit logging, which is a common requirement in regulated environments.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-monitor-kafka-consumer-lag&quot;&gt;How do I monitor Kafka consumer lag?&lt;/h3&gt;
&lt;p&gt;Consumer lag in Kafka is the difference between the latest offset produced to a partition and the last offset committed by a consumer group. You can check it with &lt;code&gt;kafka-consumer-groups.sh --describe&lt;/code&gt; for a point-in-time snapshot, or use a dashboard that polls &lt;code&gt;__consumer_offsets&lt;/code&gt; and surfaces lag per partition continuously. Partition-level visibility is what distinguishes a production-grade dashboard from basic tooling: group-level averages can look acceptable while individual partitions fall significantly behind.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-difference-between-a-kafka-dashboard-and-kafka-monitoring&quot;&gt;What is the difference between a Kafka dashboard and Kafka monitoring?&lt;/h3&gt;
&lt;p&gt;Kafka monitoring is the broader practice of collecting, alerting on, and analysing Kafka metrics, often using Prometheus, Grafana, or a commercial observability platform. A Kafka dashboard adds interactive management: browsing topics, inspecting consumer groups, managing offsets, and viewing schemas. Most Kafka dashboards include monitoring views; most monitoring tools do not include management capabilities.&lt;/p&gt;
&lt;h3 id=&quot;how-does-kpow-compare-to-akhq&quot;&gt;How does Kpow compare to AKHQ?&lt;/h3&gt;
&lt;p&gt;Kpow is a commercial Kafka dashboard with RBAC, audit logging, multi-cluster support, and Kafka Streams and Connect visibility built in. AKHQ is an open-source alternative with strong topic and consumer group browsing and some RBAC via LDAP and OIDC, but no audit logging and limited fine-grained access control in multi-cluster environments. The main trade-off is operational overhead: AKHQ requires more configuration and ongoing maintenance; Kpow is designed to run in production without significant setup.&lt;/p&gt;
&lt;h3 id=&quot;does-kpow-support-confluent-cloud-and-amazon-msk&quot;&gt;Does Kpow support Confluent Cloud and Amazon MSK?&lt;/h3&gt;
&lt;p&gt;Yes. Kpow supports a range of managed Kafka services including Amazon MSK, Confluent Cloud, Redpanda, Aiven, StreamNative, Google Cloud Managed Kafka, NetApp Instaclustr, and Oracle Cloud. Connection is configured via standard Kafka bootstrap properties. Check the &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow documentation&lt;/a&gt; for any provider-specific configuration notes.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-best-kafka-monitoring-tool&quot;&gt;What is the best Kafka monitoring tool?&lt;/h3&gt;
&lt;p&gt;The right tool depends on your requirements. For teams that need metrics only, Prometheus with JMX exporters and Grafana is a common open-source approach. For teams that need management capabilities, RBAC, audit logging, and cross-cluster visibility in a single product, a dedicated Kafka dashboard like Kpow is a better fit. Open-source tools like AKHQ or Kafdrop suit development environments or smaller teams with lower compliance requirements.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-visualise-kafka-consumer-groups&quot;&gt;How do I visualise Kafka consumer groups?&lt;/h3&gt;
&lt;p&gt;Consumer group state can be viewed with &lt;code&gt;kafka-consumer-groups.sh --describe&lt;/code&gt;, which shows lag, current offset, log end offset, and consumer ID per partition. A Kafka dashboard goes further: it shows group state (stable, rebalancing, empty), member assignments per partition, and real-time lag trends, making it easier to spot rebalancing storms or stalled consumers without scripting or periodic manual checks.&lt;/p&gt;
&lt;h3 id=&quot;is-kpow-a-kafka-dashboard&quot;&gt;Is Kpow a Kafka dashboard?&lt;/h3&gt;
&lt;p&gt;Yes. Kpow is a Kafka dashboard built by Factor House. It provides real-time visibility into broker health, consumer lag, topic and partition state, schema registry, Kafka Streams, and Kafka Connect, all from a single UI deployed within your own infrastructure.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka management console: what to look for in a tool</title><link>https://factorhouse.io/articles/kafka-management-console/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-management-console/</guid><description>A Kafka management console gives your team full control of topics, consumers, schemas, and connectors from one UI. See what to look for and how Kpow delivers it.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Operating &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; through the CLI works at small scale, but it creates problems as teams and environments grow. A Kafka management console solves that by centralising administrative operations behind a web interface that any team member can use without needing direct broker access. This article defines what a capable console should cover, maps the current tool landscape, and explains what separates a production-ready console from a basic monitoring UI.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-kafka-management-console&quot;&gt;What is a Kafka management console?&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;Kafka management&lt;/a&gt; console is a web-based interface for operating and administering an Apache Kafka environment. It provides controls for topics, consumer groups, schemas, connectors, and access management, replacing CLI-based operations with a UI that can be safely delegated to team members without direct broker access.&lt;/p&gt;
&lt;p&gt;The distinction worth drawing is between a console and a read-only monitoring tool. A monitoring tool shows you what is happening: metrics, consumer lag, throughput, broker health. A management console lets you act on it. You can create or reconfigure a topic, reset a consumer group offset, deploy a connector, or revoke a user’s access, all from the same interface, without touching the CLI or exposing broker credentials.&lt;/p&gt;
&lt;p&gt;The Kafka CLI covers the same administrative operations, but it requires shell access, direct cluster connectivity, and enough familiarity with the tooling to avoid mistakes under pressure. A console delegates those operations through a controlled interface. That separation matters when multiple teams need access to a shared cluster, when your compliance requirements demand an audit trail, or when you want to avoid the kind of offset reset that takes the wrong consumer group offline.&lt;/p&gt;
&lt;p&gt;The rest of this article defines what a production-grade console should cover and how to evaluate the options available.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe9961b0c8180a1e940601_kpow-cluster-management.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;An example Kafka management console - Kpow by Factor House&lt;/p&gt;
&lt;h2 id=&quot;who-needs-a-kafka-management-console&quot;&gt;Who needs a Kafka management console&lt;/h2&gt;
&lt;p&gt;A command-line workflow is reasonable for a single engineer managing one cluster. It becomes a liability at most other scales.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Platform and infrastructure teams&lt;/strong&gt; responsible for multiple environments face a coordination problem. Running separate CLI tooling per cluster, with per-environment credentials, is slow and error-prone. A multi-cluster console reduces that to a single interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineering teams in regulated industries&lt;/strong&gt; (financial services, healthcare, payments) often have non-negotiable requirements around access control and audit trails. The Kafka CLI provides neither. A console that implements &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC&lt;/a&gt; and immutable audit logging meets those requirements in a way that CLI workflows cannot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Organisations scaling beyond a single cluster&lt;/strong&gt; hit a point where ad-hoc CLI operations create coordination risk. When multiple teams are creating topics, managing consumer groups, or deploying connectors on the same cluster, a console with RBAC prevents one team’s operations from affecting another’s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Teams running Kafka Connect or Kafka Streams at scale&lt;/strong&gt; find CLI-only connector management particularly fragile under time pressure. Pausing a connector, checking task health, or restarting a failed task through direct REST API calls is workable in isolation but introduces risk in shared environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Teams that need to delegate Kafka access safely,&lt;/strong&gt; giving topic owners limited visibility and control without sharing admin credentials, need RBAC at the console level, not just at the Kafka ACL level.&lt;/p&gt;
&lt;h2 id=&quot;core-management-capabilities&quot;&gt;Core management capabilities&lt;/h2&gt;
&lt;p&gt;The table below is a reference checklist for evaluating any Kafka management console. Not every tool covers all of these; some focus on monitoring only, some cover a subset of management operations, and a smaller number support the full stack including Connect, Streams, RBAC, and audit logging.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;What it enables&lt;/th&gt;
&lt;th&gt;Why it matters in production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Topic management&lt;/td&gt;
&lt;td&gt;Create, configure, inspect, and delete topics from the UI&lt;/td&gt;
&lt;td&gt;Eliminates direct CLI access for routine operations; reduces misconfiguration risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer group control&lt;/td&gt;
&lt;td&gt;View member state, partition assignment, and reset offsets&lt;/td&gt;
&lt;td&gt;Essential for recovery workflows; dangerous to perform ad hoc from the CLI under load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition reassignment&lt;/td&gt;
&lt;td&gt;Rebalance partitions across brokers&lt;/td&gt;
&lt;td&gt;Required after scaling or rack changes; complex to do manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema Registry integration&lt;/td&gt;
&lt;td&gt;Manage Avro, Protobuf, and JSON schemas with compatibility checks&lt;/td&gt;
&lt;td&gt;Prevents schema breaks in production pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Connect management&lt;/td&gt;
&lt;td&gt;Deploy, configure, pause, and restart connectors&lt;/td&gt;
&lt;td&gt;Removes CLI dependency for connector operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Streams inspection&lt;/td&gt;
&lt;td&gt;View running topologies and task health&lt;/td&gt;
&lt;td&gt;Surfaces lag and failure states that do not appear in standard broker metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data inspect&lt;/td&gt;
&lt;td&gt;Browse, filter, and deserialise messages in the console&lt;/td&gt;
&lt;td&gt;Critical for debugging; saves building bespoke consumer scripts for one-off investigations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RBAC&lt;/td&gt;
&lt;td&gt;Role-based access control scoped to cluster, topic, or consumer group&lt;/td&gt;
&lt;td&gt;Enables safe delegation without sharing admin credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit logging&lt;/td&gt;
&lt;td&gt;Immutable record of all console actions&lt;/td&gt;
&lt;td&gt;Required for regulated industries; invaluable for incident investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-cluster support&lt;/td&gt;
&lt;td&gt;Operate multiple environments from one interface&lt;/td&gt;
&lt;td&gt;Reduces context-switching and avoids separate tooling per environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Coverage varies considerably across tools. The sections below map the current landscape before covering how Kpow addresses the full capability stack.&lt;/p&gt;
&lt;h2 id=&quot;open-source-vs-commercial-kafka-management-consoles&quot;&gt;Open-source vs commercial Kafka management consoles&lt;/h2&gt;
&lt;p&gt;Several open-source tools cover the basics well. Commercial tools add security features, enterprise support, and multi-cluster capabilities that become relevant as requirements grow.&lt;/p&gt;
&lt;h3 id=&quot;the-main-open-source-options&quot;&gt;The main open-source options&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/articles/akhq&quot;&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/a&gt; (formerly KafkaHQ) is the most widely deployed open-source Kafka management UI. It covers topic management, consumer group inspection, Schema Registry, and Kafka Connect. The project is actively maintained under the Apache 2.0 licence, with v0.26.0 released in March 2026. AKHQ has no paid tier and no commercial support offering. RBAC is limited compared to commercial tools.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/kafbat-ui&quot;&gt;&lt;strong&gt;Kafbat&lt;/strong&gt;&lt;/a&gt; is the community-maintained continuation of Provectus kafka-ui, which Provectus paused in September 2023. Kafbat is actively developed: version 1.5.0 (released in 2025) added live consumer lag updates, MessagePack serialisation support, and CSV export. RBAC is available through YAML-based configuration, with OAuth2 (Google, GitHub, Azure AD), LDAP, and Active Directory support. Notable gaps include no per-role data masking, no approval workflows, and no policy enforcement layer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/resources/integrate-kpow-with-redpanda&quot;&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt;&lt;/a&gt; has an open-source edition that covers topic management, consumer group inspection, and basic data browsing. In the open-source tier, RBAC and SSO are not available; those are enterprise-only features. The open-source edition is a reasonable choice for development environments or teams using Redpanda itself.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/cmak&quot;&gt;&lt;strong&gt;CMAK&lt;/strong&gt;&lt;/a&gt; (Cluster Manager for Apache Kafka, originally Kafka Manager from Yahoo) is no longer actively maintained. The last release was 3.0.0.6 in April 2022. More critically, CMAK requires a ZooKeeper endpoint and has no KRaft support, making it incompatible with Kafka 4.0 and later. It is not a viable choice for new deployments.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;AKHQ, a popular open-source Kafka management console&lt;/p&gt;
&lt;h3 id=&quot;what-open-source-tools-do-well-and-where-they-fall-short&quot;&gt;What open-source tools do well and where they fall short&lt;/h3&gt;
&lt;p&gt;Open-source tools handle the core daily operations (topic creation, consumer group inspection, basic message browsing) without significant gaps. For a single-cluster development environment or a small team with no compliance requirements, AKHQ or Kafbat is a defensible choice.&lt;/p&gt;
&lt;p&gt;The gaps appear at the edges that matter in production: RBAC is either absent, limited, or requires significant configuration work; audit logging is typically not provided; multi-cluster support is limited or absent; and commercial support is not available. If access control, audit trails, and multi-cluster management are requirements rather than nice-to-haves, open-source tools require substantial additional work to meet them.&lt;/p&gt;
&lt;h3 id=&quot;what-commercial-tools-add&quot;&gt;What commercial tools add&lt;/h3&gt;
&lt;p&gt;Commercial tools such as &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;, &lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt;, and &lt;a href=&quot;/articles/lenses&quot;&gt;Lenses.io&lt;/a&gt; typically include RBAC as a first-class feature, immutable audit logging, multi-cluster management from a single interface, and commercial support contracts. The trade-off is licence cost and the added consideration of running a closed-source tool in your infrastructure.&lt;/p&gt;
&lt;p&gt;For regulated environments, or for platform teams managing more than one or two clusters with multiple teams sharing access, commercial tooling generally reduces the operational burden significantly compared to assembling equivalent capabilities from open-source tools.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-covers-the-full-capability-stack&quot;&gt;How Kpow covers the full capability stack&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a commercial Kafka management console built by Factor House. It covers the full capability table above: topic management, consumer group control, Schema Registry, Kafka Connect, Kafka Streams, data inspect, RBAC, audit logging, and multi-cluster support, all from a single interface.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ff681d840e0f91468fd5b0_kpow-data-inspect.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Data inspection in Kpow&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment model.&lt;/strong&gt; Kpow deploys inside your own infrastructure. It runs as a Docker container, a Helm chart on Kubernetes, a JAR, via AWS CloudFormation on ECS, or from the AWS Marketplace. No data leaves your environment: agents connect directly to your brokers, Schema Registry, and Connect clusters. There are no external dependencies and no inbound connections required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-cluster management.&lt;/strong&gt; A single Kpow instance can manage multiple Kafka clusters, Schema Registries, and Connect clusters. This is useful for teams operating separate environments (production, staging, development) or multiple independent clusters, where switching between separate tools per cluster creates overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RBAC and audit logging.&lt;/strong&gt; RBAC in Kpow is scoped at the cluster, topic, or consumer group level: you can give a team read access to one topic and write access to another without granting broader permissions. Audit logging records every action taken through the console with a timestamp and user identity. Both are first-class features, not optional add-ons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data inspect.&lt;/strong&gt; Kpow’s &lt;a href=&quot;/articles/foundational-kafka-data-inspection-in-kpow&quot;&gt;data inspect&lt;/a&gt; supports Avro, Protobuf, JSON, and custom deserializers. Operators can browse, filter, and deserialise messages without exposing raw broker access, which is important in environments where message contents are sensitive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Streams support.&lt;/strong&gt; Kpow surfaces running topologies and task health for Kafka Streams applications, including information that does not appear in standard broker metrics and is difficult to access without dedicated tooling.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is a Kafka management console?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A Kafka management console is a web-based interface for operating and administering an Apache Kafka environment. It provides controls for topics, consumer groups, schemas, connectors, and access management, replacing CLI-based operations with a UI that can be safely delegated to team members without direct broker access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What’s the difference between a Kafka management console and a Kafka monitoring tool?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A monitoring tool shows you what is happening: metrics, lag, throughput. A management console lets you act on it: create or modify topics, reset consumer group offsets, manage connectors, and control access. Many tools do both, but the distinction matters when evaluating whether a tool is read-only or operational.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I manage Kafka without using the CLI?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes. A Kafka management console exposes the same administrative operations as the Kafka CLI (topic creation, consumer group management, offset resets, partition reassignment) through a web UI. Most consoles also add RBAC so operators can perform these actions without needing direct shell or broker access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What are the best open-source Kafka management consoles?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The most widely used open-source options are AKHQ and Kafbat (a maintained fork of Provectus kafka-ui). Redpanda Console has an open-source edition suited to Redpanda deployments. CMAK is no longer actively maintained and is incompatible with Kafka 4.0 due to its ZooKeeper dependency. AKHQ and Kafbat are the most defensible choices for standard Apache Kafka deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is Kafka RBAC and does a management console support it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC&lt;/a&gt; (role-based access control) lets you define what each user or team can do: view topics, reset offsets, manage connectors, scoped to specific clusters or resources. Not all consoles implement RBAC; those that do typically layer it on top of Kafka’s native ACLs. Kpow implements RBAC at the console level, scoped to cluster, topic, or consumer group.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I reset consumer group offsets without the CLI?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A management console with consumer group control lets you select a group, choose a topic and partition, and reset the offset to a specific position: earliest, latest, a timestamp, or a specific offset value. This avoids running &lt;code&gt;kafka-consumer-groups.sh&lt;/code&gt; directly and reduces the risk of resetting the wrong group or partition under pressure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I manage multiple Kafka clusters from one interface?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes, if the console supports multi-cluster management. Kpow, Conduktor, and Lenses.io all support multiple clusters from a single interface. Most open-source tools are single-cluster by default, though some support switching between configured environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is Kafka audit logging and why does it matter?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Audit logging in a Kafka management console records every action taken, including who created or deleted a topic, who reset a consumer group, and who changed a connector, along with a timestamp and user identity. This is required in regulated industries for compliance and is invaluable for diagnosing production incidents caused by administrative changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I manage Kafka Connect from a console?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A console with Connect support lets you deploy, configure, pause, restart, and delete connectors through the UI, and view connector status and task health. This replaces direct calls to the Connect REST API and is safer in shared environments where multiple teams manage connectors on the same cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What’s the difference between Kpow, Conduktor, and AKHQ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kpow and Conduktor are commercial tools with enterprise features: RBAC, audit logging, multi-cluster support, and commercial support contracts. Compared to Conduktor, Kpow does not require a proxy in the data path. AKHQ is open-source and widely used for topic and consumer group management but has more limited RBAC and no audit logging capabilities. The right choice depends on team size, compliance requirements, and whether commercial support matters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is there a free Kafka management console?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes. AKHQ and Kafbat are free and open-source. Redpanda Console has a free open-source edition. Kpow has a Community Edition and an Enterprise trial. For development and small team use, open-source tools are generally sufficient; commercial options are more common in regulated or large-scale production environments where RBAC and audit logging are requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I inspect Kafka messages without writing a consumer?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most management consoles include a data inspect or message browser feature. You select a topic, optionally filter by key, header, or value, choose a time range or offset, and the console deserialises and displays messages, supporting Avro, Protobuf, JSON, and sometimes custom deserializers. This replaces ad-hoc consumer scripts for debugging.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-kpow&quot;&gt;Getting started with Kpow&lt;/h2&gt;
&lt;p&gt;To get started with Kpow you need a running Kafka cluster (self-managed, Amazon MSK, Confluent Cloud, Redpanda, or another Kafka-compatible platform) and a container runtime or JVM environment.&lt;/p&gt;
&lt;p&gt;Kpow deploys as a single container or JAR. Configuration is environment-variable based: you point Kpow at your bootstrap servers, and optionally at your Schema Registry and Connect clusters, and it connects directly without any intermediate services or agents. A typical install takes under 30 minutes from first pull to a running console.&lt;/p&gt;
&lt;p&gt;Deployment options include Docker, Docker Compose for local development, Helm for Kubernetes, AWS CloudFormation for ECS, and the AWS Marketplace. The same artefact covers all environments.&lt;/p&gt;
&lt;p&gt;Kpow is maintained by Factor House with commercial support available for Enterprise customers.&lt;/p&gt;
&lt;p&gt;Give Kpow a try with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day Enterprise trial&lt;/a&gt;. You can connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR. No credit card is required for the Community Edition.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka message key best practices</title><link>https://factorhouse.io/articles/kafka-message-key-best-practices/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-message-key-best-practices/</guid><description>A technical guide to Kafka message key best practices covering partitioning, ordering guarantees, hot keys, log compaction, and serialization for production systems.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-is-a-kafka-message-key&quot;&gt;What is a Kafka message key?&lt;/h2&gt;
&lt;p&gt;A Kafka message key is an optional byte array attached to every record produced to a topic. The producer sets it independently from the message value, and the broker never inspects its contents. In the Java client, the key is serialized via &lt;code&gt;key.serializer&lt;/code&gt;, a separate configuration from &lt;code&gt;value.serializer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The key serves exactly two purposes in Kafka:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Routing.&lt;/strong&gt; When no explicit partition is specified, the producer’s &lt;code&gt;Partitioner&lt;/code&gt; decides which partition a record goes to. With the default Java partitioner, the rule is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;partition = Math.abs(Utils.murmur2(keyBytes)) % numPartitions&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is deterministic: the same key always maps to the same partition, for as long as &lt;code&gt;numPartitions&lt;/code&gt; stays constant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compaction identity.&lt;/strong&gt; For topics with &lt;code&gt;cleanup.policy=compact&lt;/code&gt;, the key is the primary key against which older values are garbage-collected. Kafka retains only the latest value per key.&lt;/p&gt;
&lt;p&gt;The key plays no role in authentication, deduplication, or consumer-side indexing. Consumers can ignore the key entirely if their use case doesn’t require it.&lt;/p&gt;
&lt;h2 id=&quot;kafka-message-structure&quot;&gt;Kafka message structure&lt;/h2&gt;
&lt;p&gt;A Kafka record consists of four producer-supplied fields and three broker-assigned fields:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Set by&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Key&lt;/td&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Arbitrary bytes. Drives partition routing and compaction.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value&lt;/td&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Arbitrary bytes. The payload.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headers&lt;/td&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Optional key-value metadata. Useful for routing metadata, correlation IDs, schema version hints.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamp&lt;/td&gt;
&lt;td&gt;Producer / Broker&lt;/td&gt;
&lt;td&gt;Either CreateTime (producer clock) or LogAppendTime (broker clock), depending on message.timestamp.type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic&lt;/td&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;The topic the record was written to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;The partition within the topic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offset&lt;/td&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;The monotonically increasing position within the partition.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Kafka’s only ordering guarantee is per-partition. Records with the same key land on the same partition and are therefore ordered relative to each other. Records with different keys may land on different partitions, and Kafka makes no ordering promises across partitions.&lt;/p&gt;
&lt;p&gt;For common key serializer choices: &lt;code&gt;StringSerializer&lt;/code&gt;, &lt;code&gt;LongSerializer&lt;/code&gt;, and &lt;code&gt;UUIDSerializer&lt;/code&gt; cover the vast majority of production use cases and produce keys that are readable in CLI tooling such as &lt;code&gt;kafka-console-consumer --print.key&lt;/code&gt;. Schema Registry serializers (&lt;code&gt;KafkaAvroSerializer&lt;/code&gt;, &lt;code&gt;KafkaProtobufSerializer&lt;/code&gt;) work for keys, but most teams use them only on the value and keep keys as primitive types for human-readable inspection.&lt;/p&gt;
&lt;h2 id=&quot;kafka-message-key-best-practices&quot;&gt;Kafka message key best practices&lt;/h2&gt;
&lt;h3 id=&quot;1-use-a-key-when-you-need-per-entity-ordering&quot;&gt;1. Use a key when you need per-entity ordering&lt;/h3&gt;
&lt;p&gt;Kafka guarantees message order within a partition. If your consumers need events for a specific entity to arrive in the order they were produced, that entity’s identifier must be the key.&lt;/p&gt;
&lt;p&gt;Common examples: &lt;code&gt;accountId&lt;/code&gt; for banking transactions (credits and debits must apply in sequence), &lt;code&gt;orderId&lt;/code&gt; for e-commerce state transitions, &lt;code&gt;driverId&lt;/code&gt; or &lt;code&gt;deliveryId&lt;/code&gt; for ride-hailing status updates, &lt;code&gt;deviceId&lt;/code&gt; for IoT telemetry. In each case, the ordering boundary is the entity, and the key must reflect that boundary precisely. Choosing a coarser key, such as &lt;code&gt;region&lt;/code&gt; or &lt;code&gt;status&lt;/code&gt;, groups unrelated entities onto the same partition without providing the ordering guarantee your consumers actually need.&lt;/p&gt;
&lt;h3 id=&quot;2-use-null-keys-deliberately-when-ordering-does-not-matter&quot;&gt;2. Use null keys deliberately when ordering does not matter&lt;/h3&gt;
&lt;p&gt;For log ingestion, metrics pipelines, telemetry, and other append-only workloads where no entity-level ordering is required, producing with a null key is the correct choice.&lt;/p&gt;
&lt;p&gt;With null keys, the producer’s partitioner handles distribution. Since Kafka 3.3, the default adaptive partitioner (KIP-794) batches records to a partition based on bytes produced rather than time, and actively routes away from slow brokers. Confluent’s own benchmarks for the KIP-794 partitioner reported p99 latency dropping from roughly 11 seconds to 154 milliseconds under an abnormal-broker scenario.&lt;/p&gt;
&lt;p&gt;If you are still on Kafka 2.4-3.2, the sticky partitioner (KIP-480) handles null keys by batching to one partition until &lt;code&gt;linger.ms&lt;/code&gt; elapses or the batch fills, then rotating. DoorDash reported tuning &lt;code&gt;linger.ms&lt;/code&gt; to 50-100 ms reduced broker CPU by 30-40% on their high-volume null-keyed event ingestion pipeline.&lt;/p&gt;
&lt;p&gt;The risky case is accidentally null keys on a topic that requires ordering. A producer that fails to set a key on every record will exhibit intermittent ordering failures that are difficult to reproduce. Add a producer interceptor or unit test that rejects null keys for any topic where per-entity ordering is a requirement.&lt;/p&gt;
&lt;h3 id=&quot;3-choose-high-cardinality-evenly-distributed-keys&quot;&gt;3. Choose high-cardinality, evenly distributed keys&lt;/h3&gt;
&lt;p&gt;The murmur2 hash distributes keys across partitions, but only as evenly as the key’s own distribution allows. A low-cardinality key will leave most partitions idle and concentrate traffic on a few.&lt;/p&gt;
&lt;p&gt;Good key candidates: &lt;code&gt;userId&lt;/code&gt;, &lt;code&gt;orderId&lt;/code&gt;, &lt;code&gt;deviceId&lt;/code&gt;, &lt;code&gt;sessionId&lt;/code&gt;, &lt;code&gt;accountId&lt;/code&gt;, &lt;code&gt;transactionId&lt;/code&gt;. These typically have many distinct values and no single dominant value.&lt;/p&gt;
&lt;p&gt;Poor key candidates: &lt;code&gt;country&lt;/code&gt;, &lt;code&gt;region&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;eventType&lt;/code&gt;, boolean flags. With &lt;code&gt;status ∈ {SUCCESS, FAILED}&lt;/code&gt;, only two partitions ever receive traffic regardless of how many partitions the topic has.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tenantId&lt;/code&gt; is a common intermediate case. It may appear high-cardinality, but most B2B systems have a power-law distribution of tenant size. One large tenant can produce the majority of your traffic. If you need tenant-level grouping, combine &lt;code&gt;tenantId&lt;/code&gt; with a finer-grained field using a composite key.&lt;/p&gt;
&lt;h3 id=&quot;4-always-key-by-an-immutable-identifier&quot;&gt;4. Always key by an immutable identifier&lt;/h3&gt;
&lt;p&gt;A key that changes over time breaks ordering and creates orphan records in compacted topics. The most common mistake is using a mutable field like &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, or &lt;code&gt;status&lt;/code&gt; as the key.&lt;/p&gt;
&lt;p&gt;If a user changes their username, every subsequent event for that user lands on a different partition from all historical events. Any downstream consumer joining on the key now sees two different histories for the same logical entity. In a compacted topic, the compactor treats the old and new keys as separate records, keeping both as the “current state” indefinitely.&lt;/p&gt;
&lt;p&gt;Always use a surrogate, immutable identifier: &lt;code&gt;userId&lt;/code&gt; rather than &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;accountNumber&lt;/code&gt; rather than &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;deviceId&lt;/code&gt; rather than &lt;code&gt;hostname&lt;/code&gt;. If the natural identifier in your domain is mutable, generate an internal UUID at entity creation time and use that as the key.&lt;/p&gt;
&lt;h3 id=&quot;5-keys-are-required-for-log-compaction&quot;&gt;5. Keys are required for log compaction&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cleanup.policy=compact&lt;/code&gt; instructs Kafka to retain only the latest record for each key. This makes a compacted topic behave like a distributed key-value store of current state. It is the foundation of Kafka Streams’ changelog topics, CDC streams, and any event-sourced projection that needs to rebuild state from scratch.&lt;/p&gt;
&lt;p&gt;A null-keyed record on a compacted topic cannot be compacted. It sits in the log indefinitely, causing unbounded disk growth. Confluent’s documentation is explicit on this point: compacted topics require records with keys.&lt;/p&gt;
&lt;p&gt;A null value paired with a non-null key is a tombstone. It signals the compactor to delete that key once &lt;code&gt;delete.retention.ms&lt;/code&gt; elapses. If you need to “delete” a record from a compacted topic, produce a tombstone with the same key.&lt;/p&gt;
&lt;p&gt;Add a producer-side interceptor that rejects null keys for any topic with &lt;code&gt;cleanup.policy=compact&lt;/code&gt;. Catching this in the producer is far cheaper than diagnosing unbounded disk growth in production.&lt;/p&gt;
&lt;h3 id=&quot;6-align-keys-for-stateful-stream-processing&quot;&gt;6. Align keys for stateful stream processing&lt;/h3&gt;
&lt;p&gt;Kafka Streams, ksqlDB, and Flink all require co-partitioning for joins and aggregations. Co-partitioning means: the same number of partitions on both sides, the same partitioning strategy, and the same key type.&lt;/p&gt;
&lt;p&gt;Kafka Streams enforces this at startup and will throw &lt;code&gt;TopologyException: Topics not co-partitioned&lt;/code&gt; if partition counts differ across joined topics. ksqlDB enforces it at query creation time.&lt;/p&gt;
&lt;p&gt;If you cannot co-partition the source topics, the options are: insert an explicit &lt;code&gt;KStream#repartition()&lt;/code&gt; step (Kafka 2.6+ via KIP-221) to repartition on the fly, or use &lt;code&gt;GlobalKTable&lt;/code&gt; for the smaller side of a join (which broadcasts the entire topic to every instance).&lt;/p&gt;
&lt;p&gt;When multiple producer applications write to the same topic, they must all use the same partitioner. The Java default client uses murmur2. The librdkafka client (used by Python, Go, .NET, and C# producers) defaults to CRC32 with random assignment for null keys. Two producers using different hash functions and writing the same logical key will route to different partitions, silently breaking co-partitioning and any downstream join. Set &lt;code&gt;partitioner=Murmur2Random&lt;/code&gt; on all librdkafka clients that share a topic with a Java producer.&lt;/p&gt;
&lt;h3 id=&quot;7-standardize-key-serializers-across-producer-teams&quot;&gt;7. Standardize key serializers across producer teams&lt;/h3&gt;
&lt;p&gt;A producer using &lt;code&gt;IntegerSerializer&lt;/code&gt; and a consumer using &lt;code&gt;StringDeserializer&lt;/code&gt; will deserialize silently incorrect data, potentially without throwing an exception. The failure typically surfaces as incorrect behavior in a downstream state store or join.&lt;/p&gt;
&lt;p&gt;Define the key serializer in a shared schema or data contract alongside the value schema. For topics consumed by Kafka Streams or ksqlDB, include the key type in the topic metadata. Some teams track this in Confluent Schema Registry using &lt;code&gt;RecordNameStrategy&lt;/code&gt; for the key subject; others maintain a topic catalog alongside their schema definitions. Whichever mechanism you use, the key type should be as explicit and controlled as the value schema.&lt;/p&gt;
&lt;h3 id=&quot;8-plan-for-hot-keys-before-you-need-to&quot;&gt;8. Plan for hot keys before you need to&lt;/h3&gt;
&lt;p&gt;Every production Kafka system at sufficient scale encounters key skew. New Relic’s events pipeline team found that 1.5% of their query keys produced 90% of the events processed for aggregation. Slack observed per-broker hot-spotting persistent enough to mandate that every topic’s partition count be a multiple of the broker count.&lt;/p&gt;
&lt;p&gt;The diagnostic approach: use &lt;code&gt;kafka-consumer-groups.sh --describe&lt;/code&gt; and look for a single partition with 5-10x the consumer lag of its neighbours. Even disk usage across brokers but uneven consumer lag typically points to a processing bottleneck rather than key skew. Confirm by sampling the top keys by message rate for an hour. If the top 1% of keys produce more than 20% of traffic, you have a skew problem.&lt;/p&gt;
&lt;p&gt;Mitigations, in order of preference:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Composite keys&lt;/strong&gt; combine a high-cardinality field with a coarser one for locality: &lt;code&gt;tenantId|userId&lt;/code&gt; or &lt;code&gt;region|customerId&lt;/code&gt;. This preserves logical grouping while spreading load across more hash buckets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Salting&lt;/strong&gt; appends a deterministic random suffix to known hot keys: &lt;code&gt;userId#0&lt;/code&gt; through &lt;code&gt;userId#K-1&lt;/code&gt;. Per-user ordering is lost, but per-(user, salt) ordering is preserved. Consumers that need a final per-user aggregation re-key downstream on &lt;code&gt;userId&lt;/code&gt; only. This pattern is used by Pinterest’s MemQ system for storage-layer hot key mitigation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time-window bucketing&lt;/strong&gt; appends a time bucket: &lt;code&gt;userId|2025-10-21T10:00&lt;/code&gt;. This works for hotness that is bursty rather than sustained.&lt;/p&gt;
&lt;p&gt;Do not respond to consumer lag by immediately adding partitions. That breaks the hash mapping and the ordering guarantee for keyed topics, as described in point 9 below.&lt;/p&gt;
&lt;h3 id=&quot;9-over-provision-partitions-at-topic-creation&quot;&gt;9. Over-provision partitions at topic creation&lt;/h3&gt;
&lt;p&gt;Adding partitions to an existing topic changes the murmur2 modulus. Records for &lt;code&gt;customer-42&lt;/code&gt; produced before the change and records produced after may land on different partitions. Per-key ordering is broken from that point forward, with no automated warning to downstream consumers.&lt;/p&gt;
&lt;p&gt;Confluent’s operations documentation is explicit: “If the number of partitions changes, this delivery guarantee may no longer hold.”&lt;/p&gt;
&lt;p&gt;Choose your initial partition count based on the consumer parallelism you expect 12-18 months out, then over-provision beyond that by a factor of 2-3. For small clusters, Slack’s rule is useful: make partition counts a multiple of the broker count to distribute partition leadership evenly. For large clusters, anything under 100 partitions per topic starts to produce uneven load distribution.&lt;/p&gt;
&lt;p&gt;If you must add partitions, do it during a low-traffic window, document that ordering is broken at the cutover point, and validate that downstream Kafka Streams topologies do not assume positional ordering across the partition count boundary.&lt;/p&gt;
&lt;h3 id=&quot;10-rekey-via-a-new-topic-never-in-place&quot;&gt;10. Rekey via a new topic, never in-place&lt;/h3&gt;
&lt;p&gt;If the key on an existing topic needs to change, the safe pattern is to create a new topic with the desired key and partition count, run a rekeying pipeline to migrate data, and then cut consumers over once they have caught up.&lt;/p&gt;
&lt;p&gt;The steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the new topic (&lt;code&gt;orders.v2&lt;/code&gt;) with the target partition count and key scheme.&lt;/li&gt;
&lt;li&gt;Run a rekeying consumer: &lt;code&gt;sourceStream.selectKey(...).to(&quot;orders.v2&quot;)&lt;/code&gt; in Kafka Streams, or an equivalent Flink or custom consumer.&lt;/li&gt;
&lt;li&gt;Dual-write from producers to both topics during the migration window.&lt;/li&gt;
&lt;li&gt;Switch consumers to the new topic once consumer lag on &lt;code&gt;orders.v2&lt;/code&gt; reaches zero.&lt;/li&gt;
&lt;li&gt;Drain and delete the old topic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rewriting keys into the same topic is not a safe operation. It interleaves records under two different key schemes in the same partition, making it impossible for any consumer to maintain a consistent ordering guarantee.&lt;/p&gt;
&lt;h3 id=&quot;11-set-idempotent-producer-configuration&quot;&gt;11. Set idempotent producer configuration&lt;/h3&gt;
&lt;p&gt;Producer configuration affects ordering guarantees independently of the key. Without an idempotent producer, setting &lt;code&gt;retries &amp;gt; 0&lt;/code&gt; and &lt;code&gt;max.in.flight.requests.per.connection &amp;gt; 1&lt;/code&gt; allows retries to reorder records within a partition even when the key is correct.&lt;/p&gt;
&lt;p&gt;The recommended production configuration:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;enable.idempotence=true   acks=all   max.in.flight.requests.per.connection=5   retries=2147483647&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;enable.idempotence=true&lt;/code&gt; has been the default since Kafka 3.0. The broker tracks a producer ID and sequence number per partition and deduplicates retries automatically. If you are running a producer configuration inherited from before Kafka 3.0, audit it before assuming ordering is safe.&lt;/p&gt;
&lt;h3 id=&quot;12-document-key-semantics-alongside-the-schema&quot;&gt;12. Document key semantics alongside the schema&lt;/h3&gt;
&lt;p&gt;The key type, cardinality expectations, and ordering guarantees are not inferable from the schema alone. A topic named &lt;code&gt;user-events&lt;/code&gt; with a &lt;code&gt;userId&lt;/code&gt; key does not communicate whether records are ordered per-user, whether the topic is compacted, or whether multiple producer applications write to it.&lt;/p&gt;
&lt;p&gt;At minimum, document: the key type and serializer, what the key represents, whether per-key ordering is guaranteed, and whether the topic is compacted. Teams using Confluent Platform have Topic Tags and data contract features for this. If you are managing Kafka independently, a README alongside your schema definitions or an entry in your internal data catalog is sufficient.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-with-kafka-message-key-management&quot;&gt;How Kpow helps with Kafka message key management&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides observability and management capabilities that are useful at several points in the key lifecycle described above.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ff681d840e0f91468fd5b0_kpow-data-inspect.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inspecting key content and distribution.&lt;/strong&gt; Kpow’s Data Inspect feature allows you to search topic records by key directly. The Key mode queries records from one or more topics, starting from a configured point in time, and matches on an exact key value. This is useful for confirming that a key is routing to the expected partition, checking that a compacted topic is not accumulating null-keyed records, or debugging serialization mismatches. Auto SerDes mode will attempt to infer the key and value deserialization format automatically, which reduces the setup overhead when you are inspecting an unfamiliar topic for the first time.&lt;/p&gt;
&lt;p&gt;kJQ filters allow server-side filtering across message keys and values at high throughput, which is practical for profiling key distribution across large topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring consumer lag per partition.&lt;/strong&gt; Kpow’s consumer group monitoring shows lag at the partition level, which is the first place a hot key problem becomes visible. A single partition running significantly behind its peers is the diagnostic signal described in best practice 8. Kpow exposes this breakdown in the consumer group topology view and in the detailed consumer table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Managing offsets during rekeying operations.&lt;/strong&gt; When executing the new-topic rekeying pattern described in best practice 10, Kpow’s offset management tools let you reset or adjust consumer group offsets at the group, topic, or partition level, which can be necessary when validating that a cutover consumer has fully caught up before switching production traffic.&lt;/p&gt;
&lt;p&gt;Kpow connects to any Kafka cluster and deploys via Docker, Helm, or JAR. You can try it with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For guidance on managing message payload size alongside key strategy, see our &lt;a href=&quot;/articles/kafka-message-size-best-practice&quot;&gt;Kafka message size best practice&lt;/a&gt; guide.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka security architecture: best practices for production</title><link>https://factorhouse.io/articles/kafka-security-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-security-architecture/</guid><description>Kafka ships insecure by default. Learn how to build a production-ready Kafka security architecture covering TLS encryption, SASL authentication, ACLs, audit logging, and network isolation.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafka ships with authentication, encryption, and authorization disabled by default. Every security control must be explicitly configured.&lt;/li&gt;
&lt;li&gt;There are four distinct traffic flows in a Kafka deployment, each requiring its own security controls: client-to-broker, broker-to-broker, broker-to-controller, and admin clients.&lt;/li&gt;
&lt;li&gt;A layered security model covering network isolation, TLS, authentication, authorization, and audit logging is more reliable than any single control in isolation.&lt;/li&gt;
&lt;li&gt;ZooKeeper is deprecated as of Kafka 4.0. Clusters still running ZooKeeper mode will lose security patches in November 2025.&lt;/li&gt;
&lt;li&gt;Native ACLs become difficult to manage at scale. External authorizers (OPA, Ranger, or custom implementations) address the operational limits.&lt;/li&gt;
&lt;li&gt;The gap between “audit logging is enabled” and “audit logging is actionable” is wider than most teams expect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;security-challenges-unique-to-kafka&quot;&gt;Security challenges unique to Kafka&lt;/h2&gt;
&lt;p&gt;The most important thing to understand about Kafka security is the starting position: Kafka ships insecure by default. Authentication, encryption, and authorization are all off out of the box. An unconfigured Kafka cluster allows any application that can reach the network to produce, consume, and administer the cluster without restriction.&lt;/p&gt;
&lt;p&gt;This is documented behavior, not a bug. Kafka was designed for ease of use in development environments. The problem is that teams stand up a cluster for testing, skip hardening, and then find themselves with a production deployment built against open listeners.&lt;/p&gt;
&lt;p&gt;Beyond the defaults, Kafka’s attack surface is more complex than a conventional service with a single authentication boundary. There are four distinct traffic flows, each of which requires its own security controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client to broker&lt;/strong&gt;: Producers and consumers connecting to send or receive data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broker to broker&lt;/strong&gt;: Inter-broker replication traffic, which is the most commonly missed flow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broker to controller&lt;/strong&gt;: Control plane traffic managing cluster metadata, ACLs, and topic configurations (ZooKeeper in older deployments, KRaft controllers in current ones)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin clients&lt;/strong&gt;: CLI tools, management UIs, Kafka Connect workers, Schema Registry, all operating as principals against the cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can configure TLS and authentication perfectly for client connections and remain exposed through an unauthenticated ZooKeeper port or an admin UI with no access controls. Each flow needs to be accounted for independently.&lt;/p&gt;
&lt;p&gt;Kafka is also typically multi-tenant in practice. Multiple teams, services, and sometimes external partners share the same infrastructure. Every new producer-consumer relationship expands the authorization surface that needs to be maintained.&lt;/p&gt;
&lt;h2 id=&quot;the-four-pillars-of-kafka-security&quot;&gt;The four pillars of Kafka security&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Threat it addresses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;TLS&lt;/td&gt;
&lt;td&gt;Data intercepted in transit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;SASL / mTLS&lt;/td&gt;
&lt;td&gt;Unauthorized clients connecting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;ACLs / RBAC&lt;/td&gt;
&lt;td&gt;Legitimate clients overreaching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit logging&lt;/td&gt;
&lt;td&gt;kafka.authorizer.logger + external tooling&lt;/td&gt;
&lt;td&gt;Undetected breaches or misuse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;Each pillar addresses a distinct threat. Implementing three of four still leaves a meaningful gap: a cluster with strong authentication and fine-grained ACLs but no audit logging gives you no visibility into what happened when something goes wrong.&lt;/p&gt;
&lt;h2 id=&quot;encryption-securing-data-in-transit&quot;&gt;Encryption: securing data in transit&lt;/h2&gt;
&lt;h3 id=&quot;the-three-flows-you-need-to-encrypt&quot;&gt;The three flows you need to encrypt&lt;/h3&gt;
&lt;p&gt;Most teams focus on client-to-broker TLS and stop there. Two other flows are commonly overlooked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inter-broker replication&lt;/strong&gt; is the most frequently missed. Replication traffic carries all data transiting the cluster. Teams that configure TLS for client connections often leave inter-broker traffic on PLAINTEXT because it is “internal.” The relevant config property, &lt;code&gt;security.inter.broker.protocol&lt;/code&gt;, defaults to &lt;code&gt;PLAINTEXT&lt;/code&gt;. You need to explicitly set it to &lt;code&gt;SSL&lt;/code&gt; or &lt;code&gt;SASL_SSL&lt;/code&gt;. Anyone with network access between broker nodes can intercept replication traffic if this is not configured.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker-to-controller traffic&lt;/strong&gt; (either ZooKeeper or KRaft controller) is similarly often left unencrypted. Control plane traffic includes ACL definitions, topic configurations, and cluster membership data.&lt;/p&gt;
&lt;p&gt;A minimal broker configuration with inter-broker encryption enabled:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;listeners=SASL_SSL://public:9093,SSL://internal:9094   advertised.listeners=SASL_SSL://public.example.com:9093,SSL://internal.example.com:9094   listener.security.protocol.map=SASL_SSL:SASL_SSL,SSL:SSL   sasl.enabled.mechanisms=SCRAM-SHA-256   sasl.mechanism.inter.broker.protocol=SCRAM-SHA-256   ssl.keystore.location=/var/private/ssl/kafka.server.keystore.jks   ssl.keystore.password=[keystore-secret]   ssl.truststore.location=/var/private/ssl/kafka.server.truststore.jks   ssl.truststore.password=[truststore-secret]&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;tls-version&quot;&gt;TLS version&lt;/h3&gt;
&lt;p&gt;TLS 1.3 should be enforced for all production traffic. Older TLS versions have known vulnerabilities and should be explicitly disabled via &lt;code&gt;ssl.enabled.protocols&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;certificate-management&quot;&gt;Certificate management&lt;/h3&gt;
&lt;p&gt;Certificate rotation is where TLS implementations tend to break down in practice. Teams configure TLS, confirm it is working, and then do not rotate certificates until something expires in production. Automation is the correct approach: certificate rotation should be handled by your PKI infrastructure on a schedule, not manually triggered when something breaks. Uber’s internal PKI (uPKI) pushes new X.509 key-cert pairs to brokers and clients before TTLs expire; that pattern is worth replicating regardless of the tooling you use.&lt;/p&gt;
&lt;h3 id=&quot;encryption-at-rest&quot;&gt;Encryption at rest&lt;/h3&gt;
&lt;p&gt;Kafka does not provide built-in encryption at rest. Data stored on broker disks is protected only at the infrastructure layer: volume encryption (AWS EBS, GCP Persistent Disk) or OS-level encryption. In environments requiring stronger guarantees, some teams encrypt message payloads before they reach the broker, so that the broker itself cannot read the data. This approach adds key management and downstream decryption complexity, but it is the appropriate model for zero-trust environments handling sensitive data.&lt;/p&gt;
&lt;h2 id=&quot;authentication-proving-identity&quot;&gt;Authentication: proving identity&lt;/h2&gt;
&lt;p&gt;Kafka supports four SASL mechanisms and mTLS as an alternative authentication model. The right choice depends on your environment and existing identity infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;sasl-mechanisms-compared&quot;&gt;SASL mechanisms compared&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;SASL/PLAIN&lt;/strong&gt; sends credentials in cleartext. It is only acceptable over an encrypted connection (&lt;code&gt;SASL_SSL&lt;/code&gt;, not &lt;code&gt;SASL_PLAINTEXT&lt;/code&gt;). A common misconfiguration is using PLAIN over an unencrypted listener, which exposes usernames and passwords on the wire. Restrict PLAIN to development environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SASL/SCRAM&lt;/strong&gt; (SHA-256 or SHA-512) uses a challenge-response protocol that does not transmit passwords in cleartext. Credentials are stored in ZooKeeper (ZK mode) or cluster metadata (KRaft). SCRAM-SHA-256 is the minimum for production password-based authentication; SCRAM-SHA-512 is stronger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SASL/GSSAPI (Kerberos)&lt;/strong&gt; integrates with enterprise identity systems via Active Directory or LDAP. It provides SSO across the organization and strong authentication, at the cost of operational complexity: Kerberos infrastructure, ticket renewal management, and keytab files. The right choice for large on-premises enterprises already running Kerberos. Less practical for cloud-native environments or teams without existing Kerberos investment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SASL/OAUTHBEARER&lt;/strong&gt; uses OAuth 2.0 / OpenID Connect tokens. As of 2025 this is the standard authentication mechanism for cloud-native Kafka deployments. It integrates with Okta, Keycloak, Entra ID, and similar identity providers. Short-lived tokens reduce the credential exposure window compared to long-lived passwords, and centralized identity management simplifies principal lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mTLS&lt;/strong&gt; (mutual TLS) requires both the client and the broker to present certificates, providing two-way authentication. The broker validates the client by certificate rather than by username and password. It works well for service-to-service communication where you can automate the certificate lifecycle. It becomes operationally burdensome for human users and dynamic client environments where certificate distribution is difficult to manage.&lt;/p&gt;
&lt;h3 id=&quot;choosing-a-mechanism&quot;&gt;Choosing a mechanism&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloud-native, modern IdP&lt;/td&gt;
&lt;td&gt;SASL/OAUTHBEARER&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise on-prem with Kerberos&lt;/td&gt;
&lt;td&gt;SASL/GSSAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service-to-service, automatable certs&lt;/td&gt;
&lt;td&gt;mTLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simpler production environment, no Kerberos&lt;/td&gt;
&lt;td&gt;SASL/SCRAM-SHA-256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development only&lt;/td&gt;
&lt;td&gt;SASL/PLAIN (with TLS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;authorization-locking-down-topics-groups-and-the-cluster&quot;&gt;Authorization: locking down topics, groups, and the cluster&lt;/h2&gt;
&lt;h3 id=&quot;how-kafka-acls-work&quot;&gt;How Kafka ACLs work&lt;/h3&gt;
&lt;p&gt;A Kafka ACL is a rule with the structure: &lt;strong&gt;Principal + Resource + Operation + Permission (Allow/Deny)&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Principal&lt;/strong&gt;: A user identity, for example &lt;code&gt;User:consumer-app&lt;/code&gt; or &lt;code&gt;User:kafka-broker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource types&lt;/strong&gt;: Topics, Consumer Groups, Cluster, Transactional IDs, Delegation tokens&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operations&lt;/strong&gt;: Read, Write, Create, Delete, Alter, Describe, DescribeConfigs, AlterConfigs, ClusterAction, IdempotentWrite, All&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permission&lt;/strong&gt;: Allow or Deny&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When &lt;code&gt;allow.everyone.if.no.acl.found=false&lt;/code&gt; (the setting you should always use in production), access is denied by default if no ACL matches. The consequence: the first thing you need to do when enabling authorization is create ACLs for your brokers themselves, so they can replicate.&lt;/p&gt;
&lt;h3 id=&quot;role-patterns&quot;&gt;Role patterns&lt;/h3&gt;
&lt;p&gt;A read-only consumer needs READ on specific topics, READ on its consumer groups, and DESCRIBE on topics. Missing the DESCRIBE permission causes cryptic “unknown topic” errors even when the topic exists, which is a common troubleshooting dead end.&lt;/p&gt;
&lt;p&gt;A write-only producer needs WRITE on specific topics and DESCRIBE on the cluster for metadata requests.&lt;/p&gt;
&lt;p&gt;`# Minimal producer ACL&lt;br&gt;
kafka-acls –bootstrap-server kafka-broker:9093 \&lt;br&gt;
 –command-config admin.properties \&lt;br&gt;
 –add \&lt;br&gt;
 –allow-principal User:producer-app \&lt;br&gt;
 –operation WRITE \&lt;br&gt;
 –operation DESCRIBE \&lt;br&gt;
 –topic orders&lt;/p&gt;
&lt;h1 id=&quot;minimal-consumer-acl&quot;&gt;Minimal consumer ACL&lt;/h1&gt;
&lt;p&gt;kafka-acls –bootstrap-server kafka-broker:9093 \&lt;br&gt;
 –command-config admin.properties \&lt;br&gt;
 –add \&lt;br&gt;
 –allow-principal User:consumer-app \&lt;br&gt;
 –operation READ \&lt;br&gt;
 –operation DESCRIBE \&lt;br&gt;
 –topic orders&lt;/p&gt;
&lt;p&gt;kafka-acls –bootstrap-server kafka-broker:9093 \&lt;br&gt;
 –command-config admin.properties \&lt;br&gt;
 –add \&lt;br&gt;
 –allow-principal User:consumer-app \&lt;br&gt;
 –operation READ \&lt;br&gt;
 –group consumer-group-1`&lt;/p&gt;
&lt;p&gt;The practical failure mode in authorization is permission creep. ACLs accumulate over time: teams grant broad permissions to reduce access request friction, services get decommissioned without their principals being revoked, and you end up with producers that can also consume and consumer groups with write access they should not have. Auditing ACLs regularly is as important as setting them correctly in the first place.&lt;/p&gt;
&lt;h3 id=&quot;the-limits-of-native-acls&quot;&gt;The limits of native ACLs&lt;/h3&gt;
&lt;p&gt;Native Kafka ACLs become operationally difficult at scale for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every user-topic-operation combination requires an explicit entry&lt;/li&gt;
&lt;li&gt;There is no concept of time-limited access; access granted during an incident stays granted until someone manually revokes it&lt;/li&gt;
&lt;li&gt;No group-based assignment; you manage individual principals&lt;/li&gt;
&lt;li&gt;Maintenance overhead grows with team and topic count&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;External authorizers address these limitations. &lt;strong&gt;Open Policy Agent (OPA)&lt;/strong&gt; evaluates authorization requests against policy-as-code and can also detect security policy violations (overly permissive ACLs, brokers not configured for TLS) before they reach production. &lt;strong&gt;Apache Ranger&lt;/strong&gt; provides centralized policy management with a UI, commonly used in Hadoop and enterprise data environments. At the extreme end of scale, Uber implemented a custom &lt;code&gt;KafkaAuthorizer&lt;/code&gt; delegating to their internal IAM framework, using an attribute-based model where a single generic policy replaces thousands of individual ACL entries. Topic ownership changes propagate automatically without any policy update. Most teams will not build this from scratch, but it illustrates what breaks down with native ACLs at scale.&lt;/p&gt;
&lt;h2 id=&quot;audit-logging-what-kafka-logs-and-what-it-doesnt&quot;&gt;Audit logging: what Kafka logs and what it doesn’t&lt;/h2&gt;
&lt;h3 id=&quot;native-logging-capabilities&quot;&gt;Native logging capabilities&lt;/h3&gt;
&lt;p&gt;Kafka does not provide audit logging out of the box, but it does provide the components to build one. The primary mechanism is &lt;code&gt;kafka.authorizer.logger&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Logs denied operations at &lt;code&gt;INFO&lt;/code&gt; level&lt;/li&gt;
&lt;li&gt;Logs allowed operations at &lt;code&gt;DEBUG&lt;/code&gt; level&lt;/li&gt;
&lt;li&gt;Each entry includes the principal, client host, attempted operation, and resource&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;logger.authorizer.name=kafka.authorizer.logger   logger.authorizer.level=DEBUG   logger.authorizer.appenderRef.authorizer.ref=authorizerAppender   logger.authorizer.additivity=false&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The practical problem: on a busy cluster processing 10,000 requests per second, DEBUG-level authorizer logs generate gigabytes per hour. Teams enable DEBUG for compliance purposes and find it unmanageable in practice. The result is often logs that capture denials but not allows, meaning you know who was refused access, but not who successfully read your payments topic.&lt;/p&gt;
&lt;h3 id=&quot;what-a-useful-audit-trail-covers&quot;&gt;What a useful audit trail covers&lt;/h3&gt;
&lt;p&gt;A complete audit trail should capture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication events: successful logins, failed attempts, mechanism used&lt;/li&gt;
&lt;li&gt;Authorization decisions: both allows and denies&lt;/li&gt;
&lt;li&gt;ACL changes: who created, modified, or deleted ACLs&lt;/li&gt;
&lt;li&gt;Topic access: producer writes and consumer reads per principal&lt;/li&gt;
&lt;li&gt;Administrative operations: topic creation and deletion, configuration changes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;shipping-logs-to-a-siem&quot;&gt;Shipping logs to a SIEM&lt;/h3&gt;
&lt;p&gt;Audit logs sitting in broker-local rolling files are not useful for compliance or incident response. The standard approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure &lt;code&gt;kafka.authorizer.logger&lt;/code&gt; with a dedicated rolling file appender per broker&lt;/li&gt;
&lt;li&gt;Use a log shipper (Filebeat, Fluentd, Logstash) to forward to Elasticsearch, Splunk, or your SIEM of choice&lt;/li&gt;
&lt;li&gt;Set retention appropriate to your compliance requirements. SOC 2 Type II and HIPAA commonly require 90 days or more; some regulations require 7 years&lt;/li&gt;
&lt;li&gt;Build alert rules: failed authentication spikes, ACL changes outside change windows, unexpected principals accessing sensitive topics&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When an auditor asks who accessed a specific dataset in the last 90 days, you need 90 days of aggregated, queryable authorizer logs. Teams that have not configured centralized log shipping frequently discover they have 7 days of rolling files on broker disks. That gap typically costs months of remediation work.&lt;/p&gt;
&lt;h2 id=&quot;network-level-security-the-layer-beneath-kafka&quot;&gt;Network-level security: the layer beneath Kafka&lt;/h2&gt;
&lt;p&gt;Kafka was designed with a somewhat trusted network assumption, originating from LinkedIn’s internal infrastructure. That assumption does not hold in multi-cloud, cloud-native, or multi-tenant deployments. Network security is not a substitute for authentication and authorization, but it is an additional layer. Even with authentication configured, a broker port reachable from the public internet is an unnecessary attack surface.&lt;/p&gt;
&lt;h3 id=&quot;listener-configuration&quot;&gt;Listener configuration&lt;/h3&gt;
&lt;p&gt;Kafka brokers can expose multiple listeners with different security protocols. Separating internal, external, and controller traffic onto distinct listeners provides isolation:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;listeners=INTERNAL://0.0.0.0:9093,EXTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:9094   listener.security.protocol.map=INTERNAL:SASL_SSL,EXTERNAL:SASL_SSL,CONTROLLER:SASL_SSL&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The internal listener handles broker-to-broker and trusted service traffic. The external listener handles client applications. The controller listener (KRaft) handles controller-to-broker communication in isolation.&lt;/p&gt;
&lt;h3 id=&quot;advertised-listeners&quot;&gt;Advertised listeners&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;advertised.listeners&lt;/code&gt; tells clients what address to reconnect to after the initial bootstrap connection. A misconfigured advertised listener can expose an internal hostname to external clients, or direct traffic to a PLAINTEXT listener regardless of how the initial connection was configured. The client connects to the bootstrap address, receives metadata, and then reconnects to the advertised listener. If that address resolves to an unencrypted port, subsequent connections are unencrypted. This is a particularly common misconfiguration in containerized environments.&lt;/p&gt;
&lt;h3 id=&quot;network-isolation&quot;&gt;Network isolation&lt;/h3&gt;
&lt;p&gt;Kafka brokers should not have public IP addresses unless explicitly required. Use VPC peering, private endpoints, or PrivateLink for cross-account or cross-region access. Firewall rules and security groups should restrict Kafka ports (9092, 9093, and 2181 for ZooKeeper) to known IP ranges: application subnets and management bastion hosts. Port 2181 is frequently the most exposed, and ZooKeeper with unauthenticated access on a reachable port is a critical risk.&lt;/p&gt;
&lt;h2 id=&quot;zookeeper-and-kraft-securing-the-control-plane&quot;&gt;ZooKeeper and KRaft: securing the control plane&lt;/h2&gt;
&lt;h3 id=&quot;zookeeper-the-overlooked-attack-surface&quot;&gt;ZooKeeper: the overlooked attack surface&lt;/h3&gt;
&lt;p&gt;ZooKeeper stores cluster membership, topic configurations, partition assignments, ACL definitions, and in older Kafka versions, consumer group offsets. An attacker with access to an unauthenticated ZooKeeper can read all of this, modify ACLs, delete topics, and inject bogus broker registrations.&lt;/p&gt;
&lt;p&gt;The common failure mode is that teams focus on securing the brokers while leaving ZooKeeper accessible on a “trusted” internal network. Any host compromised on that network then has access to the cluster’s entire control plane.&lt;/p&gt;
&lt;p&gt;ZooKeeper is deprecated. Kafka 3.9, released November 2024, is the last version to support ZooKeeper mode. ZooKeeper-based clusters lose official security patches in November 2025. Kafka 4.0, released March 2025, removed ZooKeeper entirely. If your cluster is still running ZooKeeper mode, migration to KRaft is a security requirement at this point, not an optimization.&lt;/p&gt;
&lt;h3 id=&quot;kraft-what-changes&quot;&gt;KRaft: what changes&lt;/h3&gt;
&lt;p&gt;KRaft eliminates ZooKeeper entirely. Cluster metadata moves into an internal Kafka topic (&lt;code&gt;__cluster_metadata&lt;/code&gt;), managed by a quorum of controller nodes using the Raft consensus protocol.&lt;/p&gt;
&lt;p&gt;From a security standpoint, the main improvements are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Single security model.&lt;/strong&gt; Previously you needed separate SASL and ACL configuration for Kafka and ZooKeeper independently. Teams that secured Kafka correctly but not ZooKeeper remained exposed. KRaft removes that second attack surface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simplified audit surface.&lt;/strong&gt; One system’s logs to monitor instead of two.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Faster failover.&lt;/strong&gt; Controller failover drops from minutes to seconds, reducing the window where the cluster is in an uncertain state.&lt;/p&gt;
&lt;p&gt;What does not change: ACLs, authentication, and TLS still require proper configuration. The &lt;code&gt;__cluster_metadata&lt;/code&gt; topic is internal but subject to the cluster’s overall security posture. Sensitive data in KRaft records is not encrypted by Kafka itself; disk-level encryption remains necessary for at-rest protection.&lt;/p&gt;
&lt;p&gt;For production KRaft deployments: a minimum of three controller nodes for quorum (five for high availability), deployed on dedicated nodes with fast SSD storage across separate availability zones, with the controller listener isolated from client listeners.&lt;/p&gt;
&lt;h2 id=&quot;putting-it-together-a-layered-security-model&quot;&gt;Putting it together: a layered security model&lt;/h2&gt;
&lt;p&gt;The correct mental model for Kafka security is defense in depth. Each layer provides protection when another is compromised.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Network layer     VPC isolation, security groups, private listeners          |   Encryption layer     TLS 1.3 for all traffic: client-broker, broker-broker, control plane          |   Authentication layer     SASL (SCRAM / OAUTHBEARER / GSSAPI) or mTLS (no anonymous connections)          |   Authorization layer     ACLs or external authorizer (OPA / Ranger / custom), least privilege          |   Audit layer     Centralized, retained, queryable logs shipped to SIEM          |   Operational layer     Regular ACL review, certificate rotation, patching, misconfiguration scanning&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If an attacker compromises a credential and gets through the authentication layer, the authorization layer limits what they can access. If they get through authorization, the audit layer creates a detectable signal. No single layer is sufficient on its own.&lt;/p&gt;
&lt;h2 id=&quot;common-security-misconfigurations&quot;&gt;Common security misconfigurations&lt;/h2&gt;
&lt;p&gt;These are the configurations that most frequently go wrong in real deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unauthenticated listeners left open.&lt;/strong&gt; &lt;code&gt;ALLOW_PLAINTEXT_LISTENER=yes&lt;/code&gt; is a real environment variable in the Bitnami Docker image, included for development convenience. In clusters that started as “a quick test,” this configuration persists into production. Any host that can reach port 9092 becomes a producer, consumer, or admin client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wildcard ACLs in production.&lt;/strong&gt; Granting &lt;code&gt;User:* ALLOW ALL&lt;/code&gt; on a topic makes the authorization layer functionally meaningless. You have authenticated clients, but all of them can do everything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Super users beyond the initial admin.&lt;/strong&gt; The &lt;code&gt;super.users&lt;/code&gt; property bypasses all ACL checks. In many environments this list grows over time as engineers add themselves for debugging access that never gets revoked. It should be a short, actively managed list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inter-broker encryption disabled.&lt;/strong&gt; Client connections use TLS, replication does not. &lt;code&gt;security.inter.broker.protocol&lt;/code&gt; defaults to PLAINTEXT and must be explicitly overridden.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ZooKeeper exposed without authentication.&lt;/strong&gt; Port 2181 open on a security group, no SASL configured, no ACLs on ZooKeeper nodes. With ZooKeeper deprecated, this is increasingly a migration-blocking technical debt issue as well as a security risk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SASL/PLAIN over unencrypted connections.&lt;/strong&gt; Using &lt;code&gt;SASL_PLAINTEXT&lt;/code&gt; with the PLAIN mechanism transmits credentials in cleartext. Credentials are visible to anyone capturing traffic on the network segment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema Registry and Kafka Connect left unsecured.&lt;/strong&gt; The security posture applied to brokers should extend to adjacent components. Connect workers running as Kafka clients need authentication and authorization. Schema Registry exposes an HTTP API that needs its own TLS and authentication. A Connect worker with admin-level cluster permissions and no authentication is a privileged path into your cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit log retention too short.&lt;/strong&gt; Seven days of rolling files on broker-local disk fails SOC 2, HIPAA, and PCI-DSS audits that require 90-day or longer access history. Configure centralized shipping with appropriate retention from the start.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permission creep over time.&lt;/strong&gt; Deprecated services still have active principals with write access. Team membership changes but ACLs do not. Treat ACL review as a recurring operational task, not a one-time setup.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-with-kafka-security-management&quot;&gt;How Kpow helps with Kafka security management&lt;/h2&gt;
&lt;p&gt;One area where Kafka’s native tooling is limited is visibility into what is actually happening across your cluster, particularly around access control and user activity.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a management and &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;monitoring tool&lt;/a&gt; for Kafka that includes capabilities relevant to the authorization and audit sections of the architecture described above.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ff6298dab81986118e48d0_kpow-security-user-profile.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authorization management.&lt;/strong&gt; Kpow supports both simple access control via environment variable configuration and full &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;role-based access control&lt;/a&gt; that integrates with your identity provider. RBAC in Kpow maps user roles to specific actions across clusters, topics, consumer groups, schemas, and connectors. Users are denied all actions by default; permissions are explicitly granted. This means every operator using Kpow to administer your cluster is subject to the same least-privilege model you are applying to your Kafka clients.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit logging.&lt;/strong&gt; Kpow captures all user actions in an audit log, stored in an internal topic (&lt;code&gt;__oprtr_audit_log&lt;/code&gt;) and queryable from the UI. The audit log records the contents of each request, the identity of the user who made it, and the authorization result including which policies were evaluated. Admins can view the last seven days of activity from within the application.&lt;/p&gt;
&lt;p&gt;For integration with external systems, Kpow supports &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/webhook&quot;&gt;webhook delivery&lt;/a&gt; of audit log events to Slack, Microsoft Teams, or any custom endpoint. You can configure verbosity to capture mutations only, queries only, or all events, and the structured JSON payload is suitable for routing into a SIEM or triggering downstream workflows.&lt;/p&gt;
&lt;p&gt;This addresses a specific gap in the native Kafka audit story: while &lt;code&gt;kafka.authorizer.logger&lt;/code&gt; records broker-level authorization decisions, it does not capture actions taken by humans using a management UI. Kpow’s audit log covers that layer.&lt;/p&gt;
&lt;p&gt;You can try Kpow with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;, and connect it to any Kafka cluster in minutes by deploying via Docker, Helm, or JAR.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Building a production-ready Kafka security architecture requires deliberate configuration across multiple layers. The starting point is acknowledging that Kafka ships with all security controls disabled. From there, the work is methodical: TLS across all three traffic flows, authentication appropriate to your environment and identity infrastructure, authorization scoped to the principle of least privilege, and audit logging that is centralized, retained, and queryable, not just enabled.&lt;/p&gt;
&lt;p&gt;The operational work is as important as the initial configuration. Certificate rotation, ACL review, and misconfiguration scanning need to be recurring processes. Security configurations that are correct at deployment drift over time as teams, services, and infrastructure change. The clusters that end up with the most significant exposures are not the ones that were never secured but the ones that were secured once and then not maintained.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka UI: The Ultimate Guide</title><link>https://factorhouse.io/articles/kafka-ui/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-ui/</guid><description>A Kafka UI is a web interface for managing Apache Kafka, giving operators visual control over topics, consumers, brokers, and connectors without the CLI.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A Kafka UI is a web-based interface that sits on top of an &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; cluster and turns the broker, topic, partition, and consumer-group APIs into something you can read and operate from a browser. The CLI tools shipped with Kafka are sufficient when you run a handful of topics on a single cluster. They become a friction point as soon as several teams share the platform, when operators need a consistent view across environments, or when an incident requires you to inspect partition state in seconds rather than minutes.&lt;/p&gt;
&lt;p&gt;To get a sense of the operational surface area a Kafka UI has to cover at the upper end, &lt;a href=&quot;/articles/jpmorgan-kafka-architecture&quot;&gt;JPMorgan&lt;/a&gt; runs 102 clusters with around 510 nodes and 13,000 topics, ingesting roughly 400 billion events per day. At that scale, the difference between a UI that surfaces under-replicated partitions clearly and one that forces an operator back to &lt;code&gt;kafka-topics.sh&lt;/code&gt; is the difference between a five-minute fix and a thirty-minute incident.&lt;/p&gt;
&lt;p&gt;This guide is written for platform and data engineers evaluating which Kafka UI to standardise on. It covers what a Kafka UI does, how the market is segmented, the criteria worth weighing during evaluation, and how the major tools compare in practice.&lt;/p&gt;
&lt;h2 id=&quot;what-does-a-kafka-ui-actually-do&quot;&gt;What does a Kafka UI actually do?&lt;/h2&gt;
&lt;p&gt;Most Kafka UIs converge on the same set of functional categories. The depth of coverage varies, but the categories are stable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topic management.&lt;/strong&gt; Create, delete, and reconfigure topics, inspect partition layout and replica placement, and read messages from a partition with offset, key, header, and value filtering. Topic management is the day-to-day surface for both operators and developers, covering everything from partition count decisions to per-topic retention and compaction settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer group monitoring and offset management.&lt;/strong&gt; View consumer group membership, per-partition lag, current offset against log-end offset, and reset or seek consumer offsets when a consumer needs to replay or skip a window of data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker and cluster health.&lt;/strong&gt; Broker liveness, controller identity, partition distribution, under-replicated and offline partitions, and basic throughput metrics at the broker level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema registry integration.&lt;/strong&gt; Browse subjects, view schema versions and compatibility settings, and decode Avro, Protobuf, or JSON Schema payloads in the message viewer so message content is human-readable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Access control.&lt;/strong&gt; Surface and manage ACLs or RBAC role assignments depending on the underlying authorisation model, and capture an audit trail of who changed what.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connector management.&lt;/strong&gt; List Kafka Connect connectors, view status and task health, pause and restart connectors, and inspect configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Producer tooling.&lt;/strong&gt; Send test messages with arbitrary keys, headers, and serialisers from inside the UI, exercising the same producer path that application code uses.&lt;/p&gt;
&lt;p&gt;Each of these categories maps to a different operational job, and a tool’s coverage of any one of them is rarely binary. Most UIs do the easy parts well and differ on the harder parts: schema-aware filtering, RBAC granularity, Connect task observability, and offset-reset workflows.&lt;/p&gt;
&lt;h2 id=&quot;the-kafka-ui-landscape&quot;&gt;The Kafka UI landscape&lt;/h2&gt;
&lt;p&gt;The market splits into three rough categories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open-source and self-hosted.&lt;/strong&gt; &lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat UI&lt;/a&gt;, &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda Console&lt;/a&gt;, and &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; sit here. They are free to download, ship as Docker images or self-contained binaries, and cover the core functional categories well. Governance features such as fine-grained RBAC, audit trails, and data masking are typically thinner or behind a paid tier. A comparison of the most widely-used options is available in &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;best free kafka ui tools&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Commercial and self-hosted or managed.&lt;/strong&gt; &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;, &lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt;, &lt;a href=&quot;/articles/lenses&quot;&gt;Lenses.io&lt;/a&gt;, and &lt;a href=&quot;/articles/kadeck&quot;&gt;Kadeck&lt;/a&gt; sit here. They charge a per-cluster, per-user, or per-seat fee in exchange for deeper RBAC, audit logging, governance workflows, and vendor support. Deployment is usually still under your control. For a broader view across both open-source and commercial options, &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;best kafka management tools&lt;/a&gt; covers the full landscape.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vendor-bundled.&lt;/strong&gt; &lt;a href=&quot;/articles/confluent-control-center&quot;&gt;Confluent Control Center&lt;/a&gt; is the canonical example. It ships as part of Confluent Platform and integrates tightly with Confluent’s Schema Registry, ksqlDB, Cluster Linking, and MDS-based RBAC. The trade-off is that it is effectively locked to Confluent deployments.&lt;/p&gt;
&lt;p&gt;One landscape shift worth flagging: Kafka 4.0 fully removed ZooKeeper in March 2025, and KRaft is now the only supported metadata mode. Any UI that still relies on ZooKeeper-based metadata APIs has a gap to close. The well-maintained tools have already made the transition. If you are evaluating something less actively developed, confirm KRaft support before committing.&lt;/p&gt;
&lt;h2 id=&quot;how-to-evaluate-a-kafka-ui-key-criteria&quot;&gt;How to evaluate a Kafka UI: key criteria&lt;/h2&gt;
&lt;p&gt;The criteria below are the ones I weight most heavily during a real evaluation. Different teams will rank them differently, but skipping any of them tends to produce regret a year in.&lt;/p&gt;
&lt;h3 id=&quot;monitoring-and-observability-depth&quot;&gt;Monitoring and observability depth&lt;/h3&gt;
&lt;p&gt;The baseline question is whether the UI surfaces the metrics that actually move during an incident: &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;consumer lag&lt;/a&gt; at partition granularity, under-replicated partitions, offline partitions, broker request latency, and partition skew across brokers. The easy metrics (broker count, topic count, total messages) are uniform across tools. The harder ones are not.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/datadog-kafka-architecture&quot;&gt;Datadog&lt;/a&gt; provides a useful illustration of why visibility matters. The team reduced a host-subscriptions topic from 500,000 messages per second to 5,000 messages per second, a 100x reduction, after gaining clearer visibility into their pipeline. The optimisation freed over 600 CPU cores and 1 TB of memory. The lesson is that observability is what makes optimisation possible: you cannot improve what you cannot see.&lt;/p&gt;
&lt;p&gt;A Kafka UI handles the real-time slice of this: point-in-time consumer monitoring, cluster health, and producer visibility. For time-series storage, alerting, and capacity planning, the UI should complement a proper monitoring stack rather than replace it. The &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;kafka monitoring&lt;/a&gt; guide covers that boundary in full, including guidance on kafka dashboards, &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;best kafka monitoring tools&lt;/a&gt;, and integrations with tools like &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Grafana&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-multi-tenancy&quot;&gt;Access control and multi-tenancy&lt;/h3&gt;
&lt;p&gt;When a shared cluster supports more than two teams, ACLs alone become difficult to manage. Granular &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC&lt;/a&gt;, audit logging, and an identity-provider integration (SAML, OIDC, LDAP) are the features that matter. Look for role-based authorisation at the topic, consumer-group, and connector level, with the ability to scope roles to a single environment. For multi-tenant Kafka deployments, the UI is where access policies become visible and auditable across teams.&lt;/p&gt;
&lt;p&gt;Uber illustrates the scaling problem well. Their custom KafkaAuthorizer uses a single attribute-based policy to replace what would otherwise be thousands of individual ACL entries. A UI that surfaces and manages permissions weakly would make that model unworkable in operation, even if the authentication, authorisation, and encryption at the broker level are sound.&lt;/p&gt;
&lt;p&gt;For a deeper treatment of the security model underneath the UI, see the &lt;a href=&quot;/articles/kafka-security-architecture&quot;&gt;kafka security architecture&lt;/a&gt; article.&lt;/p&gt;
&lt;h3 id=&quot;schema-registry-support&quot;&gt;Schema registry support&lt;/h3&gt;
&lt;p&gt;Schema-aware message viewing is the difference between a useful inspection workflow and a Base64-decoded mess. The UI should resolve subjects automatically, decode Avro, Protobuf, and JSON Schema payloads, surface compatibility rules, and let you view historical schema versions. Schema evolution visibility is particularly useful during incidents involving consumer deserialisation failures.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-management&quot;&gt;Kafka Connect management&lt;/h3&gt;
&lt;p&gt;For teams using &lt;a href=&quot;/articles/kafka-cdc-change-data-capture&quot;&gt;Kafka Connect for CDC&lt;/a&gt;, sink-to-warehouse, or system integration, the UI should surface connector lifecycle (paused, running, failed), per-task status, and configuration. Restart and pause controls from the UI shorten the loop during connector incidents. Use cases range from &lt;a href=&quot;/how-to/self-service-kafka-governance-with-kpow-and-servicenow&quot;&gt;ServiceNow integrations&lt;/a&gt; to Jira event pipelines, and in each case connector observability is what separates a quick diagnosis from a long one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/notion-kafka-architecture&quot;&gt;Notion&lt;/a&gt; replaced custom connectors with Confluent’s pre-built Connect integrations after concluding that the old approach was, in their words, “too expensive and difficult to maintain at scale.” The change saved over $1 million in 2022 and tripled engineering productivity. A UI that surfaces connector health directly reduces the operational cost of running Connect at scale.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing-and-ksqldb-visibility&quot;&gt;Stream processing and ksqlDB visibility&lt;/h3&gt;
&lt;p&gt;Kafka Streams applications and ksqlDB queries are first-class citizens in many production estates, but few UIs treat them as such. The ones that do let you inspect topology, internal state stores, and per-query status. If you run Streams applications in production, the ability to navigate from a consumer group back to the topology that owns it, and forward to the state-store partitions that back it, removes a category of debugging that would otherwise need bespoke tooling. For ksqlDB users, the equivalent is a UI that lists queries, their source and sink topics, and their current status.&lt;/p&gt;
&lt;h3 id=&quot;deployment-model-and-operational-overhead&quot;&gt;Deployment model and operational overhead&lt;/h3&gt;
&lt;p&gt;Self-hosted versus SaaS, single container versus Helm chart, and how the UI authenticates to the cluster (direct broker access, a JMX scrape, or an agent proxy). Security teams will care about the data-plane access model: a UI that proxies all client traffic to brokers has a very different blast radius from one that only reads metadata.&lt;/p&gt;
&lt;p&gt;Installation footprint matters during evaluation. Most open-source tools ship a single container that can be running against a test cluster within minutes. Commercial tools tend to involve a Helm chart, an admin database, and an initial RBAC bootstrap, which is closer to an hour of work. SaaS options remove the deployment step entirely but introduce data-residency and connectivity questions.&lt;/p&gt;
&lt;h3 id=&quot;multi-cluster-and-multi-environment-management&quot;&gt;Multi-cluster and multi-environment management&lt;/h3&gt;
&lt;p&gt;Almost every team running Kafka in production runs at least three clusters: development, staging, and production. Larger estates add regional clusters, isolation clusters for regulated workloads, and dedicated clusters for high-throughput pipelines. A UI that handles only one cluster at a time forces operators to tab between browser windows, breaks audit trails at cluster boundaries, and makes cross-environment comparison (does the staging topic config actually match production?) tedious.&lt;/p&gt;
&lt;p&gt;When evaluating multi-cluster support, look for: a cluster switcher that preserves context, unified search across clusters, environment-aware RBAC so that production write permissions do not leak to development users, and a way to compare topic configuration across clusters without leaving the UI. Some tools also offer global catalogs that list every topic across every cluster in one view, which is useful in estates where the same logical topic name is used across environments.&lt;/p&gt;
&lt;h3 id=&quot;producer-and-consumer-tooling&quot;&gt;Producer and consumer tooling&lt;/h3&gt;
&lt;p&gt;The ability to produce a test message and consume from an arbitrary offset is a small feature with large operational impact. During incidents, the question “is the producer actually sending, and is the broker actually accepting?” comes up often. A UI that lets you produce a known-good message from a known-good identity narrows the search space quickly. Related tools for managing offsets and records from the CLI include the kafka offset tool, the ability to &lt;a href=&quot;/how-to/delete-records-in-kafka&quot;&gt;delete records&lt;/a&gt; from a topic, and the kafka admin tool.&lt;/p&gt;
&lt;h3 id=&quot;pricing-model-and-total-cost-of-ownership&quot;&gt;Pricing model and total cost of ownership&lt;/h3&gt;
&lt;p&gt;Free is not the same as low cost. Open-source tools have no licence fee, but the engineering time spent deploying them, upgrading them when the upstream project releases a new version, patching them when a CVE lands, and carrying them on the on-call rotation is real. For a small team running a single cluster, that overhead is trivial. For a platform team running ten clusters across three regions, it adds up.&lt;/p&gt;
&lt;p&gt;Commercial tools split into two pricing shapes. Per-cluster pricing (often used for self-hosted commercial tools) scales with infrastructure and is predictable as headcount changes. Per-seat or per-user pricing scales with adoption: the more useful the tool is, the more it costs. For estates where the goal is broad self-service, per-cluster pricing tends to age better. For estates where access is restricted to a small platform team, per-seat pricing can be cheaper.&lt;/p&gt;
&lt;p&gt;A complete TCO model should include: licence fees, the engineering cost of deployment and maintenance (often half a person, sometimes more), support tier costs, and the opportunity cost of operator time spent in CLI workflows that the UI would have automated. The last item is hardest to quantify but often the largest.&lt;/p&gt;
&lt;h3 id=&quot;performance-and-footprint-at-scale&quot;&gt;Performance and footprint at scale&lt;/h3&gt;
&lt;p&gt;How does the UI itself behave when it is pointed at a cluster with thousands of topics and hundreds of consumer groups? Page load times, search responsiveness, message-viewer latency, and the memory footprint of the UI server become operational concerns at the kind of scale referenced in the &lt;a href=&quot;/articles/jpmorgan-kafka-architecture&quot;&gt;JPMorgan&lt;/a&gt; and &lt;a href=&quot;/articles/walmart-kafka-architecture&quot;&gt;Walmart&lt;/a&gt; case studies. Some UIs paginate well and use server-side filtering; others pull metadata in bulk and degrade noticeably past a few thousand topics. If your estate is at that scale, ask vendors for a benchmark against a comparable topic count, and if you are evaluating an open-source tool, test it against a representative cluster before committing.&lt;/p&gt;
&lt;h3 id=&quot;disaster-recovery-and-cluster-linking-visibility&quot;&gt;Disaster recovery and cluster linking visibility&lt;/h3&gt;
&lt;p&gt;For multi-region deployments, the UI’s view into replication health matters. MirrorMaker 2 topology, replication lag between source and target clusters, and the configuration of replicated topics are all things you want to inspect from one place rather than from a mix of CLI and broker logs. Confluent Cluster Linking introduces a different model that some UIs surface natively and others ignore. &lt;a href=&quot;/articles/barclays-kafka-architecture&quot;&gt;Barclays&lt;/a&gt; runs Confluent Kafka on Amazon EKS with multi-region active-active and active-passive configurations, and Walmart operates Kafka across public and private clouds. At that footprint, replication visibility is a security and compliance concern as much as an operational one.&lt;/p&gt;
&lt;h2 id=&quot;kafka-ui-and-security&quot;&gt;Kafka UI and security&lt;/h2&gt;
&lt;p&gt;Security is non-negotiable when a UI has read and write access to a production cluster. The &lt;a href=&quot;/articles/kafka-security-architecture&quot;&gt;kafka security architecture&lt;/a&gt; has several layers, and the UI touches most of them. The relevant dimensions are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication.&lt;/strong&gt; SSO via SAML or OIDC, LDAP integration for on-prem identity providers, and local users as a fallback. Avoid tools that only support local users in production deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authorisation granularity.&lt;/strong&gt; Cluster-level, topic-level, consumer-group-level, and connector-level role assignments. The right granularity depends on how many teams share the cluster: a single-team deployment can live with cluster-level roles, a multi-team deployment cannot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit logging.&lt;/strong&gt; Every write action (offset reset, topic creation or deletion, connector restart, ACL change) should produce a tamper-evident audit record. For regulated industries, the audit log needs to be exportable to a SIEM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network exposure.&lt;/strong&gt; A UI server with broker credentials is a sensitive surface. Bind it to an internal network, put it behind your existing identity proxy, and avoid exposing it to the public internet even with authentication enabled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data-plane access model.&lt;/strong&gt; Some UIs proxy all client traffic to brokers, which means the UI host needs to be sized for the throughput it serves. Others only read metadata and produce or consume on demand, which keeps the UI host small but moves the data path back to the client. Both are valid; the right choice depends on your network model.&lt;/p&gt;
&lt;p&gt;Barclays is a useful reference here: Confluent Kafka on Amazon EKS, multi-region active-active and active-passive, Kafka-centric systems owned by a central platform team under SRE principles. At that scale and in a regulated industry, an unsecured &lt;a href=&quot;https://markdownlivepreview.com/#&quot;&gt;kafka security&lt;/a&gt; surface is a material risk, and the platform team’s choice of UI is part of the compliance story.&lt;/p&gt;
&lt;h2 id=&quot;kafka-ui-and-monitoring-where-the-overlap-ends&quot;&gt;Kafka UI and monitoring: where the overlap ends&lt;/h2&gt;
&lt;p&gt;Most Kafka UIs ship some level of built-in metrics. They are not substitutes for a proper &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;monitoring stack&lt;/a&gt;. The boundary between the two is worth being explicit about.&lt;/p&gt;
&lt;p&gt;A Kafka UI is for real-time operational interaction: point-in-time inspection, debugging, ad-hoc reads of message content, manual offset management, and connector control. Its job is to answer “what is happening right now, and what action do I want to take?”&lt;/p&gt;
&lt;p&gt;A monitoring stack (&lt;a href=&quot;/how-to/kafka-alerting-with-kpow-prometheus-and-alertmanager&quot;&gt;Prometheus&lt;/a&gt; and Grafana, Datadog, New Relic, Dynatrace) is for time-series storage, alerting on thresholds, SLO tracking, anomaly detection, and historical analysis. Its job is to answer “what has happened over the last hour, day, or quarter, and should someone be paged?”&lt;/p&gt;
&lt;p&gt;A useful checklist when evaluating where to draw the line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UI responsibilities:&lt;/strong&gt; consumer-group lag at the moment of inspection, partition state, broker liveness, message content inspection, schema lookup, ad-hoc producer test, connector status and restart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring stack responsibilities:&lt;/strong&gt; lag trended over time, alerting when lag breaches a threshold, broker JVM metrics, request-latency percentiles, retention and tiered-storage usage trends, capacity planning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practice, both layers consume similar broker JMX metrics. The standard convention is to scrape with &lt;code&gt;kafka-exporter&lt;/code&gt; and the JMX exporter, store in Prometheus, and visualise in Grafana. A UI that exposes its own Prometheus scrape endpoint can plug into that pipeline without duplication. Read &lt;a href=&quot;https://factorhouse.io/articles/best-practices-kafka-data-observability&quot;&gt;best practices for kafka data observability&lt;/a&gt; for guidance on metric collection patterns and the overlap between tooling layers in depth.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/netflix-kafka-architecture&quot;&gt;Netflix&lt;/a&gt; handles between 700 billion and 2 trillion events per day across its Keystone pipeline and Data Mesh platform. At that volume, a UI alone cannot provide the alerting and anomaly detection that production operations require, and the monitoring stack is what catches the slow-moving regressions a real-time UI is never going to flag. The two layers are complementary, not interchangeable.&lt;/p&gt;
&lt;h2 id=&quot;tool-by-tool-breakdown&quot;&gt;Tool-by-tool breakdown&lt;/h2&gt;
&lt;p&gt;Before the per-tool detail, the comparison table below summarises the dimensions most teams care about during a first-pass evaluation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Licence&lt;/th&gt;
&lt;th&gt;RBAC&lt;/th&gt;
&lt;th&gt;Multi-cluster&lt;/th&gt;
&lt;th&gt;Schema Registry&lt;/th&gt;
&lt;th&gt;Connect&lt;/th&gt;
&lt;th&gt;Pricing model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kpow&lt;/td&gt;
&lt;td&gt;Self-hosted container&lt;/td&gt;
&lt;td&gt;Commercial (Community Edition available)&lt;/td&gt;
&lt;td&gt;Full (SAML, OIDC, LDAP)&lt;/td&gt;
&lt;td&gt;Yes (up to 12 per instance)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafbat UI&lt;/td&gt;
&lt;td&gt;Self-hosted container&lt;/td&gt;
&lt;td&gt;Open-source (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Basic (YAML)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Console&lt;/td&gt;
&lt;td&gt;Self-hosted Go binary&lt;/td&gt;
&lt;td&gt;Open-core (BSL / RCL)&lt;/td&gt;
&lt;td&gt;Behind enterprise licence&lt;/td&gt;
&lt;td&gt;Limited without enterprise&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Free + enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;Self-hosted Micronaut app&lt;/td&gt;
&lt;td&gt;Open-source (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conduktor&lt;/td&gt;
&lt;td&gt;Self-hosted web platform&lt;/td&gt;
&lt;td&gt;Commercial (open-core Community tier)&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per seat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lenses.io&lt;/td&gt;
&lt;td&gt;Kubernetes-required&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per cluster + per user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kadeck&lt;/td&gt;
&lt;td&gt;Desktop and server&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Limited (Desktop)&lt;/td&gt;
&lt;td&gt;Yes (Server tier)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Control Center&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;td&gt;Commercial (Confluent Enterprise)&lt;/td&gt;
&lt;td&gt;Full (MDS-based)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Bundled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;kpow-factor-house&quot;&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow (Factor House)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commercial, self-hosted. Stateless JVM container that runs against any Apache Kafka cluster, with deep RBAC backed by SAML, OIDC, LDAP, or Keycloak SSO, full support for Schema Registry, Kafka Connect, ksqlDB, ACL management, and Kafka Streams topology visualisation. Up to 12 clusters per instance. Designed for SRE and platform teams running multi-cluster Kafka in regulated environments. Pricing is per cluster, which keeps cost predictable as headcount changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722460e8a935845facc77_kpow-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;kafbat-ui&quot;&gt;&lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat UI&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open-source under Apache 2.0, self-hosted as a container. Wide adoption, an active maintainer community, and good general-purpose coverage including multi-cluster support, Avro, Protobuf, and JSON deserialisation, Schema Registry integration, and CEL-based filtering. RBAC is YAML-based and minimal. Best fit for small to mid-size teams with modest governance needs. Worth noting that Kafbat UI is the active fork of what used to be the “kafka-ui” project on GitHub, which is the source of the recurring naming confusion in the community.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c72279c93efb0be1f59126_kafbat-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;redpanda-console&quot;&gt;&lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda Console&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open-core, Go binary. Fast message viewer and a clean operator experience. The community edition is BSL-licensed; multi-cluster support, SSO, RBAC, and data masking sit behind the Redpanda enterprise licence. Connect management is not included. Best fit for existing Redpanda customers with enterprise contracts. If you run Apache Kafka rather than Redpanda, the value is narrower.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7228f5010387c137d9bf0_redpanda-console-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;akhq&quot;&gt;&lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open-source under Apache 2.0, deployed as a Micronaut application. GitOps-friendly configuration, OIDC, OAuth2, LDAP, and GitHub SSO, full Connect and Schema Registry coverage, and ksqlDB support. Basic RBAC since version 0.25. No native data masking, and UI performance is known to degrade on very large clusters. Best fit for teams wanting a free, GitOps-native tool without server-side data masking requirements.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;conduktor&quot;&gt;&lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commercial, with an open-core Community tier. Self-hosted web platform with an optional Gateway proxy for ownership tracking, topic catalogs, self-service workflows. Be aware the proxy can introduce risk due to being in the data path. Best fit for large organisations where multiple teams share infrastructure and need explicit ownership and self-service. Per-seat pricing scales with adoption: published estimates for 100 users across 3 clusters land in the $80,000-$150,000 per year range.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722b1f17be485095adbee_conduktor-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;lensesio&quot;&gt;&lt;a href=&quot;/articles/lenses&quot;&gt;Lenses.io&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commercial, Kubernetes-required (SQL Processors run as Kubernetes pods). The differentiator is SQL-driven stream processing alongside observability, including topology visualisation, a Kafka-to-Kafka replicator, a global multi-cluster catalog, and data policies. Best fit for teams wanting SQL-driven stream processing inside the same tool that handles operational interaction. The Kubernetes dependency adds operational overhead, and post-acquisition product direction is worth confirming with the vendor during evaluation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722c109a967e2ee935e20_lenses-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;kadeck&quot;&gt;&lt;a href=&quot;/articles/kadeck&quot;&gt;Kadeck&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commercial. Desktop-first product with a server tier for team deployments. Good for individual developer use and quick local inspection of clusters; less suited to team-based operations where shared audit trails and centralised RBAC are required.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a1413a51d9ac9bc9dc7bf02_kadeck-kafka.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;confluent-control-center&quot;&gt;&lt;a href=&quot;/articles/confluent-control-center&quot;&gt;Confluent Control Center&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Bundled with Confluent Platform Enterprise. Native integration with the Confluent ecosystem: Schema Registry, ksqlDB, Cluster Linking, and MDS-based RBAC. Effectively locked to Confluent deployments, and the advanced features depend on MDS being present. Typical Confluent Platform pricing ranges from roughly $50,000 to $500,000+ per year, depending on cluster footprint and support tier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722a00e8a935845fad116_confluent-control-center-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kafka-ui-in-practice-three-operational-scenarios&quot;&gt;Kafka UI in practice: three operational scenarios&lt;/h2&gt;
&lt;p&gt;The criteria above are easier to evaluate against concrete jobs. The three scenarios below cover the most common ones I see during platform evaluations.&lt;/p&gt;
&lt;h3 id=&quot;debugging-consumer-lag-during-an-incident&quot;&gt;Debugging consumer lag during an incident&lt;/h3&gt;
&lt;p&gt;A consumer group has fallen behind. The on-call engineer opens the UI, navigates to the consumer group, and looks at the per-partition lag. One partition is responsible for the bulk of the lag; the others are caught up. The UI shows the partition leader, the consumer instance currently assigned to it, and the timestamp of the last committed offset.&lt;/p&gt;
&lt;p&gt;From there, the engineer can inspect the messages near the current offset, look at the message size distribution, and check whether the consumer is failing to process a specific message type. Reviewing the consumer configuration and consumer properties can surface whether the issue is a processing timeout, a deserialisation error, or a throughput mismatch. If the partition has fallen too far behind to catch up within the SLA window, an offset reset to a later position becomes a deliberate decision rather than a panicked one. The UI captures the reset in its audit log, and kafka logs at the broker level can confirm what the consumer actually received.&lt;/p&gt;
&lt;h3 id=&quot;managing-topics-and-partitions-at-scale&quot;&gt;Managing topics and partitions at scale&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/articles/doordash-kafka-architecture&quot;&gt;DoorDash&lt;/a&gt; operates five Kafka clusters managed by a central Real-Time Streaming Platform team. At that footprint, topic governance covers replication factor, &lt;a href=&quot;/articles/kafka-topic-partition-best-practices&quot;&gt;partition count&lt;/a&gt;, retention policy, compaction settings, and per-topic ACLs. Good &lt;a href=&quot;/articles/kafka-partition-key-best-practices&quot;&gt;partition key selection&lt;/a&gt; and &lt;a href=&quot;/articles/kafka-message-key-best-practices&quot;&gt;message key design&lt;/a&gt; matter here too, since the UI makes partition skew visible but the root cause is usually upstream. &lt;a href=&quot;/articles/kafka-message-size-best-practice&quot;&gt;Message size&lt;/a&gt; is another configuration dimension the UI surfaces clearly. Doing this through CLI scripts is workable; doing it through a UI that lets the platform team review every new-topic request, apply a template, and audit the result is faster and less error-prone.&lt;/p&gt;
&lt;p&gt;The UI matters most when teams outside the platform team need to inspect or request changes. A self-service workflow that proposes the change, routes it to a platform reviewer, and applies it on approval is the difference between a healthy multi-tenant cluster and one where every team has direct admin access “just to get things done.” For the broader operational discipline around &lt;a href=&quot;/articles/kafka-cluster-management&quot;&gt;kafka cluster management&lt;/a&gt;, cluster best practices, and &lt;a href=&quot;/articles/kafka-scaling-best-practices&quot;&gt;kafka scaling best practices&lt;/a&gt;, those articles cover the decisions that sit behind the UI.&lt;/p&gt;
&lt;h3 id=&quot;onboarding-teams-to-a-shared-cluster&quot;&gt;Onboarding teams to a shared cluster&lt;/h3&gt;
&lt;p&gt;Walmart’s Kafka infrastructure serves over 25,000 consumers across public and private clouds as of June 2024, processing trillions of messages per day. A self-serve UI model is what makes that footprint tractable. New teams use the UI to inspect topics they have read access to, check consumer group health, produce test messages against a staging cluster, and request access to additional topics through a workflow the platform team owns.&lt;/p&gt;
&lt;p&gt;The alternative (a central team handling every read request, every consumer-group inspection, and every offset query) does not scale past a few dozen tenant teams. The UI is the lever that moves the work from the platform team to the tenants without giving them broker-admin access.&lt;/p&gt;
&lt;h2 id=&quot;whats-next-for-kafka-ui-tooling&quot;&gt;What’s next for Kafka UI tooling&lt;/h2&gt;
&lt;p&gt;Three forward-looking themes are worth flagging for anyone making a multi-year decision.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-932 and queues.&lt;/strong&gt; &lt;a href=&quot;/resources/blog-post-kip-932-queues-for-kafka-explained&quot;&gt;KIP-932&lt;/a&gt; introduces queue semantics to Kafka through a new “share group” consumer model. Once shipped broadly, this changes the consumer model in ways that will require UI tooling to expose a new kind of entity alongside topics and traditional consumer groups. UIs that don’t adapt will show an incomplete picture of cluster state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KRaft as the steady state.&lt;/strong&gt; With ZooKeeper fully removed in Kafka 4.0, the metadata model is simpler and more consistent. UIs that previously read ZooKeeper for cluster state now interact exclusively with the Kafka metadata quorum. This opens the door to richer, faster metadata exposure in tooling, and it eliminates a class of stale-metadata bugs that used to surface in ZooKeeper-era UIs. The &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;kafka architecture&lt;/a&gt; article covers the KRaft metadata model in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI assistants and natural-language operators.&lt;/strong&gt; Several commercial roadmaps now include some form of natural-language interaction: ask the UI to find consumer groups that have been lagging for more than an hour, or to summarise the recent topic configuration changes across the production estate. Whether this becomes a meaningful operational interface or stays a demo feature is still open, but it is worth tracking. The credible version of this is one where the UI translates a question into an API call against the same metadata it already surfaces, rather than a chatbot that hallucinates broker IDs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tiered storage visibility.&lt;/strong&gt; As more vendors ship tiered storage (Confluent, Aiven, Redpanda, and the upstream KIP-405 work), the UI needs to surface what data lives on the broker, what has been tiered to object storage, and what the cost implications are. This is a relatively new area, and tools differ significantly in how clearly they present it.&lt;/p&gt;
&lt;h2 id=&quot;choosing-the-right-kafka-ui&quot;&gt;Choosing the right Kafka UI&lt;/h2&gt;
&lt;p&gt;A concise decision framework, summarising the categories above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Small team or personal use:&lt;/strong&gt; Kpow Community Edition or CMAK. Free, easy to deploy, sufficient for one or two clusters with light governance needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitOps-native preference:&lt;/strong&gt; AKHQ. Configuration in version control, OIDC out of the box.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance, audit, or regulated industry:&lt;/strong&gt; Kpow or Conduktor. Full RBAC, audit logging, and identity-provider integration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data engineering or SQL-style exploration:&lt;/strong&gt; Lenses.io. SQL Processors and a global catalog suit data engineering workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Already on Confluent Platform:&lt;/strong&gt; Confluent Control Center. Suits if you are completely committed to the Confluent stack.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise, multi-cluster, multi-tenant:&lt;/strong&gt; Kpow or Conduktor, with the pricing model as the deciding factor. Per-cluster pricing favours broad adoption; per-seat pricing becomes expensive for larger teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“Free” is not the same as “low cost.” Self-hosted open-source tools carry engineering overhead for deployment, version upgrades, security patching, and on-call. For a single-cluster team, that overhead is trivial. For a platform team running a regulated estate across multiple regions, the all-in cost of an open-source tool often exceeds the licence cost of a commercial one.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The right Kafka UI reduces operational overhead, makes security enforceable across teams, and gives operators, developers, and data engineers a meaningful window into cluster behaviour. The wrong choice, or no choice at all, is a form of operational debt that compounds as the cluster grows: every offset reset done by hand, every ACL change applied via CLI, and every consumer-group lag investigation that takes an hour instead of five minutes adds up. The UI is the operational layer that sits above &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;architecture&lt;/a&gt;, &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;monitoring&lt;/a&gt;, and &lt;a href=&quot;/articles/kafka-security-architecture&quot;&gt;security&lt;/a&gt;, and the right one is the one that fits your team’s scale, governance needs, and pricing tolerance.&lt;/p&gt;
&lt;p&gt;At Factor House, we built Kpow for platform and SRE teams running Kafka in regulated, multi-cluster environments. If your evaluation maps to that profile, you can &lt;a href=&quot;/products/kpow&quot;&gt;try Kpow for free for 30 days&lt;/a&gt; against any Kafka cluster, deployed via Docker, Helm, or JAR.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;
&lt;h3 id=&quot;what-is-a-kafka-ui&quot;&gt;What is a Kafka UI?&lt;/h3&gt;
&lt;p&gt;A Kafka UI is a web-based interface for managing and inspecting an Apache Kafka cluster. It exposes topics, partitions, consumer groups, brokers, schemas, and connectors through a browser, removing the need to use the Kafka CLI for routine operational tasks.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-difference-between-a-kafka-ui-and-a-kafka-monitoring-tool&quot;&gt;What is the difference between a Kafka UI and a Kafka monitoring tool?&lt;/h3&gt;
&lt;p&gt;A Kafka UI is for real-time operational interaction: inspecting cluster state, reading messages, and performing actions like offset resets or connector restarts. A Kafka monitoring tool such as Prometheus and Grafana or Datadog is for time-series storage, alerting, SLO tracking, and historical trend analysis. Most production estates use both.&lt;/p&gt;
&lt;h3 id=&quot;which-kafka-ui-tools-are-free-and-open-source&quot;&gt;Which Kafka UI tools are free and open source?&lt;/h3&gt;
&lt;p&gt;The widely-used open-source options are Kafbat UI (Apache 2.0), AKHQ (Apache 2.0), and Redpanda Console (BSL community edition, with enterprise features behind a paid licence). Each is self-hosted and free to download. While not open source, Kpow Community Edition is also a viable option.&lt;/p&gt;
&lt;h3 id=&quot;is-confluent-control-center-free-to-use&quot;&gt;Is Confluent Control Center free to use?&lt;/h3&gt;
&lt;p&gt;No. Confluent Control Center is bundled with Confluent Platform Enterprise and is not available as a standalone free product. Pricing is included in the broader Confluent Platform licence, which typically ranges from around $50,000 to $500,000+ per year depending on cluster footprint and support tier.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-manage-consumer-group-offsets-from-a-kafka-ui&quot;&gt;How do I manage consumer group offsets from a Kafka UI?&lt;/h3&gt;
&lt;p&gt;Most Kafka UIs let you view the current offset and log-end offset for each partition in a consumer group, and reset the offset to the earliest, latest, a specific timestamp, or a specific offset value. The action is typically gated by an RBAC permission and recorded in an audit log. The exact workflow varies by tool but the underlying API is the standard Kafka admin client.&lt;/p&gt;
&lt;h3 id=&quot;can-a-kafka-ui-manage-kafka-connect-connectors&quot;&gt;Can a Kafka UI manage Kafka Connect connectors?&lt;/h3&gt;
&lt;p&gt;Yes, most Kafka UIs integrate with Kafka Connect to list connectors, surface task status, and provide pause, resume, and restart controls. Configuration can usually be inspected and edited from the UI. Redpanda Console is a notable exception: it does not include Connect management.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-secure-a-kafka-ui-in-a-production-environment&quot;&gt;How do I secure a Kafka UI in a production environment?&lt;/h3&gt;
&lt;p&gt;Authenticate users through your identity provider (SAML, OIDC, or LDAP) rather than local users. Enforce RBAC at the topic, consumer-group, and connector level. Bind the UI to an internal network behind your existing identity proxy. Enable audit logging and ship the audit log to a SIEM. Ensure the UI uses a service account with the minimum broker permissions it actually needs.&lt;/p&gt;
&lt;h3 id=&quot;which-kafka-ui-tools-support-rbac-and-sso&quot;&gt;Which Kafka UI tools support RBAC and SSO?&lt;/h3&gt;
&lt;p&gt;Full RBAC with SSO is offered by Kpow, Conduktor, Lenses.io, and Confluent Control Center. AKHQ supports SSO and basic RBAC since version 0.25. Kafbat UI has basic YAML-based RBAC. Redpanda Console requires the enterprise licence for SSO and RBAC.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-best-kafka-ui-for-a-small-team-versus-an-enterprise-deployment&quot;&gt;What is the best Kafka UI for a small team versus an enterprise deployment?&lt;/h3&gt;
&lt;p&gt;For a small team running one or two clusters, an open-source tool such as Kpow Community Edition, Kafbat UI or AKHQ usually covers the operational needs at zero licence cost. For an enterprise deployment with multiple clusters, regulated workloads, and many tenant teams, a commercial tool with full RBAC, audit logging, and vendor support (Kpow, Conduktor, Lenses.io, or Confluent Control Center) tends to be a better fit.&lt;/p&gt;
&lt;h3 id=&quot;can-a-kafka-ui-manage-multiple-clusters-from-one-interface&quot;&gt;Can a Kafka UI manage multiple clusters from one interface?&lt;/h3&gt;
&lt;p&gt;Yes. Kpow, Conduktor, Lenses.io, AKHQ, Kafbat UI, and Confluent Control Center all support multi-cluster management from a single instance. Cluster switching, unified search, and environment-aware RBAC vary in maturity, and Redpanda Console’s multi-cluster support requires the enterprise licence.&lt;/p&gt;
&lt;h3 id=&quot;does-kafka-ui-work-with-kraft-mode-kafka-4x&quot;&gt;Does Kafka UI work with KRaft mode (Kafka 4.x)?&lt;/h3&gt;
&lt;p&gt;The actively maintained Kafka UIs (Kpow, Kafbat UI, AKHQ, Conduktor, Lenses.io, Redpanda Console, Confluent Control Center) all support KRaft mode. With Kafka 4.0 having fully removed ZooKeeper as of March 2025, KRaft is the only supported metadata mode going forward, and any UI that still depends on ZooKeeper APIs has a gap to close.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-difference-between-kafka-ui-and-kafbat-ui&quot;&gt;What is the difference between Kafka UI and Kafbat UI?&lt;/h3&gt;
&lt;p&gt;“Kafka UI” was the original project name for an open-source Kafka management interface formerly maintained on GitHub by Provectus. The project was forked and is now actively maintained as Kafbat UI by the same core community of maintainers. If you see references to “kafka-ui” in older documentation, they almost always refer to what is now Kafbat UI.&lt;/p&gt;
&lt;h3 id=&quot;how-does-a-kafka-ui-handle-schema-registry-and-avro-or-protobuf-messages&quot;&gt;How does a Kafka UI handle schema registry and Avro or Protobuf messages?&lt;/h3&gt;
&lt;p&gt;A Kafka UI with Schema Registry integration resolves the schema for a message by reading the schema ID from the message header, fetching the schema definition from the registry, and using it to deserialise the payload into a human-readable form. Avro, Protobuf, and JSON Schema are supported by the major tools. The UI typically also exposes subject management, version history, and compatibility-rule inspection.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Chad Harris</author></item><item><title>Dead letter queues in Kafka: patterns and pitfalls</title><link>https://factorhouse.io/articles/dead-letter-queues-kafka/</link><guid isPermaLink="true">https://factorhouse.io/articles/dead-letter-queues-kafka/</guid><description>How to implement a dead letter queue in Apache Kafka, with Spring Kafka, Connect, and Streams examples, and the production failure modes to avoid.</description><pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A single malformed record can block an entire Kafka partition. When your consumer encounters a message it cannot deserialize - a corrupted Avro payload, a JSON field with an unexpected type, a schema version that was deprecated months ago - it throws an exception, retries, and throws again. Consumer group lag climbs. Every healthy message behind that one record waits. This is a poison pill, and the standard solution is a dead letter queue.&lt;/p&gt;
&lt;p&gt;Kafka has no native dead letter queue primitive outside of Kafka Connect. The broker has no concept of a failed message; failure is entirely a consumer-side concern. As a result, the DLQ is a pattern you implement in your consumer application, your Connect connector configuration, or your Kafka Streams topology. This article covers all three paths with working examples, explains the retry-topic pattern that should precede any DLQ, and walks through the production failure modes that are frequently omitted from shorter treatments of the topic.&lt;/p&gt;
&lt;p&gt;By the end, you should be able to implement a DLQ in your own stack and know which failure modes to test for before shipping to production.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafka has no native dead letter queue outside of Kafka Connect. In all other cases, the DLQ pattern is implemented at the consumer application, Kafka Connect connector, or Kafka Streams topology layer.&lt;/li&gt;
&lt;li&gt;The production-grade shape is retry-topic plus DLQ: transient failures should cycle through a bounded retry pass before a message is parked, keeping the DLQ as a terminal state rather than another retry tier.&lt;/li&gt;
&lt;li&gt;Spring Kafka, Kafka Connect, and Kafka Streams each handle the DLQ differently. Connect is the only path that requires no custom code; Kafka Streams requires the most.&lt;/li&gt;
&lt;li&gt;The failure modes that most often break a DLQ in production are ordering loss on replay, schema mismatches in the DLQ topic itself, infinite retry loops between the source and retry topics, and side effects being re-applied during blind replay.&lt;/li&gt;
&lt;li&gt;Monitoring the age of the oldest unprocessed DLQ record is more actionable than monitoring topic depth alone, because it is independent of traffic volume.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;, the Kafka UI, includes bulk actions for managing dead letter queues, letting you clone and replay records from the DLQ to a retry or source topic with RBAC controls and a full audit trail.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-a-dead-letter-queue-and-why-doesnt-kafka-have-one&quot;&gt;What is a dead letter queue, and why doesn’t Kafka have one?&lt;/h2&gt;
&lt;p&gt;In message brokers like RabbitMQ and AWS SQS, a dead letter queue is a broker-level feature. When a message exceeds its delivery attempts or cannot be routed, the broker moves it to a designated DLQ. Application code does not need to handle the routing.&lt;/p&gt;
&lt;p&gt;Kafka’s architecture works differently. The broker stores ordered byte sequences in partitioned logs and advances consumer offsets when the consumer commits them. Whether a message is valid or processable is a question the consumer answers, not the broker. If a consumer determines a message cannot be processed, it is the consumer’s responsibility to publish that record elsewhere and commit the source offset so the partition can advance.&lt;/p&gt;
&lt;p&gt;This means the DLQ pattern in Kafka is implemented at one of three layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the consumer application, using a framework like Spring Kafka with &lt;code&gt;DefaultErrorHandler&lt;/code&gt; and &lt;code&gt;DeadLetterPublishingRecoverer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In a Kafka Connect connector, using the native &lt;code&gt;errors.deadletterqueue.*&lt;/code&gt; configuration&lt;/li&gt;
&lt;li&gt;In a Kafka Streams topology, using custom &lt;code&gt;DeserializationExceptionHandler&lt;/code&gt; and &lt;code&gt;ProductionExceptionHandler&lt;/code&gt; implementations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each path covers a different failure surface and has different operational trade-offs.&lt;/p&gt;
&lt;h2 id=&quot;when-you-actually-need-a-dlq&quot;&gt;When you actually need a DLQ&lt;/h2&gt;
&lt;p&gt;Not every processing failure belongs in a DLQ. The pattern is appropriate in three situations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deserialization failures.&lt;/strong&gt; A message arrives with an incompatible Avro schema, a field with the wrong type, or corrupted bytes. The consumer cannot read the record at all. No amount of retrying will fix a malformed message, so it needs to be parked for inspection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Business-rule rejections.&lt;/strong&gt; The message is well-formed and deserializes correctly, but the application logic cannot handle it: a referenced entity no longer exists, a required downstream system returned a permanent error, or a validation rule fails unconditionally. These are not transient failures. The application has determined the message cannot be processed as received.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Retry budget exhaustion.&lt;/strong&gt; A message fails due to a transient cause - a downstream API timeout, a temporary database outage - but the consumer has retried it enough times that continuing to retry would be more disruptive than parking it. Once the retry budget is exhausted, the message should move to the DLQ rather than blocking the consumer.&lt;/p&gt;
&lt;p&gt;Transient failures that are likely to resolve on their own should go through a retry topic first, not directly to the DLQ. The DLQ should be the terminal state: messages there require human or automated intervention to assess and replay.&lt;/p&gt;
&lt;h2 id=&quot;the-retry-topic-plus-dlq-pattern&quot;&gt;The retry-topic plus DLQ pattern&lt;/h2&gt;
&lt;p&gt;The production-grade shape for Kafka consumer error handling combines a retry-topic tier with a terminal DLQ, rather than routing directly to the DLQ on first failure.&lt;/p&gt;
&lt;p&gt;The flow works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A message fails processing on the main consumer.&lt;/li&gt;
&lt;li&gt;If the failure is transient, the consumer publishes the message to a retry topic (preserving the original key) and commits the source offset. The original consumer continues to the next record.&lt;/li&gt;
&lt;li&gt;A retry-topic consumer picks the message up after a delay. If it fails again, it either cascades to a second retry topic with a longer delay, or moves to the DLQ once the retry budget is exhausted.&lt;/li&gt;
&lt;li&gt;If the failure is immediately classified as non-transient (deserialization failure, schema mismatch), the consumer skips the retry tier entirely and publishes directly to the DLQ.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber’s Insurance Engineering&lt;/a&gt; team documented a tiered approach of this kind in their engineering post on reliable reprocessing by Ning Xia and Phani Marupaka. Their implementation uses one Kafka topic per retry level (&lt;code&gt;payments.retry-1&lt;/code&gt;, &lt;code&gt;payments.retry-2&lt;/code&gt;, and so on), each with a consumer that applies a processing delay before re-attempting. Messages cascade through these levels before reaching the final DLQ topic. Their framing is worth adopting: consumer success means reaching a conclusive outcome for every message, either processed or parked, never silently dropped.&lt;/p&gt;
&lt;p&gt;Two distinctions are worth being precise about.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;retry topic&lt;/strong&gt; is for transient failures: the message may succeed on a second or third attempt once the transient condition resolves. A &lt;strong&gt;DLQ&lt;/strong&gt; is the terminal park: the message has exhausted its remediation options and requires intervention before it can be processed. Treating the DLQ as another retry tier by routing messages back to the source automatically after parking them creates infinite loops, which is covered in the production failure modes section below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blocking versus non-blocking retry&lt;/strong&gt; is a related choice. Blocking retry stalls the consumer on the failed message until it succeeds or a timeout elapses. This preserves strict per-partition ordering but prevents progress on the partition while the retry is running. Non-blocking retry publishes the message to a retry topic immediately and continues processing, trading per-key ordering for throughput. For most production workloads, non-blocking retry is the better default, with the exception of cases where strict per-key ordering is a hard requirement of the downstream system.&lt;/p&gt;
&lt;h2 id=&quot;implementing-a-dlq-with-spring-kafka&quot;&gt;Implementing a DLQ with Spring Kafka&lt;/h2&gt;
&lt;p&gt;Spring Kafka’s &lt;code&gt;DefaultErrorHandler&lt;/code&gt; combined with &lt;code&gt;DeadLetterPublishingRecoverer&lt;/code&gt; is the most common implementation path for JVM-based consumers. Here is a working configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;Configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; KafkaErrorHandlingConfig&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;Bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; DefaultErrorHandler &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;errorHandler&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(KafkaTemplate&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;kafkaTemplate&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        // Routes failed records to &amp;#x3C;source-topic&gt;.DLT, preserving the source partition.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        DeadLetterPublishingRecoverer recoverer &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            new&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; DeadLetterPublishingRecoverer&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(kafkaTemplate);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        // Retry up to 3 times at 1-second intervals before invoking the recoverer.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        FixedBackOff backOff &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; FixedBackOff&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1000L&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3L&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        DefaultErrorHandler handler &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; DefaultErrorHandler&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(recoverer, backOff);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        // These exception types bypass retries entirely and go straight to the DLT.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        handler.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;addNotRetryableExceptions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            DeserializationException.class,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            ValidationException.class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; handler;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, &lt;code&gt;DeadLetterPublishingRecoverer&lt;/code&gt; routes to a topic named &lt;code&gt;&amp;lt;source-topic&amp;gt;.DLT&lt;/code&gt; and preserves the source partition number. Your DLT topic must have at least as many partitions as the source topic, and the original message key is retained. Both conditions are necessary for replay ordering to work correctly.&lt;/p&gt;
&lt;p&gt;One implementation detail worth understanding: the recoverer publishes the failed record to the DLT before the consumer commits the offset on the source topic. This is the at-least-once guarantee in practice. If the consumer crashes after publishing to the DLT but before committing, the source message will be reprocessed on restart, potentially resulting in a duplicate DLT entry. If you are writing to a DLT from a transactional consumer, configure &lt;code&gt;enable.idempotence=true&lt;/code&gt; on the DLT producer to guard against this.&lt;/p&gt;
&lt;p&gt;Spring Kafka adds a set of diagnostic headers to every DLT record:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HeaderContents&lt;/strong&gt;&lt;code&gt;kafka_dlt-original-topic&lt;/code&gt;Source topic name&lt;code&gt;kafka_dlt-original-partition&lt;/code&gt;Partition the record came from&lt;code&gt;kafka_dlt-original-offset&lt;/code&gt;Offset within that partition&lt;code&gt;kafka_dlt-exception-message&lt;/code&gt;Exception message as a string&lt;code&gt;kafka_dlt-exception-fqcn&lt;/code&gt;Fully qualified exception class name&lt;/p&gt;
&lt;p&gt;These headers are what allow you to locate the original record, understand what failed, and replay it correctly. Without them, a DLT is a topic full of byte arrays with no diagnostic context.&lt;/p&gt;
&lt;h2 id=&quot;dlq-in-kafka-connect&quot;&gt;DLQ in Kafka Connect&lt;/h2&gt;
&lt;p&gt;Kafka Connect has first-class DLQ support through three connector properties:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;properties&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;errors.tolerance&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=all&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;errors.deadletterqueue.topic.name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=my-connector.dlq&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;errors.deadletterqueue.context.headers.enable&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;errors.deadletterqueue.topic.replication.factor&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting &lt;code&gt;errors.tolerance=all&lt;/code&gt; without specifying a &lt;code&gt;deadletterqueue.topic.name&lt;/code&gt; causes Connect to silently skip failed records. Always pair &lt;code&gt;errors.tolerance=all&lt;/code&gt; with an explicit DLQ topic name to ensure failed records are routed rather than dropped.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;errors.deadletterqueue.context.headers.enable=true&lt;/code&gt;, Connect adds headers to each DLQ record describing the failure: the exception class, the stack trace, the source connector name, and the source topic, partition, and offset. Robin Moffatt’s &lt;a href=&quot;https://www.confluent.io/blog/kafka-connect-deep-dive-error-handling-dead-letter-queues/&quot;&gt;deep-dive on Kafka Connect error handling&lt;/a&gt; covers a useful recovery pattern: chain a second sink connector that reads from the DLQ topic and attempts an alternative deserialization strategy, for example falling back to raw JSON if the primary Avro conversion fails.&lt;/p&gt;
&lt;p&gt;There is a significant limitation that is frequently misunderstood: Connect’s DLQ catches converter and transform errors only, not task-level errors. When a converter throws during deserialization or a Single Message Transform fails, the record goes to the DLQ. When a Kafka Connect task itself fails due to a misconfigured sink, an unreachable endpoint, or an unhandled exception in the task code, the task halts entirely and no records are routed to the DLQ. Task-level failures require a different remediation path: investigate the task logs, fix the root cause, and restart the connector task.&lt;/p&gt;
&lt;h2 id=&quot;dlq-in-kafka-streams&quot;&gt;DLQ in Kafka Streams&lt;/h2&gt;
&lt;p&gt;Kafka Streams routes failed records through two exception handler interfaces that address different failure surfaces.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DeserializationExceptionHandler&lt;/code&gt; fires when a record cannot be deserialized in the source topology. The built-in &lt;code&gt;LogAndContinueExceptionHandler&lt;/code&gt; logs a warning and drops the record. This is not a DLQ. The record is gone. If your topology is using &lt;code&gt;LogAndContinueExceptionHandler&lt;/code&gt; without a custom handler alongside it, you may be losing records without knowing it. A custom handler that routes to a DLQ topic looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; DlqDeserializationHandler&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; DeserializationExceptionHandler&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; String DLQ_TOPIC &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;my-app.deserialization-errors&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Producer&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[], &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[]&gt; dlqProducer;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;Override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; DeserializationHandlerResponse &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            ProcessorContext &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            ConsumerRecord&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[], &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[]&gt; &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;record&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            Exception &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;exception&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        Headers headers &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; RecordHeaders&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;headers&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        headers.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dlq-source-topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;topic&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;getBytes&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(UTF_8));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        headers.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dlq-source-partition&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            ByteBuffer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;allocate&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;putInt&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;partition&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;()).&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        headers.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dlq-source-offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            ByteBuffer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;allocate&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;putLong&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;offset&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;()).&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        headers.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dlq-exception-class&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            exception.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;getClass&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;getName&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;getBytes&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(UTF_8));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        dlqProducer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ProducerRecord&amp;#x3C;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            DLQ_TOPIC, record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;partition&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(), record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(), headers));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; DeserializationHandlerResponse.CONTINUE;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register the handler in your Streams configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;props.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    DlqDeserializationHandler.class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ProductionExceptionHandler&lt;/code&gt; covers exceptions that occur when Kafka Streams attempts to write output records: serialization failures on the output side, or produce errors to destination topics. A custom &lt;code&gt;ProductionExceptionHandler&lt;/code&gt; can route these to a separate DLQ topic using the same pattern.&lt;/p&gt;
&lt;h2 id=&quot;comparing-the-three-implementation-paths&quot;&gt;Comparing the three implementation paths&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Spring Kafka&lt;/th&gt;
&lt;th&gt;Kafka Connect&lt;/th&gt;
&lt;th&gt;Kafka Streams&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Catches deserialization errors&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (custom handler required)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catches business-logic errors&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (ProductionExceptionHandler)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catches task/topology-level errors&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built-in retry before DLQ&lt;/td&gt;
&lt;td&gt;Yes (BackOff config)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error context headers&lt;/td&gt;
&lt;td&gt;Yes (built-in)&lt;/td&gt;
&lt;td&gt;Yes (built-in)&lt;/td&gt;
&lt;td&gt;Custom implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires custom code&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preserves original message key&lt;/td&gt;
&lt;td&gt;Yes (default)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (if implemented)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;the-mistakes-that-break-a-dlq-in-production&quot;&gt;The mistakes that break a DLQ in production&lt;/h2&gt;
&lt;h3 id=&quot;losing-ordering-on-replay&quot;&gt;Losing ordering on replay&lt;/h3&gt;
&lt;p&gt;When you publish to a DLQ topic, the key you use determines which partition the record lands on. If you change the key or publish with no key, Kafka assigns the record to a partition based on the new (or null) key. When you later replay from the DLQ back to the source topic, the record arrives on a different partition than the original, which breaks per-key ordering guarantees for any downstream consumer that depends on them.&lt;/p&gt;
&lt;p&gt;The correct approach is to preserve the original message key when writing to the DLQ. Spring Kafka’s &lt;code&gt;DeadLetterPublishingRecoverer&lt;/code&gt; does this by default. In custom implementations, explicitly copy the original key. For replay to maintain ordering correctly, your DLQ topic should have the same partition count as the source topic, or you should have a deliberate strategy for re-keying on replay and an understanding of the ordering consequences.&lt;/p&gt;
&lt;h3 id=&quot;schema-mismatch-on-the-dlq-itself&quot;&gt;Schema mismatch on the DLQ itself&lt;/h3&gt;
&lt;p&gt;One common reason a message ends up in a DLQ is a schema mismatch: the consumer cannot deserialize the record against the expected Schema Registry subject. If your DLQ topic is configured to use the same Schema Registry subject as the source topic, you have a circular problem: you still cannot deserialize the record in order to write it to the DLQ, because the deserialization failure is precisely what you are trying to park.&lt;/p&gt;
&lt;p&gt;The solution is to write raw bytes to the DLQ rather than using Schema Registry-managed serialization for the DLQ value. Store failure context as record headers: the original schema ID from the Confluent wire format prefix, the source topic and offset, and the exception type. When a reader later inspects the DLQ, the headers tell it what schema was expected. As the source topic evolves through new schema versions, a raw-bytes DLQ remains readable regardless of schema state.&lt;/p&gt;
&lt;h3 id=&quot;infinite-retry-loops-between-source-and-retry-topic&quot;&gt;Infinite retry loops between source and retry topic&lt;/h3&gt;
&lt;p&gt;If your retry-topic consumer routes exhausted messages back to the source topic, and the source consumer routes failed messages back to the retry topic, you have a loop. Both hops may seem individually reasonable, but together they create a circuit that can spin indefinitely on a message that will never succeed.&lt;/p&gt;
&lt;p&gt;The mitigation is a retry-count header. Before publishing a message to a retry topic, read the current retry count from the headers, increment it, and write it back. When the count reaches the retry budget, route to the DLQ instead of the retry topic. Spring Kafka’s &lt;code&gt;DefaultErrorHandler&lt;/code&gt; manages this internally. In custom implementations, you need to track it explicitly and test that the budget is actually enforced before deploying to production.&lt;/p&gt;
&lt;h3 id=&quot;idempotency-on-replay&quot;&gt;Idempotency on replay&lt;/h3&gt;
&lt;p&gt;Replaying a record from the DLQ re-runs your consumer logic. If your consumer executed side effects before the failure occurred - inserting a database row, sending an external notification, initiating a payment - replaying the record will execute those side effects again. Blind replay is safe only for consumers whose operations are idempotent.&lt;/p&gt;
&lt;p&gt;Before building replay tooling, audit your consumer for side effects that cannot be safely re-applied. Non-idempotent operations need either a deduplication check keyed on the original offset and topic (available in the DLQ record headers) or a transactional pattern that ties the side effect to the Kafka offset commit.&lt;/p&gt;
&lt;h3 id=&quot;silent-failure-when-the-dlq-producer-itself-fails&quot;&gt;Silent failure when the DLQ producer itself fails&lt;/h3&gt;
&lt;p&gt;If the DLQ producer cannot connect, times out, or the DLQ topic does not exist, your consumer needs an explicit policy. The options are: pause and alert without committing the offset, commit the offset and accept the loss, or crash the consumer process.&lt;/p&gt;
&lt;p&gt;Committing the offset when the DLQ write has failed means the record is gone permanently. For most applications, this is the wrong trade-off. The better default is to not commit, allowing the consumer to restart and reprocess from the last committed offset, combined with an alert that fires when DLQ write failures begin occurring. This requires that your consumer is idempotent for records it may reprocess, which is another reason to audit for idempotency before the first production incident.&lt;/p&gt;
&lt;h2 id=&quot;monitoring-and-replaying-the-dlq&quot;&gt;Monitoring and replaying the DLQ&lt;/h2&gt;
&lt;p&gt;Two metrics provide meaningful signal for a DLQ in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag on the DLQ topic&lt;/strong&gt; (topic depth) tells you how many records have been parked and not yet consumed by a replay consumer. An increasing depth signals upstream failures. A depth of zero means the DLQ is clear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Age of the oldest unprocessed record&lt;/strong&gt; is more operationally useful than depth alone. A DLQ with 200 records that arrived in the last 10 minutes is a different situation from 200 records sitting there for 8 hours. Most monitoring systems expose the timestamp of the earliest unread record in a topic. An alert that fires when any record has been in the DLQ for more than a configured time threshold is more actionable than a pure depth threshold, because it is independent of traffic volume. It stays relevant at both low and high throughput, and it fires whether lag accumulated slowly or all at once.&lt;/p&gt;
&lt;p&gt;For replay, the most common approaches are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kcat (formerly kafkacat):&lt;/strong&gt; consume from the DLQ topic and produce to the source or retry topic via stdin/stdout pipe. Sufficient for small-volume one-off replays.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka MirrorMaker 2:&lt;/strong&gt; can mirror a DLQ topic to another cluster or reroute it to a different topic, with filtering via record header predicates. Higher operational overhead for straightforward cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom replay service:&lt;/strong&gt; a dedicated consumer that reads from the DLQ and produces to the source, optionally with a transform step to correct invalid records before re-submission. Most production teams build some version of this eventually.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Off-the-shelf replay tooling for Kafka is thinner than what exists in RabbitMQ or SQS ecosystems, and most teams end up scripting or building their own.&lt;/p&gt;
&lt;p&gt;A related architectural choice is whether to maintain one DLQ topic per source topic or a single shared DLQ. One-per-source makes depth monitoring unambiguous and simplifies replay routing: you always know which source the records came from, and a depth alert on &lt;code&gt;payments.DLT&lt;/code&gt; points directly at the payments consumer. A shared DLQ is simpler to operate at small scale but becomes harder to attribute as the number of upstream consumers grows, since topic depth is no longer associated with a single system. The replay consumer for a shared DLQ also needs to consult the source topic header on each record to determine where to route it.&lt;/p&gt;
&lt;h2 id=&quot;managing-kafka-dlqin-kpow&quot;&gt;Managing Kafka DLQ in Kpow&lt;/h2&gt;
&lt;p&gt;Kafka has no native DLQ outside of Kafka Connect. In all other cases, the pattern is implemented at the application or framework level, and its quality reflects the care taken in the implementation.&lt;/p&gt;
&lt;p&gt;The production-grade shape is retry-topic plus DLQ: transient failures should have a bounded retry pass before a message is parked, and the DLQ itself should be the terminal state rather than another retry tier. The operational surface - monitoring for record age, alerting promptly, and replaying records with confidence about idempotency - is what separates a DLQ that exists from a DLQ that is useful during an incident.&lt;/p&gt;
&lt;p&gt;If you want a UI for inspecting DLQ topics, monitoring record age, and replaying records back to a source or retry topic with RBAC controls and a full audit trail, take a look at &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;/articles/clone-to-topic-for-kafka-dlq&quot;&gt;Clone to Topic feature&lt;/a&gt; introduced in Kpow 96.2 performs a byte-level clone of DLQ records to any permitted topic directly from the Data Inspect interface, with per-topic RBAC policies restricting which topics can serve as clone sources and destinations, and a complete audit log of every replay operation. You can try it free for 30 days against any Kafka cluster.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Best Kafka management tools for 2026</title><link>https://factorhouse.io/articles/best-kafka-management-tools/</link><guid isPermaLink="true">https://factorhouse.io/articles/best-kafka-management-tools/</guid><description>Compare the 10 best Kafka management tools for 2026, including Kpow, AKHQ, Conduktor, and Confluent Control Center. Covers pricing, RBAC, and deployment requirements.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apache Kafka ships with a CLI and nothing else. Every web UI, observability layer, and governance workflow your team relies on is a third-party project built on top of it. That makes the choice of management tool consequential: it shapes how quickly engineers can debug a stuck consumer group, how cleanly you can handle a PII audit, and whether your platform team spends its time on actual infrastructure rather than running kafka-consumer-groups.sh on behalf of every application team.&lt;/p&gt;
&lt;p&gt;This article covers the tools that appear most consistently on real-world Kafka shortlists in 2026, with honest assessments of where each one fits and where it falls short. For a focused comparison of web interfaces specifically, see our &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Best Kafka UI&lt;/a&gt; roundup.&lt;/p&gt;
&lt;h2 id=&quot;10-best-kafka-management-tools&quot;&gt;&lt;strong&gt;10 best Kafka management tools&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The table below summarises the tools covered in this article. Scores reflect the current state of each product as of mid-2026.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Multi-cluster&lt;/th&gt;
&lt;th&gt;RBAC/Security&lt;/th&gt;
&lt;th&gt;Active?&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kpow&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Full (SAML/OIDC/LDAP)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;$3,950/cluster/yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Control Center&lt;/td&gt;
&lt;td&gt;Commercial (bundled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Full (MDS)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conduktor&lt;/td&gt;
&lt;td&gt;Commercial / open-core&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;From ~$80k/yr (100 seats, 3 clusters)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafbat UI&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Console&lt;/td&gt;
&lt;td&gt;Open-core&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Enterprise licence only&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Free (community); contact sales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lenses HQ&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;From $4,000/yr (1 cluster, 15 users)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafdrop&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offset Explorer&lt;/td&gt;
&lt;td&gt;Commercial (desktop)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;$99/user/yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CMAK&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;1-kpow-by-factor-house&quot;&gt;&lt;strong&gt;1. Kpow by Factor House&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722460e8a935845facc77_kpow-blog-screenshot.avif&quot; alt=&quot;Kpow Kafka UI&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Commercial (proprietary, self-hosted container). Community Edition available for non-production use.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a single stateless JVM container that connects to any Kafka cluster, whether that’s self-managed, MSK, Confluent Cloud, Redpanda, Aiven, or Instaclustr, and presents a unified UI and REST API covering topics, consumer groups, Kafka Connect, Schema Registry, ksqlDB, ACLs, and broker configuration. One instance manages up to 12 clusters. Telemetry is stored in internal Kafka topics on the monitored cluster itself, so there are no external databases, sidecars, or additional infrastructure to manage. The current release is v95.5.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SRE and platform teams running multi-cluster Kafka in regulated environments where audit logging, &lt;a href=&quot;/articles/best-practices-kafka-data-observability&quot;&gt;server-side data masking&lt;/a&gt;, and air-gapped deployment are required. Kpow is the primary recommendation for fintech, healthcare, and public sector teams.&lt;/p&gt;
&lt;h3 id=&quot;strengths&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Deployment simplicity.&lt;/strong&gt; A single stateless container, configurable entirely via environment variables. No Postgres, no RocksDB, no persistent volumes to worry about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security coverage.&lt;/strong&gt; &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC&lt;/a&gt;, multi-tenancy, SAML/OIDC/LDAP/Keycloak SSO, and server-side data masking with a streamed audit log that ships to any SIEM. This is where most open-source alternatives stop short.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kJQ search.&lt;/strong&gt; A JQ-like predicate language for filtering messages across millions of records directly in the UI, without exporting data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Streams topology visualisation.&lt;/strong&gt; Native, rendered in-product. Most other tools in this list do not offer this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pricing transparency.&lt;/strong&gt; Kpow pricing is publicly available, and scales based on the number of clusters, not per user. Licenses can be purchased from Factor House, or through the AWS Marketplace on an annual or pay-as-you-go basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accessibility.&lt;/strong&gt; WCAG 2.1 AA compliant, per Factor House’s documentation.&lt;/p&gt;
&lt;h3 id=&quot;limitations&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No managed SaaS option. You are responsible for running the container.&lt;/p&gt;
&lt;h3 id=&quot;setup-and-maintenance&quot;&gt;&lt;strong&gt;Setup and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kpow runs as a Docker container, deployable via Docker Compose, Helm, CloudFormation, or AWS Marketplace. Configuration is handled through environment variables. Because there is no external state store, upgrades are a container swap.&lt;/p&gt;
&lt;h3 id=&quot;pricing&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Pricing is per cluster, starting at $4,500 USD per year (annual). Also available to purchase on the &lt;a href=&quot;https://aws.amazon.com/marketplace/seller-profile?id=ab356f1d-3394-4523-b5d4-b339e3cca9e0&quot;&gt;AWS Marketplace&lt;/a&gt;. Users are not metered, so the cost for 3 clusters with 100 users is $13,500/year, versus per-seat models that scale with headcount.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;/products/kpow&quot;&gt;&lt;strong&gt;free 30-day Enterprise trial&lt;/strong&gt;&lt;/a&gt; is available.&lt;/p&gt;
&lt;h2 id=&quot;2-confluent-control-center&quot;&gt;&lt;strong&gt;2. Confluent Control Center&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722a00e8a935845fad116_confluent-control-center-blog-screenshot.avif&quot; alt=&quot;Confluent Control Center&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Commercial, bundled with Confluent Platform Enterprise.&lt;/p&gt;
&lt;p&gt;Confluent’s first-party management UI. &lt;a href=&quot;/articles/confluent-control-center&quot;&gt;Control Center&lt;/a&gt; 2.0, shipped in 2025, replaced the dedicated metrics Kafka cluster with Prometheus for metrics ingestion, reducing startup time and raising partition limits. It covers the full Confluent stack: Schema Registry, Connect, ksqlDB, Cluster Linking, Flink (preview), and Confluent’s MDS-based RBAC.&lt;/p&gt;
&lt;h3 id=&quot;best-for-1&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams already running Confluent Platform Enterprise. Outside that context, Control Center does not make sense to evaluate.&lt;/p&gt;
&lt;h3 id=&quot;strengths-1&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Deep integration with the Confluent stack. Strongest monitoring telemetry for Confluent-native deployments. Control Center 2.0 significantly improved startup performance over prior versions.&lt;/p&gt;
&lt;h3 id=&quot;limitations-1&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Requires a Confluent Platform Enterprise licence. Many advanced features depend on Confluent’s RBAC stack and do not work with vanilla Kafka security. Not a tool you deploy against a third-party cluster. Pricing for Confluent Platform ranges from $50,000 to $500,000+ per year depending on cluster size.&lt;/p&gt;
&lt;h3 id=&quot;pricing-1&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Bundled with Confluent Platform. Not separately purchasable.&lt;/p&gt;
&lt;h2 id=&quot;3-akhq&quot;&gt;&lt;strong&gt;3. AKHQ&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;AKHQ&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Open-source (Apache 2.0).&lt;/p&gt;
&lt;p&gt;A Micronaut-based web UI by Ludovic Dehon. &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; is the most established free option for production Kafka management, with a GitOps-first configuration model where connections, users, groups, and Schema Registry links are defined in YAML and deployed via Helm.&lt;/p&gt;
&lt;h3 id=&quot;best-for-2&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Engineering teams that want a free, GitOps-native interface and have no server-side data masking requirements.&lt;/p&gt;
&lt;h3 id=&quot;strengths-2&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Multi-cluster management, OIDC/OAuth2/LDAP/GitHub SSO, Connect and Schema Registry integration (Avro/Protobuf/JSON), basic RBAC since v0.25, ksqlDB support. Mature enough for compliance-conscious teams as long as masking is handled upstream.&lt;/p&gt;
&lt;h3 id=&quot;limitations-2&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No native data masking. If HIPAA, PCI-DSS, or GDPR compliance requires server-side masking of topic data, AKHQ cannot provide it. Audit logging depends entirely on your auth provider. UI performance under heavy load is a known issue. Latest release: v0.27.0 (March 2025).&lt;/p&gt;
&lt;h3 id=&quot;pricing-2&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free. Realistic TCO is 3-10 engineer-days for setup, plus ongoing maintenance.&lt;/p&gt;
&lt;h2 id=&quot;4-conduktor&quot;&gt;&lt;strong&gt;4. Conduktor&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722b1f17be485095adbee_conduktor-blog-screenshot.avif&quot; alt=&quot;Conduktor&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Commercial. Pricing per-seat with a 50-seat minimum.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt; is a two-product platform: Console (web UI for operations, monitoring, and governance) and Gateway (a proxy for traffic control, encryption, data masking, and partner data sharing). It has the most complete governance layer in the market for multi-team Kafka, with topic and application ownership models, self-service workflows with approval gates, and chargeback.&lt;/p&gt;
&lt;h3 id=&quot;best-for-3&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Large organisations where multiple product teams share Kafka infrastructure and need ownership, self-service workflows, and auditability across team boundaries.&lt;/p&gt;
&lt;h3 id=&quot;strengths-3&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Ownership tracking, topic catalogues, self-service request workflows, chargeback (GA since April 2025), Partner Zones for external data sharing. The Community tier is usable for small teams.&lt;/p&gt;
&lt;h3 id=&quot;limitations-3&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Per-seat pricing scales quickly. For 100 users across 3 clusters, estimated list price is $80,000-$150,000 per year, well above Kpow’s per-cluster pricing at the same scale. Gateway adds additional infrastructure complexity. Pricing requires a sales conversation beyond the Community tier.&lt;/p&gt;
&lt;h3 id=&quot;pricing-3&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Console Community: free (up to 50 users, 3 clusters). Console Scale: per-seat, 50-seat minimum, pricing via Conduktor sales.&lt;/p&gt;
&lt;h2 id=&quot;5-kafbat-ui-formerly-provectus-kafka-ui&quot;&gt;&lt;strong&gt;5. Kafbat UI (formerly Provectus kafka-ui)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c72279c93efb0be1f59126_kafbat-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Open-source (Apache 2.0).&lt;/p&gt;
&lt;p&gt;When Provectus paused development on provectus/kafka-ui in September 2023, the original maintainers forked it as kafbat/kafka-ui. The Kafbat fork is now the active line. If your environment is still pulling provectuslabs/kafka-ui Docker images, switch immediately: the original repo carried CVE-2023-52251, an RCE that took roughly 4.5 months to patch after initial disclosure.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat UI&lt;/a&gt; is the most modern-looking open-source Kafka UI currently available, with a clean interface and active release cadence. Latest release: v1.3.0 (July 2025).&lt;/p&gt;
&lt;h3 id=&quot;best-for-4&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Small to mid-size teams that want a clean, modern interface and can tolerate basic RBAC and client-side-only data masking.&lt;/p&gt;
&lt;h3 id=&quot;strengths-4&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Multi-cluster support, Avro/Protobuf/JSON deserialization, Schema Registry and Connect integration, CEL-based message filtering (replacing the Groovy filters that caused the RCE), YAML-based RBAC, MCP support added in v1.3.0.&lt;/p&gt;
&lt;h3 id=&quot;limitations-4&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Volunteer-maintained with no commercial backing or SLA. RBAC is basic, with no team or namespace ownership model. Data masking is regex-based and client-side, which means it can be bypassed. Not suitable for environments with server-side masking requirements.&lt;/p&gt;
&lt;h3 id=&quot;pricing-4&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free.&lt;/p&gt;
&lt;h2 id=&quot;6-redpanda-console&quot;&gt;&lt;strong&gt;6. Redpanda Console&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7228f5010387c137d9bf0_redpanda-console-blog-screenshot.avif&quot; alt=&quot;Redpanda Console&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Open-core. Community edition under BSL; enterprise features under the Redpanda Community License (RCL).&lt;/p&gt;
&lt;p&gt;Originally Kowl by CloudHut, acquired by &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda&lt;/a&gt;. The Go binary is fast and lightweight, and the message viewer is the best in class for UX. The catch is the licensing model: every feature required for multi-team production operations (SSO, RBAC, data masking, audit logging) is behind a paid Redpanda Enterprise licence.&lt;/p&gt;
&lt;h3 id=&quot;best-for-5&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams already running Redpanda who have an enterprise contract. For vanilla Apache Kafka shops, the community edition is a topic viewer, not a management tool.&lt;/p&gt;
&lt;h3 id=&quot;limitations-5&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you are not a Redpanda customer, the enterprise features that make it a serious production tool are inaccessible without a sales engagement. Per the Redpanda documentation, if an enterprise licence expires while features are enabled, the Console shuts those features down. Enterprise pricing is not publicly listed.&lt;/p&gt;
&lt;h3 id=&quot;pricing-5&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free for community features. Enterprise pricing tied to your Redpanda cluster contract; contact Redpanda sales.&lt;/p&gt;
&lt;h2 id=&quot;7-lenses-hq&quot;&gt;&lt;strong&gt;7. Lenses HQ&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722c109a967e2ee935e20_lenses-blog-screenshot.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Commercial.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/lenses&quot;&gt;Lenses HQ&lt;/a&gt; is a multi-cluster Kafka management and stream processing platform, now owned by Celonis following a 2021 acquisition. Its main differentiation is SQL Processors: stream processing jobs written as SQL that execute as Kubernetes pods. No other tool in this list offers an equivalent. Lenses also provides a topology visualisation layer, a Kafka-to-Kafka replicator (K2K), and a global multi-cluster catalogue with data policies for masking and security groups.&lt;/p&gt;
&lt;p&gt;Supported providers: Confluent, MSK, Redpanda, Azure Event Hubs, Aiven, and self-managed Kafka.&lt;/p&gt;
&lt;h3 id=&quot;best-for-6&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams that need SQL-driven stream processing and observability in one product, particularly where Kafka Streams or ksqlDB are already part of the stack and a more flexible SQL layer over topics is valuable.&lt;/p&gt;
&lt;h3 id=&quot;strengths-5&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SQL Processors have no real equivalent in this market. Topology visualisation is strong. The Community Edition is genuinely usable for very small deployments. G2 reviewers consistently call out support quality, specifically the Slack community responsiveness.&lt;/p&gt;
&lt;h3 id=&quot;limitations-6&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SQL Processors require a Kubernetes platform, so production deployments carry meaningful operational overhead. Strategic direction has been less clearly communicated since the Celonis acquisition. Multi-cluster Enterprise pricing is sales-only, with no public list price for the SKU most teams would actually need.&lt;/p&gt;
&lt;h3 id=&quot;setup-and-maintenance-1&quot;&gt;&lt;strong&gt;Setup and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Production deployments require Kubernetes. Not a fit for teams without an existing K8s platform team.&lt;/p&gt;
&lt;h3 id=&quot;pricing-6&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Community: free (2 clusters, 2 users). Enterprise Edition: from $4,000/year for 15 users on a single cluster. Multi-cluster Enterprise pricing: contact Lenses sales.&lt;/p&gt;
&lt;h2 id=&quot;8-kafdrop&quot;&gt;&lt;strong&gt;8. Kafdrop&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69f430c5edb959134c22195a_kafdrop-kafka-monitoring.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Open-source (Apache 2.0).&lt;/p&gt;
&lt;p&gt;A lightweight Spring Boot web UI for browsing Kafka topics and messages. Kafdrop started at HomeAdvisor and was later rebooted by Obsidian Dynamics. It runs on a 64 MB heap and starts in seconds, which makes it useful for local development and quick ad-hoc inspection. It does not offer management features in any meaningful sense.&lt;/p&gt;
&lt;p&gt;The repository carries an open “looking for collaborators/maintainers” issue (#487) that has been open since March 2023. Dependabot keeps base image dependencies current, but feature development has stalled.&lt;/p&gt;
&lt;h3 id=&quot;best-for-7&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Solo developers, side projects, and dev/test clusters where all you need is a topic browser. Kafdrop is not a production management tool.&lt;/p&gt;
&lt;h3 id=&quot;strengths-6&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Zero infrastructure, very fast to deploy, small JVM footprint. Supports Avro/Protobuf deserialization via Schema Registry. Latest release: v4.2.0 (July 2025).&lt;/p&gt;
&lt;h3 id=&quot;limitations-7&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No RBAC, no audit trail, no data masking, no Connect management, no Schema Registry write operations, no consumer group reset. Actively seeking maintainers. Single-cluster only per deployment.&lt;/p&gt;
&lt;h3 id=&quot;pricing-7&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free.&lt;/p&gt;
&lt;h2 id=&quot;9-offset-explorer&quot;&gt;&lt;strong&gt;9. Offset Explorer&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fd8b94cc037ef6b6d0e28c_offset-explorer.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Commercial desktop application. Free for personal/non-commercial use.&lt;/p&gt;
&lt;p&gt;Offset Explorer (formerly Kafka Tool) is a native desktop GUI for Windows, macOS, and Linux that connects directly to Kafka clusters from a local machine. It requires no server-side infrastructure, which is its primary advantage: connect from your laptop, browse topics, inspect consumer groups, and manage offsets without deploying anything. It supports SASL_SSL/SCRAM auth setups that sometimes cause friction in web-based tools.&lt;/p&gt;
&lt;p&gt;Offset Explorer is a personal developer tool. It has no web UI, no multi-tenant access model, and no audit log. Most teams use it alongside a server-side tool rather than as a replacement.&lt;/p&gt;
&lt;h3 id=&quot;best-for-8&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Individual developers who need a personal Kafka client, particularly in environments with restrictive desktop policies that limit self-hosted web tools, or for one-off investigative work where spinning up a full web UI is unnecessary.&lt;/p&gt;
&lt;h3 id=&quot;strengths-7&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No deployment overhead. Works from any network location with cluster access. Handles awkward auth configurations reliably. Plugin SDK for custom deserialisers. Current version: 3.0.4.&lt;/p&gt;
&lt;h3 id=&quot;limitations-8&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single-user by design. Per-user commercial licensing scales linearly at $99/user/year, which makes it expensive for teams. No server-side access control, no audit trail, no team-level governance.&lt;/p&gt;
&lt;h3 id=&quot;pricing-8&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;$99 per user/year (commercial). Free for verified personal use.&lt;/p&gt;
&lt;h2 id=&quot;10-cmak-cluster-manager-for-apache-kafka&quot;&gt;&lt;strong&gt;10. CMAK (Cluster Manager for Apache Kafka)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fd8b9c49461c37f71e6b56_cmak.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Open-source (Apache 2.0). Formerly Yahoo Kafka Manager.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/cmak&quot;&gt;CMAK&lt;/a&gt; was the dominant Kafka admin UI before 2020. Its last release was v3.0.0.6 on 29 April 2022. Since then, the project has not shipped a release, and its ZooKeeper-centric design is a poor fit for KRaft-mode Kafka 4.x. Open issues include bugs filed in 2016 and 2017 that remain unresolved.&lt;/p&gt;
&lt;h3 id=&quot;best-for-9&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Legacy installations that already run it and cannot be migrated in the short term. Do not deploy CMAK for new projects.&lt;/p&gt;
&lt;h3 id=&quot;limitations-9&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No releases since April 2022. &lt;a href=&quot;/articles/kafka-cluster-management&quot;&gt;ZooKeeper dependency&lt;/a&gt; makes it incompatible with Kafka 4.x KRaft mode. No Schema Registry integration, no Connect management, no message browsing.&lt;/p&gt;
&lt;h3 id=&quot;pricing-9&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free.&lt;/p&gt;
&lt;h2 id=&quot;best-free-kafka-management-tools&quot;&gt;&lt;strong&gt;Best free Kafka management tools&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;First recommendation: Kpow Community Edition.&lt;/strong&gt; The &lt;a href=&quot;/community&quot;&gt;Community Edition&lt;/a&gt; is a free Docker image intended for non-production environments. It gives your team access to Kpow’s interface and feature set for local development, staging, and ephemeral clusters, so if you later move to Enterprise, there is no learning curve. It is the most capable free option if your goal is evaluating production-grade tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second recommendation: AKHQ.&lt;/strong&gt; For teams that need a free tool in production and have no server-side masking requirements, AKHQ is the most defensible choice. It has a mature Helm story, GitOps-native configuration, and a broader enterprise adoption record than any other free option.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third recommendation: Kafbat UI.&lt;/strong&gt; For greenfield environments where governance requirements are light, Kafbat UI is the most actively developed open-source option and the most approachable for teams new to Kafka management tooling.&lt;/p&gt;
&lt;h2 id=&quot;best-open-source-kafka-management-tool&quot;&gt;&lt;strong&gt;Best open-source Kafka management tool&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AKHQ.&lt;/strong&gt; It is the most production-tested free open-source option, with a GitOps configuration model, multi-cluster support, SSO via OIDC/OAuth2/LDAP, and a track record of deployment at organisations including Adobe and BlaBlaCar. The one firm caveat: it has no native data masking. If that is a requirement, you need a commercial tool.&lt;/p&gt;
&lt;p&gt;Kafbat UI is a close second for teams starting from scratch, but it carries the inherent risk of being volunteer-maintained with no commercial backing. Check the GitHub release cadence before committing.&lt;/p&gt;
&lt;h2 id=&quot;choosing-the-right-tool&quot;&gt;&lt;strong&gt;Choosing the right tool&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cluster count and scale.&lt;/strong&gt; Per-cluster pricing (Kpow) becomes significantly more cost-effective than per-seat pricing (Conduktor) once your team grows past around 20-30 users per cluster. Run the numbers for your specific headcount and cluster count before committing to either model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compliance and data governance.&lt;/strong&gt; RBAC and SSO are table stakes for any team beyond a handful of developers. Server-side data masking with an auditable log narrows the viable list considerably: Kpow, Conduktor, and Lenses HQ all provide it properly. AKHQ and Kafbat UI do not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment model.&lt;/strong&gt; All tools covered here are self-hosted. None offers a managed SaaS option, so you are absorbing the operational overhead regardless. Factor that into TCO calculations, especially for open-source tools: 0.2 FTE/year to maintain AKHQ at a burdened engineering cost of $200,000-$300,000/year is $40,000-$60,000/year before you account for security patching, upgrades, or on-call coverage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka provider compatibility.&lt;/strong&gt; If you are running MSK, Confluent Cloud, Redpanda, or Aiven rather than self-managed Kafka, verify provider-specific compatibility before trialling anything. Kpow is explicitly tested against all major managed Kafka providers. Confluent Control Center is only practical for Confluent Platform deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer tooling vs. team tooling.&lt;/strong&gt; Kafdrop and Offset Explorer are personal developer tools, not team infrastructure. Both are useful for ad-hoc inspection and local development, but neither replaces a server-side management layer for production operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maintenance risk.&lt;/strong&gt; CMAK is effectively abandoned and should not be used for new deployments. The original Provectus kafka-ui repo is no longer maintained; use Kafbat UI if you want that codebase. AKHQ, Kafbat, and Kafdrop are all volunteer-led, which is a risk to quantify before depending on them in regulated environments.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;&lt;strong&gt;FAQ&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between a Kafka UI and a Kafka management tool?&lt;/strong&gt; A &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Kafka UI&lt;/a&gt; typically refers to a web interface for browsing topics and messages. A Kafka management tool covers a broader scope: &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;consumer group management&lt;/a&gt;, offset resets, ACL administration, Schema Registry, Connect, and audit logging. Several tools in this list provide both.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which Kafka management tool works with Amazon MSK?&lt;/strong&gt; Kpow, AKHQ, Kafbat UI, and Conduktor all support MSK. Kpow documents MSK as an explicit provider with dedicated configuration guidance. Confluent Control Center is not practical for MSK deployments. Kpow is available on the &lt;a href=&quot;https://aws.amazon.com/marketplace/seller-profile?id=ab356f1d-3394-4523-b5d4-b339e3cca9e0&quot;&gt;AWS Marketplace&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do any Kafka management tools offer a free trial?&lt;/strong&gt; Kpow offers a &lt;a href=&quot;/products/kpow&quot;&gt;30-day free Enterprise trial&lt;/a&gt;. AKHQ, Kafbat UI, and CMAK are free to run without a trial period.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which tool is best for teams with HIPAA or PCI-DSS requirements?&lt;/strong&gt; Requirements for data masking and auditable access logs narrow the list to Kpow, Conduktor, and Lenses HQ. Of these, Kpow has the lowest deployment complexity and the only publicly listed price. AKHQ and Kafbat UI do not provide masking and should not be used as the primary tool in regulated data environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is CMAK still maintained?&lt;/strong&gt; No. The last release was April 2022. It also depends on ZooKeeper, which is removed in Kafka 4.x. CMAK should be treated as deprecated for any new deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How does Kpow pricing compare to Conduktor for large teams?&lt;/strong&gt; Kpow is priced per cluster, not per seat. For 3 clusters and 100 users, Kpow costs $13,500/year at AWS Marketplace list price. Conduktor’s per-seat model at a similar scale typically comes in at $80,000-$150,000/year at list price. The gap widens as team size grows.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Best Kafka monitoring tools for 2026</title><link>https://factorhouse.io/articles/best-kafka-monitoring-tools/</link><guid isPermaLink="true">https://factorhouse.io/articles/best-kafka-monitoring-tools/</guid><description>Compare 12 Kafka monitoring tools for 2026, from enterprise-grade Kpow to open-source AKHQ and Prometheus. Covers deployment, pricing, and key trade-offs.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apache Kafka monitoring is harder than it looks. By the time Kafka actually matters to your organisation, you’re running multiple clusters across cloud providers, Schema Registry is drifting, and the monitoring tool you stood up eighteen months ago now has its own Postgres database, its own backup regime, and a runbook for the rebalancing loop it enters every time a broker restarts.&lt;/p&gt;
&lt;p&gt;A serious monitoring layer needs to handle at least six jobs simultaneously: broker and cluster health, consumer lag at the partition level, schema correctness, topology operations (Connect, Streams, Flink), message inspection during incidents, and, for regulated teams, governance including &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC, SSO, audit logging&lt;/a&gt;, and PII masking. Most tools do one or two of those jobs well. Few handle all six without operational overhead that rivals the cluster itself.&lt;/p&gt;
&lt;p&gt;This guide covers the twelve tools that come up most in production engineering conversations in 2026. If you’re evaluating management UIs more broadly, see our &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Best Kafka UI guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;12-best-kafka-monitoring-tools&quot;&gt;&lt;strong&gt;12 best Kafka monitoring tools&lt;/strong&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kpow&lt;/td&gt;
&lt;td&gt;Full-stack enterprise UI&lt;/td&gt;
&lt;td&gt;Multi-cluster, regulated environments&lt;/td&gt;
&lt;td&gt;From $4,500/yr per cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Control Center&lt;/td&gt;
&lt;td&gt;Enterprise UI&lt;/td&gt;
&lt;td&gt;All-in Confluent Platform deployments&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conduktor&lt;/td&gt;
&lt;td&gt;Commercial UI + proxy&lt;/td&gt;
&lt;td&gt;Large-scale governance and multi-tenancy&lt;/td&gt;
&lt;td&gt;Per seat + per cluster (Gateway)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;Open-source UI&lt;/td&gt;
&lt;td&gt;Non-regulated teams, GitOps environments&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Console&lt;/td&gt;
&lt;td&gt;Open-source UI&lt;/td&gt;
&lt;td&gt;Redpanda-first teams&lt;/td&gt;
&lt;td&gt;Free core; SSO/RBAC require Enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka UI (Provectus)&lt;/td&gt;
&lt;td&gt;Open-source UI&lt;/td&gt;
&lt;td&gt;Lightweight single-cluster visibility&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafdrop&lt;/td&gt;
&lt;td&gt;Lightweight UI&lt;/td&gt;
&lt;td&gt;Local and dev use&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prometheus + Grafana&lt;/td&gt;
&lt;td&gt;Metrics stack&lt;/td&gt;
&lt;td&gt;Time-series alerting substrate&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kminion&lt;/td&gt;
&lt;td&gt;Metrics exporter&lt;/td&gt;
&lt;td&gt;Prometheus-native consumer lag&lt;/td&gt;
&lt;td&gt;Free (MIT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Burrow&lt;/td&gt;
&lt;td&gt;Lag evaluator&lt;/td&gt;
&lt;td&gt;Threshold-free lag alerting&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;SaaS observability&lt;/td&gt;
&lt;td&gt;Teams standardised on Datadog&lt;/td&gt;
&lt;td&gt;Per host + custom metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lenses.io&lt;/td&gt;
&lt;td&gt;SQL-driven platform&lt;/td&gt;
&lt;td&gt;SQL-over-Kafka and cross-cluster replication&lt;/td&gt;
&lt;td&gt;Enterprise sales&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;1-kpow-by-factor-house&quot;&gt;&lt;strong&gt;1. Kpow by Factor House&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a commercial enterprise UI and API for Apache Kafka and the surrounding ecosystem (Schema Registry, Kafka Connect, ksqlDB), covering monitoring, operations, and governance from a single stateless Docker container with zero external dependencies. It stores telemetry in internal Kafka topics on the cluster it monitors, so its resilience is tied directly to the cluster’s: if your cluster is up, Kpow is up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722460e8a935845facc77_kpow-blog-screenshot.avif&quot; alt=&quot;Kpow Kafka UI&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Platform and SRE teams managing multiple clusters across MSK, Confluent Cloud, Redpanda, and self-managed brokers with enterprise security and compliance requirements. The default choice for financial services, healthcare, and fintech.&lt;/p&gt;
&lt;h3 id=&quot;strengths&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Stateless single container with no Postgres, RocksDB, or sidecars to operate or back up&lt;/li&gt;
&lt;li&gt;Full enterprise governance: RBAC, SSO (Okta, OIDC, SAML, LDAP, Keycloak), server-side PII masking, and a complete user-action audit log shippable to any SIEM&lt;/li&gt;
&lt;li&gt;kJQ server-side message filtering across JSON, Avro, Protobuf, and Transit, with no throwaway consumer code required during incidents&lt;/li&gt;
&lt;li&gt;First-class MSK IAM authentication and native AWS Glue Schema Registry support (rare among competing tools)&lt;/li&gt;
&lt;li&gt;Multi-cluster from a single install; Prometheus endpoints per cluster and topic feed your existing Grafana stack&lt;/li&gt;
&lt;li&gt;Native KRaft support&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;limitations&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Self-hosted only: you run it in your own VPC. There is no SaaS option. For security-conscious and regulated teams, this is an advantage; for teams that want managed hosting, it is a deployment step. The Community Edition covers development and non-production use only.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker pull factorhouse/kpow&lt;/code&gt; and configure via environment variables pointing at your cluster. No migrations, no stateful volumes. Upgrades are a container swap. Fully GitOps-compatible.&lt;/p&gt;
&lt;h3 id=&quot;pricing&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Annual subscription, per cluster, with no per-user fee, so cost stays flat as your team grows. Available direct or via AWS Marketplace (hourly metered or annual cluster credits). Starting at $4,500/year. Start a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;2-confluent-control-center&quot;&gt;&lt;strong&gt;2. Confluent Control Center&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/articles/confluent-control-center&quot;&gt;Confluent Control Center (C3)&lt;/a&gt; is the official management interface for Confluent Platform, providing deep integration across the Confluent stack: Stream Lineage, KSQL, Schema Registry, Replicator, and RBAC via Confluent’s Metadata Service.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722a00e8a935845fad116_confluent-control-center-blog-screenshot.avif&quot; alt=&quot;Confluent Control Center&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-1&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams fully committed to Confluent Platform on-prem with the infrastructure headroom for a dedicated host. Not suitable for mixed-broker fleets.&lt;/p&gt;
&lt;h3 id=&quot;strengths-1&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Deep Confluent-native telemetry. Stream Lineage is unavailable in any other tool. Mature SSO and RBAC integration within the Confluent ecosystem.&lt;/p&gt;
&lt;h3 id=&quot;limitations-1&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A heavy infrastructure commitment: Confluent recommends a dedicated host with 32 GB RAM, 8 cores, and ~300 GB SSD. C3 is a Kafka Streams application with RocksDB-backed local state. LockException rebalancing loops under sustained load are a documented community issue. It does not work meaningfully against MSK or vanilla Apache Kafka.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-1&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Deployed as part of a full Confluent Platform installation. High availability requires two instances behind a load balancer. Operationally heavyweight compared to every other tool on this list.&lt;/p&gt;
&lt;h3 id=&quot;pricing-1&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Bundled with Confluent Platform Enterprise. No standalone purchase. Confluent Platform typically lands between $50K and $500K+ per year.&lt;/p&gt;
&lt;h2 id=&quot;3-conduktor&quot;&gt;&lt;strong&gt;3. Conduktor&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt; is a commercial dual-layered platform: Conduktor Console (web UI for monitoring, operations, and governance) and Conduktor Gateway (a separately licensed wire-level Kafka proxy). If you’re evaluating it based on memory of the original free desktop tool, the product you find today is materially different and more expensive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722b1f17be485095adbee_conduktor-blog-screenshot.avif&quot; alt=&quot;Conduktor&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-2&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Mid-to-large enterprises that specifically need proxy-level enforcement across a large developer population, with an operations team capacity to run and maintain a Postgres-backed stack.&lt;/p&gt;
&lt;h3 id=&quot;strengths-2&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Topic and application ownership catalogs, per-team self-service with approval workflows, and schema ownership tracking. The Gateway enables virtual clusters, field-level encryption, and guardrails that prevent non-compliant topic creation. No UI-based tool can match this layer of enforcement.&lt;/p&gt;
&lt;h3 id=&quot;limitations-2&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The Console requires Postgres, Cortex, and Alertmanager: a multi-container stateful stack before you’ve monitored a single broker. Licensing complexity is a recurring community complaint: Community tier caps at 3 clusters and 50 users, Scale tier uses per-seat pricing with a 50-seat minimum, and the Gateway is separately licensed with a 3-cluster minimum.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-2&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Multi-container deployment requiring Postgres, Cortex, and Alertmanager. The Gateway adds another architectural layer in front of your brokers. Significant operational overhead.&lt;/p&gt;
&lt;h3 id=&quot;pricing-2&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Per seat (50-seat minimum) for Console Scale tier, plus per-cluster Gateway licensing. Community tier free for up to 3 clusters and 50 users. Enterprise pricing via sales.&lt;/p&gt;
&lt;h2 id=&quot;4-akhq-formerly-kafkahq&quot;&gt;&lt;strong&gt;4. AKHQ (formerly KafkaHQ)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; is the most capable free open-source option for teams that have outgrown simple topic browsers. Deployed as a single stateless Docker container configured in YAML, it covers topics, consumer groups, Schema Registry, Kafka Connect, KSQL, Avro/Protobuf deserialization, and Live Tail.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;AKHQ&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-3&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Cost-conscious engineering teams in non-regulated environments running GitOps-style Kafka setups. Community support only.&lt;/p&gt;
&lt;h3 id=&quot;strengths-3&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Broad feature coverage for a free tool. Auth backends include LDAP, OAuth2/OIDC, GitHub SSO, JWT, and Keycloak. Multi-cluster support via YAML. GitOps-friendly: connections and role bindings are declared in application.yml.&lt;/p&gt;
&lt;h3 id=&quot;limitations-3&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No native data masking or PII redaction. No first-class user-action audit log. Community support only. Incident resolution goes through GitHub issues. Known UI performance issues at high partition counts.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-3&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single Docker container, YAML-driven. Stateless, so upgrades are straightforward. Ongoing maintenance falls to your team.&lt;/p&gt;
&lt;h3 id=&quot;pricing-3&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (Apache 2.0 license).&lt;/p&gt;
&lt;h2 id=&quot;5-redpanda-console-formerly-kowl&quot;&gt;&lt;strong&gt;5. Redpanda Console (formerly Kowl)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Originally built as Kowl, acquired by &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda&lt;/a&gt; in 2022 and now maintained with full-time engineering resources. Despite the branding, the core tool works against any Kafka-API-compatible cluster, though enterprise features are Redpanda-first.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7228f5010387c137d9bf0_redpanda-console-blog-screenshot.avif&quot; alt=&quot;Redpanda Console&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-4&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams running Redpanda as their primary broker, or small Kafka teams comfortable with basic auth on a single cluster.&lt;/p&gt;
&lt;h3 id=&quot;strengths-4&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Polished developer UX. Programmable Push Filters (JavaScript-based server-side message filtering) is the best message inspection experience in any open-source tool. Active release cadence. Stateless single-binary deployment.&lt;/p&gt;
&lt;h3 id=&quot;limitations-4&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SSO and RBAC require a Redpanda Enterprise license, explicitly required in the console.yaml config. Without it, you are on basic auth with no role bindings. Vanilla Kafka or MSK teams pay for a license tied to a broker they are not using. A single Console instance maps to a single Kafka cluster.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-4&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single binary or Docker container. Stateless and straightforward to deploy. Multi-cluster requires multiple deployments.&lt;/p&gt;
&lt;h3 id=&quot;pricing-4&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;OSS core is free (Apache 2.0). SSO and RBAC require Redpanda Enterprise license, available via enterprise sales.&lt;/p&gt;
&lt;h2 id=&quot;6-kafka-ui-provectus&quot;&gt;&lt;strong&gt;6. Kafka UI (Provectus)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kafka UI, maintained by Provectus, is a free open-source web interface for Apache Kafka. It provides topic management, &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;consumer group lag&lt;/a&gt; monitoring, Schema Registry integration, Kafka Connect management, and basic message browsing from a single Docker container.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69f430aeef17e96fb7a9a7b9_kafka-ui-provectus-kafka-monitoring.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-5&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Teams that want a lightweight, free UI for quick cluster visibility without compliance or multi-cluster requirements.&lt;/p&gt;
&lt;h3 id=&quot;strengths-5&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Easy to stand up and configure. Covers the basics well: topic listing, consumer lag, Schema Registry, and Kafka Connect. Active GitHub community. Multi-cluster support via YAML.&lt;/p&gt;
&lt;h3 id=&quot;limitations-5&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No native authentication or RBAC: security is delegated to a reverse proxy. No data masking, no user audit trail. Less feature-complete than AKHQ for teams that need auth or more advanced tooling. Not appropriate for regulated environments.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-5&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single Docker container, YAML configuration. Low operational overhead for development and evaluation environments.&lt;/p&gt;
&lt;h3 id=&quot;pricing-5&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (Apache 2.0 license).&lt;/p&gt;
&lt;h2 id=&quot;7-kafdrop&quot;&gt;&lt;strong&gt;7. Kafdrop&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kafdrop is the lightest tool on this list: a single Spring Boot container configurable to run at 64 MB heap. It covers topic listing, message browsing (JSON, Avro, Protobuf), &lt;a href=&quot;/articles/best-practices-kafka-data-observability&quot;&gt;per-partition consumer lag&lt;/a&gt;, ACL viewing, basic topic management, and Schema Registry integration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69f430c5edb959134c22195a_kafdrop-kafka-monitoring.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-6&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Local development, dev team internal use on a single cluster, or quick sanity checks. Not a production monitoring platform.&lt;/p&gt;
&lt;h3 id=&quot;strengths-6&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Minimal resource footprint. Fast to stand up. Useful as a read-only cluster inspection tool in development environments.&lt;/p&gt;
&lt;h3 id=&quot;limitations-6&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No authentication or RBAC (security delegated entirely to a reverse proxy), no Kafka Connect management, no audit logging, no data masking, no multi-cluster support. A UI for looking at topics and messages, not a monitoring solution.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-6&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single container, environment variable configuration. Minimal operational overhead.&lt;/p&gt;
&lt;h3 id=&quot;pricing-6&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (Apache 2.0 license).&lt;/p&gt;
&lt;h2 id=&quot;8-prometheus--grafana-jmx-exporter&quot;&gt;&lt;strong&gt;8. Prometheus + Grafana (JMX Exporter)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Not a product but a stack you assemble: the JMX Prometheus Exporter as a Java agent on each broker, danielqsj/kafka_exporter for consumer group lag, Prometheus to scrape, and Grafana to visualise. Every commercial tool in this list exposes Prometheus endpoints to feed back into this stack, which tells you how foundational it is.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69f430d39bebae2b3a4b7efc_Grafana-kafka-monitoring.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-7&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Every Kafka team, as the time-series metrics and alerting substrate. Not a standalone solution. Layer a dedicated operations UI on top.&lt;/p&gt;
&lt;h3 id=&quot;strengths-7&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free, widely understood, integrates into existing infrastructure monitoring, de facto standard for Kafka time-series metrics, enormous community dashboard ecosystem.&lt;/p&gt;
&lt;h3 id=&quot;limitations-7&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Not an operations UI. No topic browser, no message inspection, no offset reset, no connector management. Setup requires JMX agents on every broker and careful configuration. Consumer lag via kafka_exporter is less nuanced than Burrow or Kpow’s internal compute. Dashboard versioning breaks between JMX Exporter 0.x and 1.x.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-7&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;JMX agents on every broker, Prometheus scrape config, Grafana dashboard management. Medium operational overhead; significant initial configuration effort.&lt;/p&gt;
&lt;h3 id=&quot;pricing-7&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (OSS). Grafana Cloud has paid tiers for managed hosting.&lt;/p&gt;
&lt;h2 id=&quot;9-kminion&quot;&gt;&lt;strong&gt;9. Kminion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kminion is a Go-based Kafka metrics exporter that exposes detailed consumer group and topic metrics in Prometheus format. It provides richer consumer group lag data than the standard kafka_exporter, including per-partition lag, consumer group state, and end-to-end latency measurement via configurable test producers.&lt;/p&gt;
&lt;h3 id=&quot;best-for-8&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SRE teams building a Prometheus-native Kafka monitoring stack who need more consumer group telemetry detail than the standard kafka_exporter provides.&lt;/p&gt;
&lt;h3 id=&quot;strengths-8&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;More complete consumer group metrics than kafka_exporter. Lightweight stateless Go binary. End-to-end latency probing with configurable test producers and consumers. Prometheus-native from the ground up.&lt;/p&gt;
&lt;h3 id=&quot;limitations-8&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Metrics exporter only: no UI, no operations capability. Requires Prometheus and Grafana to be useful. Measures from outside the cluster, which is less nuanced than internal compute approaches.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-8&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single Go binary or Docker container. Environment variable configuration. Low operational overhead.&lt;/p&gt;
&lt;h3 id=&quot;pricing-8&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (MIT license).&lt;/p&gt;
&lt;h2 id=&quot;10-burrow-linkedin&quot;&gt;&lt;strong&gt;10. Burrow (LinkedIn)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Burrow is a single-purpose tool built by LinkedIn for one job: threshold-free consumer lag evaluation. It reads __consumer_offsets, evaluates each consumer group’s lag over a sliding window, and returns an OK, WARN, or ERROR status via an HTTP API. No UI. No external database.&lt;/p&gt;
&lt;h3 id=&quot;best-for-9&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SRE teams that want a programmatic lag evaluation service as a component of their alerting pipeline. An excellent addition to a broader stack, not a standalone solution.&lt;/p&gt;
&lt;h3 id=&quot;strengths-9&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Threshold-free evaluation eliminates false positives from traffic spikes. Burrow evaluates whether the consumer is making progress relative to the window, not whether lag exceeds an arbitrary number. Lightweight Go binary with no external dependencies.&lt;/p&gt;
&lt;h3 id=&quot;limitations-9&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No UI. Maintenance cadence has slowed significantly, with open issues from 2024 with limited maintainer response. The tool still works; it is not actively evolving.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-9&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Single Go binary, HTTP API. Minimal operational overhead.&lt;/p&gt;
&lt;h3 id=&quot;pricing-9&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Free (Apache 2.0 license).&lt;/p&gt;
&lt;h2 id=&quot;11-datadog-kafka-integration&quot;&gt;&lt;strong&gt;11. Datadog (Kafka integration)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Datadog is a metrics and APM SaaS platform that ingests Kafka telemetry alongside the rest of your infrastructure. Teams sometimes evaluate Datadog as a replacement for a Kafka operations UI. It is not.&lt;/p&gt;
&lt;h3 id=&quot;best-for-10&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Enterprises already standardised on Datadog for observability, where rolling Kafka metrics into the same alerting and correlation pane is worth the incremental cost. Always paired with a separate Kafka operations UI.&lt;/p&gt;
&lt;h3 id=&quot;strengths-10&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Time-series metrics across brokers, topics, partitions, and JVM internals. Data Streams Monitoring for end-to-end latency and lag visualisation. Correlation with the rest of your infrastructure stack.&lt;/p&gt;
&lt;h3 id=&quot;limitations-10&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Cannot browse messages, reset consumer offsets, manage Connect connectors, or view schema versions. JMX metrics beyond the curated 350-metric default count against per-host custom metric allotments at $5 per 100 metrics per month over allowance. For MSK, falls back to the lower-fidelity CloudWatch-based integration.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-10&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SaaS with a Datadog Agent on every broker. Your operational overhead is agent deployment and dashboard maintenance.&lt;/p&gt;
&lt;h3 id=&quot;pricing-10&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Per host plus custom metric overage plus the Data Streams Monitoring SKU. Costs scale significantly at large cluster sizes.&lt;/p&gt;
&lt;h2 id=&quot;12-lensesio&quot;&gt;&lt;strong&gt;12. Lenses.io&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/articles/lenses&quot;&gt;Lenses&lt;/a&gt; treats Kafka as a data platform: a SQL studio for querying and transforming Kafka streams, a graph-based topology view, a multi-Kafka global catalog, and a K2K cross-cluster replicator. Acquired by Celonis; community feedback indicates development as a standalone product has slowed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722c109a967e2ee935e20_lenses-blog-screenshot.avif&quot; alt=&quot;Lenses&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;best-for-11&quot;&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Larger enterprises that specifically need SQL-driven data exploration over Kafka and cross-cluster replication, with an existing Kubernetes platform team to operate the Lenses infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;strengths-11&quot;&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SQL-over-Kafka workflows with no direct equivalent elsewhere. Topology visualisation. K2K cross-cluster replication. Multi-cluster global catalog.&lt;/p&gt;
&lt;h3 id=&quot;limitations-11&quot;&gt;&lt;strong&gt;Limitations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;SQL Processors execute as Kubernetes pods, so production deployments require a Kubernetes platform team. Licensing is enterprise sales-only with no public pricing. Strategic uncertainty following the Celonis acquisition is worth factoring into any vendor evaluation.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-maintenance-11&quot;&gt;&lt;strong&gt;Set up and maintenance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Multi-container Kubernetes deployment. Significant operational overhead; requires a platform team to own the Lenses infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;pricing-11&quot;&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Enterprise sales only. No public pricing.&lt;/p&gt;
&lt;h2 id=&quot;best-free-kafka-monitoring-tool&quot;&gt;&lt;strong&gt;Best free Kafka monitoring tool&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kpow Community Edition&lt;/strong&gt; is the strongest free option for teams moving toward a production Kafka deployment. It runs the same codebase as the commercial product (stateless single container, full message inspection with kJQ filtering, multi-cluster support) with limits on cluster count and non-production use. You evaluate real enterprise functionality without a time limit, then move to a paid license when you go to production. Explore &lt;a href=&quot;/products/kpow&quot;&gt;Kpow Community Edition&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For teams that need a free tool with no production restrictions, &lt;strong&gt;AKHQ&lt;/strong&gt; is the most mature open-source option, with broad feature coverage and a genuine auth story via OIDC and LDAP. As a third pick, &lt;strong&gt;Kafka UI by Provectus&lt;/strong&gt; is the easier starting point if you need quick topic visibility and don’t require auth or multi-cluster support.&lt;/p&gt;
&lt;h2 id=&quot;best-open-source-kafka-monitoring-tools&quot;&gt;&lt;strong&gt;Best open-source Kafka monitoring tools&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AKHQ&lt;/strong&gt; is the most mature and complete free open-source option. Its YAML-based configuration, multi-cluster support, and auth backends (LDAP, OIDC, JWT) go meaningfully further than most free alternatives, making it a genuine fit for GitOps-oriented teams in non-regulated environments.&lt;/p&gt;
&lt;p&gt;For metrics specifically, &lt;strong&gt;Kminion&lt;/strong&gt; is the best open-source Prometheus exporter for consumer group lag detail, and pairs well with Grafana for teams building a metrics substrate from scratch. Use both together if your priority is a fully free, instrumented, alerting-capable stack, then layer a UI on top as your requirements grow.&lt;/p&gt;
&lt;h2 id=&quot;key-considerations-when-choosing&quot;&gt;&lt;strong&gt;Key considerations when choosing&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;deployment-complexity&quot;&gt;&lt;strong&gt;Deployment complexity&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The monitoring tool should not require more infrastructure than the Kafka cluster it monitors. Count every hard dependency (Postgres, Redis, Cortex, RocksDB, per-broker agent) as a separate operational liability with its own upgrade path, backup regime, and failure mode.&lt;/p&gt;
&lt;h3 id=&quot;enterprise-readiness&quot;&gt;&lt;strong&gt;Enterprise readiness&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For regulated environments, the requirements are specific: RBAC, SSO (OIDC/SAML/LDAP), a user-action audit log you can ship to a SIEM, and server-side data masking that prevents PII from reaching the browser. Client-side masking does not satisfy compliance requirements. Anything less shifts risk onto your team.&lt;/p&gt;
&lt;h3 id=&quot;pricing-model&quot;&gt;&lt;strong&gt;Pricing model&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Per-seat pricing penalises team growth. Per-host or per-broker pricing penalises horizontal scaling. Per-cluster pricing is the most predictable model for most platform teams. Factor engineering maintenance hours into the total cost of open-source options. They are not always cheaper.&lt;/p&gt;
&lt;h3 id=&quot;managed-service-compatibility&quot;&gt;&lt;strong&gt;Managed service compatibility&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MSK with IAM authentication, Confluent Cloud, Aiven, Redpanda, and Strimzi all have different auth and metadata behaviour. Verify support against your actual production topology. Specifically test MSK IAM auth and AWS Glue Schema Registry if your stack includes them. Many tools claim compatibility but treat both as secondary integrations.&lt;/p&gt;
&lt;h3 id=&quot;monitoring-scope&quot;&gt;&lt;strong&gt;Monitoring scope&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Broker metrics are necessary but not sufficient. Ensure the tool covers per-partition consumer lag, Schema Registry visibility, Kafka Connect management, and message inspection with native deserialization. If you are running Kafka Streams or Flink jobs, confirm topology visibility before committing.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;&lt;strong&gt;FAQ&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;what-is-the-best-kafka-monitoring-tool-for-production-use&quot;&gt;&lt;strong&gt;What is the best Kafka monitoring tool for production use?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow by Factor House&lt;/a&gt; is the strongest all-round choice: stateless deployment, enterprise RBAC and SSO, server-side data masking, multi-cluster support, and per-cluster pricing. For open-source-only environments, AKHQ is the most complete free option.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-best-free-kafka-monitoring-tool&quot;&gt;&lt;strong&gt;What is the best free Kafka monitoring tool?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow Community Edition&lt;/a&gt; offers the most functionality without a license fee: same codebase as the commercial product, limited to non-production clusters. For production-free options, AKHQ is the most mature open-source pick.&lt;/p&gt;
&lt;h3 id=&quot;can-i-monitor-kafka-consumer-lag-without-a-ui&quot;&gt;&lt;strong&gt;Can I monitor Kafka consumer lag without a UI?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Yes. Kpow continuously collects &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/prometheus/overview&quot;&gt;Kafka telemetry&lt;/a&gt;, including per-consumer-group and per-partition lag, and emits it as a data stream you can route to any observability system. Whether you’re running Prometheus, Datadog, Grafana, or a custom alerting stack, you can consume Kpow’s metrics directly without needing a separate lag-monitoring tool or a dedicated UI.&lt;/p&gt;
&lt;h3 id=&quot;does-confluent-control-center-work-with-msk-or-self-managed-kafka&quot;&gt;&lt;strong&gt;Does Confluent Control Center work with MSK or self-managed Kafka?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;No, not meaningfully. Control Center is built for Confluent Platform and loses most of its value outside that context. For mixed-broker or multi-cloud Kafka fleets, you need a vendor-agnostic tool.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-monitor-kafka-in-a-regulated-environment&quot;&gt;&lt;strong&gt;How do I monitor Kafka in a regulated environment?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;You need server-side data masking, RBAC, SSO, and a user-action audit log shippable to your SIEM. Kpow covers all of these from a single stateless container, with no secondary database required. Verify that any tool you evaluate masks data server-side, not in the browser.&lt;/p&gt;
&lt;h3 id=&quot;what-kafka-monitoring-tool-works-best-with-aws-msk&quot;&gt;&lt;strong&gt;What Kafka monitoring tool works best with AWS MSK?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kpow provides first-class MSK IAM authentication and native AWS Glue Schema Registry support. Most tools treat MSK as a secondary integration or lack IAM auth support entirely. Test both explicitly during any PoC against an MSK cluster.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Kafka broker monitoring</title><link>https://factorhouse.io/articles/kafka-broker-monitoring/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-broker-monitoring/</guid><description>How to monitor Kafka brokers: key JMX metrics, alerting thresholds, process monitoring scripts, and common issues with step-by-step diagnosis.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Brokers are the operational core of a Kafka cluster. They receive produce requests, serve fetch requests, manage partition replicas, and coordinate leader election. When a broker degrades, every producer and consumer connected to it is affected, often before your monitoring stack surfaces anything useful.&lt;/p&gt;
&lt;p&gt;This article covers broker-level monitoring specifically: the JMX metrics to watch, thresholds to alert on, process monitoring scripts, and how to diagnose the most common failure modes. For cluster-wide concerns, consumer lag, and producer delivery guarantees, refer to the separate guides on &lt;a href=&quot;/articles/kafka-cluster-monitoring&quot;&gt;Kafka cluster monitoring&lt;/a&gt;, &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;Kafka consumer monitoring&lt;/a&gt;, and &lt;a href=&quot;/articles/kafka-producer-monitoring&quot;&gt;Kafka producer monitoring&lt;/a&gt;. For a full overview of Kafka observability, see the &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;Kafka monitoring guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafka brokers expose metrics via JMX by default; Prometheus users need the JMX Exporter agent to scrape them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; and &lt;code&gt;ActiveControllerCount&lt;/code&gt; are the two most critical metrics for cluster health.&lt;/li&gt;
&lt;li&gt;Broker monitoring covers three layers: the Kafka process, the JVM, and the host OS. All three require attention.&lt;/li&gt;
&lt;li&gt;A lightweight process monitoring script can detect broker failures faster than most monitoring stacks.&lt;/li&gt;
&lt;li&gt;Most broker incidents trace to a small set of root causes: disk saturation, leader imbalance, network thread exhaustion, or ISR instability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-broker-monitoring&quot;&gt;What is Kafka broker monitoring?&lt;/h2&gt;
&lt;p&gt;A Kafka broker handles several concurrent responsibilities: writing incoming messages to disk, serving read requests from consumers, replicating partition data to follower brokers, and participating in leader election. Each of these activities has distinct failure modes.&lt;/p&gt;
&lt;p&gt;Broker health is not the same as cluster health. A single degraded broker can produce replica lag, leader imbalance, and consumer fetch failures without the cluster appearing unavailable. Topics remain accessible on unaffected brokers, but the partitions whose leaders or followers are on the degraded broker will silently degrade. Identifying which broker is under pressure, and why, is the purpose of broker-level monitoring.&lt;/p&gt;
&lt;p&gt;Monitoring spans three layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Kafka process metrics&lt;/strong&gt; (exposed via JMX): replication state, request throughput, controller status, failure rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JVM metrics&lt;/strong&gt;: heap usage, garbage collection pause duration, open file descriptors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Host OS metrics&lt;/strong&gt;: disk capacity, disk I/O throughput, network utilization.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Consumer lag, producer delivery guarantees, and cluster-wide partition distribution are covered in separate articles. This article focuses on the broker process and what runs immediately around it.&lt;/p&gt;
&lt;h2 id=&quot;how-kafka-brokers-expose-metrics&quot;&gt;How Kafka brokers expose metrics&lt;/h2&gt;
&lt;h3 id=&quot;jmx-default&quot;&gt;JMX (default)&lt;/h3&gt;
&lt;p&gt;Kafka exposes metrics as JMX MBeans by default. Server-side broker metrics use Yammer Metrics internally; native Java clients use Kafka’s own metrics registry. Both project their measurements onto MBeans hosted in the JVM’s MBean server.&lt;/p&gt;
&lt;p&gt;Remote JMX access is disabled by default. To enable it, set the &lt;code&gt;JMX_PORT&lt;/code&gt; environment variable when starting the broker:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; JMX_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9999&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In production, raw JMX access is a security risk. Because JMX supports remote method invocation (RMI), an unauthenticated client can invoke operations on MBeans, including modifying runtime configurations and triggering JVM shutdowns. Use &lt;code&gt;KAFKA_JMX_OPTS&lt;/code&gt; to enforce authentication and encryption:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KAFKA_JMX_OPTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;-Dcom.sun.management.jmxremote &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.authenticate=true &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.ssl=true &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.port=9999 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.rmi.port=9999 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.password.file=/etc/kafka/jmxremote.password &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  -Dcom.sun.management.jmxremote.access.file=/etc/kafka/jmxremote.access&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tools such as &lt;code&gt;jconsole&lt;/code&gt;, &lt;code&gt;jmxterm&lt;/code&gt;, and &lt;code&gt;kafka-run-class.sh kafka.tools.JmxTool&lt;/code&gt; can query JMX directly once access is enabled.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-via-jmx-exporter&quot;&gt;Prometheus via JMX Exporter&lt;/h3&gt;
&lt;p&gt;The standard approach for Prometheus-based stacks is to run the &lt;a href=&quot;https://github.com/prometheus/jmx_exporter&quot;&gt;Prometheus JMX Exporter&lt;/a&gt; as a Java agent alongside the broker JVM.&lt;/p&gt;
&lt;p&gt;Running the exporter in local agent mode (rather than as a standalone HTTP server) is preferred. It avoids the serialization overhead of remote RMI polling and captures additional host-level process metrics including JVM CPU and memory utilization.&lt;/p&gt;
&lt;p&gt;The agent translates JMX MBeans to Prometheus metrics and serves them on an HTTP endpoint, typically on port 7071. It requires a YAML configuration file that maps MBean paths to Prometheus metric names. A community-maintained configuration for Kafka is available in the &lt;a href=&quot;https://github.com/prometheus/jmx_exporter/blob/main/example_configs/kafka-2_0_0.yml&quot;&gt;JMX Exporter GitHub repository&lt;/a&gt;. Keep this configuration file in version control alongside your broker configuration and review it when upgrading Kafka versions.&lt;/p&gt;
&lt;h3 id=&quot;kafka-metrics-reporters-pluggable&quot;&gt;Kafka metrics reporters (pluggable)&lt;/h3&gt;
&lt;p&gt;Kafka supports custom &lt;code&gt;MetricsReporter&lt;/code&gt; implementations via the &lt;code&gt;metric.reporters&lt;/code&gt; setting in &lt;code&gt;server.properties&lt;/code&gt;. This allows metrics to be pushed directly to external systems without going through JMX or Prometheus. Confluent Platform ships a reporter that sends metrics to Confluent Control Center. Vendors such as Datadog and New Relic provide their own reporter implementations as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KRaft mode:&lt;/strong&gt; In KRaft mode (GA since Kafka 3.3), some ZooKeeper-related MBeans are removed and several KRaft-specific MBeans appear in their place, including &lt;code&gt;FencedBrokerCount&lt;/code&gt; and &lt;code&gt;LastAppliedRecordLagMs&lt;/code&gt;. If you are migrating from ZooKeeper mode, or if you have inherited dashboards built against an older cluster, audit your MBean paths before deploying them against a KRaft cluster.&lt;/p&gt;
&lt;h2 id=&quot;key-metrics-to-monitor&quot;&gt;Key metrics to monitor&lt;/h2&gt;
&lt;p&gt;The sections below cover metrics across all three layers. JMX MBean paths are given for direct JMX access; if you are using Prometheus, metric names follow the pattern produced by the JMX Exporter configuration.&lt;/p&gt;
&lt;h3 id=&quot;replication-health-metrics&quot;&gt;Replication health metrics&lt;/h3&gt;
&lt;p&gt;These are the highest-priority metrics on any broker. A non-zero value in the first three rows means data availability is at risk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions where the ISR count is below the replication factor&lt;/td&gt;
&lt;td&gt;A sustained non-zero value means the cluster has fewer replicas than configured to fall back on if a leader fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions with fewer in-sync replicas than &lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Producers configured with &lt;code&gt;acks=all&lt;/code&gt; receive &lt;code&gt;NotEnoughReplicasException&lt;/code&gt; when this is non-zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AtMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=AtMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions where ISR count equals &lt;code&gt;min.insync.replicas&lt;/code&gt; exactly&lt;/td&gt;
&lt;td&gt;One more broker going offline pushes these into the under-min state; treat as a warning signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate at which replicas are leaving the ISR&lt;/td&gt;
&lt;td&gt;Sustained shrinks indicate follower replicas are falling behind, typically from disk pressure or GC pauses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsrExpandsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=IsrExpandsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate at which replicas are rejoining the ISR&lt;/td&gt;
&lt;td&gt;High expand rate alongside shrinks indicates ISR churn rather than a clean recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;controller-metrics&quot;&gt;Controller metrics&lt;/h3&gt;
&lt;p&gt;There is always exactly one active controller in a Kafka cluster. These metrics confirm that invariant holds and that the controller is performing well.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether this broker is currently the active controller (0 or 1)&lt;/td&gt;
&lt;td&gt;The cluster-wide sum must be exactly 1. Zero means no metadata management; more than 1 indicates a split-brain condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions with no available leader&lt;/td&gt;
&lt;td&gt;Non-zero means those partitions are completely unavailable to producers and consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LeaderElectionRateAndTimeMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Frequency and duration of leader elections&lt;/td&gt;
&lt;td&gt;Frequent elections trigger consumer rebalances and producer retries; elevated duration suggests controller pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FencedBrokerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=FencedBrokerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Brokers the controller has marked as unreachable (KRaft only)&lt;/td&gt;
&lt;td&gt;Any fenced broker represents lost partition leadership and requires immediate investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LastAppliedRecordLagMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=LastAppliedRecordLagMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Metadata replication lag relative to the active controller (KRaft only)&lt;/td&gt;
&lt;td&gt;Should be 0 on the active controller; values above 1,000ms on standby nodes indicate metadata replication delay&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;request-handling-metrics&quot;&gt;Request handling metrics&lt;/h3&gt;
&lt;p&gt;These metrics indicate whether the broker is keeping up with its request load.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average idle percentage of the request handler thread pool&lt;/td&gt;
&lt;td&gt;Values below 0.20 indicate saturation on request processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average idle percentage of the network processor threads&lt;/td&gt;
&lt;td&gt;Target above 0.30; below 0.20 indicates network thread exhaustion, often caused by slow consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TotalTimeMs&lt;/code&gt; (Produce)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End-to-end latency for produce requests&lt;/td&gt;
&lt;td&gt;P99 target is under 100ms; sustained spikes indicate broker-side pressure or slow disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TotalTimeMs&lt;/code&gt; (FetchConsumer)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchConsumer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End-to-end latency for consumer fetch requests&lt;/td&gt;
&lt;td&gt;Sustained elevation traces to replica lag or disk read pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RequestQueueSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.network:type=RequestChannel,name=RequestQueueSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests waiting for a handler thread&lt;/td&gt;
&lt;td&gt;Should remain below 10; sustained growth indicates handler thread saturation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FailedProduceRequestsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=FailedProduceRequestsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate of failed produce requests&lt;/td&gt;
&lt;td&gt;Any non-zero value requires investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FailedFetchRequestsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=FailedFetchRequestsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate of failed fetch requests&lt;/td&gt;
&lt;td&gt;Any non-zero value requires investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Latency sub-phases.&lt;/strong&gt; &lt;code&gt;TotalTimeMs&lt;/code&gt; is the sum of five phases: time waiting in the request queue (&lt;code&gt;RequestQueueTimeMs&lt;/code&gt;), local processing time on the partition leader (&lt;code&gt;LocalTimeMs&lt;/code&gt;), replication wait time for &lt;code&gt;acks=all&lt;/code&gt; producers (&lt;code&gt;RemoteTimeMs&lt;/code&gt;), time waiting in the response queue (&lt;code&gt;ResponseQueueTimeMs&lt;/code&gt;), and time to transmit the response to the client (&lt;code&gt;ResponseSendTimeMs&lt;/code&gt;). When P99 &lt;code&gt;TotalTimeMs&lt;/code&gt; is elevated, checking each sub-phase narrows the root cause. High &lt;code&gt;RemoteTimeMs&lt;/code&gt; points to replication pressure. High &lt;code&gt;LocalTimeMs&lt;/code&gt; typically indicates disk write saturation or message format conversion overhead.&lt;/p&gt;
&lt;h3 id=&quot;throughput-and-io-metrics&quot;&gt;Throughput and I/O metrics&lt;/h3&gt;
&lt;p&gt;These metrics give a baseline view of broker load and are useful for capacity planning.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BytesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytes received per second&lt;/td&gt;
&lt;td&gt;Sudden spikes can saturate disk write bandwidth or network interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BytesOutPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytes sent per second&lt;/td&gt;
&lt;td&gt;Includes replication traffic; track separately from consumer traffic where possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MessagesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Messages received per second&lt;/td&gt;
&lt;td&gt;Useful for understanding message rate independently of message size&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;log-and-disk-metrics&quot;&gt;Log and disk metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflineLogDirectoryCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.log:type=LogManager,name=OfflineLogDirectoryCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log directories that are offline&lt;/td&gt;
&lt;td&gt;Any non-zero value means the broker cannot write to some partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeadThreadCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.log:type=LogCleaner,name=DeadThreadCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log cleaner threads that have failed silently&lt;/td&gt;
&lt;td&gt;Non-zero means compaction has stopped; &lt;code&gt;uncleanable-bytes&lt;/code&gt; will grow until disk fills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uncleanable-bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.log:type=LogCleaner,name=uncleanable-bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data volume waiting to be compacted&lt;/td&gt;
&lt;td&gt;A persistent upward trend on compacted topics indicates log cleaner failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk space (OS)&lt;/td&gt;
&lt;td&gt;n/a – monitor via node exporter or equivalent&lt;/td&gt;
&lt;td&gt;Percentage of disk used on the Kafka log directory&lt;/td&gt;
&lt;td&gt;Kafka stops accepting produce requests when disk fills completely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;jvm-metrics&quot;&gt;JVM metrics&lt;/h3&gt;
&lt;p&gt;JVM pressure is a common root cause of ISR instability and request latency spikes. If a broker’s JVM pauses long enough during garbage collection, the broker fails to send heartbeats to the controller. This triggers a timeout and forces partition leadership reassignment, which looks from the outside like an ISR shrink or elevated election rate.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JVM heap used&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.lang:type=Memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Heap in use as a percentage of max&lt;/td&gt;
&lt;td&gt;Sustained above 75% increases GC pressure and pause frequency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC pause duration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.lang:type=GarbageCollector&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Duration of stop-the-world GC events&lt;/td&gt;
&lt;td&gt;Pauses above approximately 1 second cause ISR shrinks and heartbeat timeouts; the G1GC target is 20ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC frequency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.lang:type=GarbageCollector&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of GC events per minute&lt;/td&gt;
&lt;td&gt;High frequency of short pauses often precedes a longer pause event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open file descriptors&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java.lang:type=OperatingSystem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of open file handles&lt;/td&gt;
&lt;td&gt;Kafka opens many handles for log segments; hitting the OS limit causes broker errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Heap sizing.&lt;/strong&gt; Kafka uses a hybrid memory model: a small JVM heap handles partition metadata, indexes, and producer state, while the OS page cache holds hot log segment data. Allocating too much to the JVM heap reduces the page cache, which forces consumer reads to disk. On a 64 GB host, a typical configuration is 6-12 GB for the JVM heap and the remainder for the OS page cache. A rough rule of thumb is 1-2 MB of heap per active partition replica hosted on the broker.&lt;/p&gt;
&lt;h2 id=&quot;broker-process-monitoring-script&quot;&gt;Broker process monitoring script&lt;/h2&gt;
&lt;p&gt;The JMX metrics above require the broker to be running and reachable. A process-level check catches failures that happen before or outside the metrics stack: a broker crash, a port that is not listening, a startup failure, or a JVM that is hung during initialization. The scripts below complement JMX monitoring rather than replacing it.&lt;/p&gt;
&lt;h3 id=&quot;check-the-broker-process-is-running-jps--ps&quot;&gt;Check the broker process is running (jps / ps)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;jps&lt;/code&gt; (JVM process status, included in the JDK) lists running JVM processes by main class. For Apache Kafka, the main class is &lt;code&gt;kafka.Kafka&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_CLASS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka.Kafka&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; jps&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -l&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_CLASS&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Broker process is running&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;ERROR: Kafka broker process not found&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;jps&lt;/code&gt; is not available, fall back to &lt;code&gt;ps&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; ps&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; aux&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;[k]afka.Kafka&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Broker process is running&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;ERROR: Kafka broker process not found&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Confluent Platform:&lt;/strong&gt; The main class for Confluent Server is &lt;code&gt;io.confluent.kafka.server.ConfluentServer&lt;/code&gt;. Adjust the grep pattern accordingly.&lt;/p&gt;
&lt;p&gt;A running process does not mean the broker is healthy. It may be in an error state or hung waiting on disk or network. Use this check as a first-line detector, not a health indicator.&lt;/p&gt;
&lt;h3 id=&quot;check-the-broker-port-is-accepting-connections&quot;&gt;Check the broker port is accepting connections&lt;/h3&gt;
&lt;p&gt;A process check confirms the JVM is alive, but not that it is accepting Kafka connections. Check that the listener port (default 9092) is open and accepting connections:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;9092&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; nc&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -z&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -w&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;TIMEOUT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; 2&gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Broker port ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;} is accepting connections&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;ERROR: Broker not accepting connections on ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}:${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In environments without netcat, use the &lt;code&gt;/dev/tcp&lt;/code&gt; bash built-in:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;9092&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; /dev/tcp/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;${BROKER_HOST}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;${BROKER_PORT}) &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Broker port ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;} is accepting connections&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;ERROR: Broker not accepting connections on ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_HOST&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}:${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;BROKER_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;jmx-based-health-check-script&quot;&gt;JMX-based health check script&lt;/h3&gt;
&lt;p&gt;A port check validates TCP connectivity but not broker health. Use &lt;code&gt;kafka-run-class.sh&lt;/code&gt; with &lt;code&gt;kafka.tools.JmxTool&lt;/code&gt; to query &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; directly. This works without additional tooling if the JDK and Kafka binaries are available.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;JMX_HOST&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;JMX_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;9999&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;MBEAN&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kafka-run-class.sh&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka.tools.JmxTool&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --jmx-url&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;service:jmx:rmi:///jndi/rmi://${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;JMX_HOST&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}:${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;JMX_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}/jmxrmi&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --object-name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;MBEAN&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --attributes&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; Value&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --one-time&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; 2&gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; tail&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;{print $NF}&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-z&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;ERROR: Could not retrieve JMX metric&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -gt&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;WARNING: UnderReplicatedPartitions = ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;OK: UnderReplicatedPartitions = 0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives a more meaningful health signal than a port check. A broker can accept TCP connections while being in a degraded replication state.&lt;/p&gt;
&lt;h3 id=&quot;kafka-admin-api-check&quot;&gt;Kafka Admin API check&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;kafka-broker-api-versions.sh&lt;/code&gt; is a lightweight liveness check that validates the Kafka protocol layer, not just TCP connectivity:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kafka-broker-api-versions.sh&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --bootstrap-server&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; localhost:9092&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the broker is healthy, this returns the list of supported API versions. If the protocol handshake fails or the broker is not responsive, it exits with an error. Use this as a quick manual check or wrap it in a script for automated monitoring. It is a useful final check after a rolling restart to confirm each broker has rejoined the cluster before proceeding to the next.&lt;/p&gt;
&lt;h2 id=&quot;broker-monitoring-tools&quot;&gt;Broker monitoring tools&lt;/h2&gt;
&lt;h3 id=&quot;prometheus-and-grafana&quot;&gt;Prometheus and Grafana&lt;/h3&gt;
&lt;p&gt;The most common self-hosted stack. The Prometheus JMX Exporter scrapes broker JMX metrics, Prometheus stores them as time-series data, and Grafana renders dashboards. Community dashboards for Kafka are available on Grafana Labs, though quality and metric coverage vary. Maintaining a complete and accurate Kafka dashboard requires ongoing attention as cluster topology and Kafka versions change.&lt;/p&gt;
&lt;h3 id=&quot;managed-monitoring-platforms-datadog-new-relic&quot;&gt;Managed monitoring platforms (Datadog, New Relic)&lt;/h3&gt;
&lt;p&gt;Agent-based collection where vendor agents handle JMX scraping and forwarding. Both Datadog and New Relic provide out-of-the-box Kafka dashboards and alert policies, which reduces setup time compared to the self-hosted Prometheus stack. Cost scales with host count and metrics volume. Default alert thresholds may need adjustment for high-throughput deployments.&lt;/p&gt;
&lt;h3 id=&quot;confluent-control-center&quot;&gt;Confluent Control Center&lt;/h3&gt;
&lt;p&gt;Available in Confluent Platform, not Apache Kafka. Provides deep integration with Confluent-specific metrics and a built-in interface for broker health, topic management, and consumer lag. Less useful if you are running vanilla Apache Kafka.&lt;/p&gt;
&lt;h3 id=&quot;kpow-by-factor-house&quot;&gt;Kpow by Factor House&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is purpose-built for Kafka observability. It surfaces broker health, partition state, ISR status, and throughput metrics without requiring a separate Prometheus stack or Grafana instance, and it runs inside your own network. Try it free for 30 days.&lt;/p&gt;
&lt;h2 id=&quot;alerting-strategy-for-broker-monitoring&quot;&gt;Alerting strategy for broker monitoring&lt;/h2&gt;
&lt;p&gt;The goal is to page on-call when there is an active data risk and route everything else to a dashboard or asynchronous channel. Static thresholds on percentage-based metrics can generate false alarms during quiet periods: a single failed request in a low-traffic window can push an error rate to 50% and trigger a high-severity alert despite having no real impact. Organize alerts by severity:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Metrics&lt;/th&gt;
&lt;th&gt;Check interval&lt;/th&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P0&lt;/td&gt;
&lt;td&gt;Immediate cluster health&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;, &lt;code&gt;ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15 seconds&lt;/td&gt;
&lt;td&gt;On-call pager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;Data safety&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;, &lt;code&gt;IsrShrinksPerSec&lt;/code&gt;, &lt;code&gt;FencedBrokerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30 seconds&lt;/td&gt;
&lt;td&gt;High-priority chat or pager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;Performance signals&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TotalTimeMs&lt;/code&gt; P99, broker throughput, thread idle percentages&lt;/td&gt;
&lt;td&gt;1 minute&lt;/td&gt;
&lt;td&gt;Slack channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P3&lt;/td&gt;
&lt;td&gt;Capacity planning&lt;/td&gt;
&lt;td&gt;CPU, memory pools, disk usage&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;Email or ticket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P4&lt;/td&gt;
&lt;td&gt;Deep diagnostics&lt;/td&gt;
&lt;td&gt;Per-partition metrics, thread dumps&lt;/td&gt;
&lt;td&gt;As needed&lt;/td&gt;
&lt;td&gt;Dashboard only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Recommended thresholds based on the &lt;a href=&quot;https://kafka.apache.org/documentation/#monitoring&quot;&gt;Apache Kafka documentation&lt;/a&gt; and operational guidance:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Warning threshold&lt;/th&gt;
&lt;th&gt;Critical threshold&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 for &amp;gt; 2 minutes&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 for &amp;gt; 5 minutes&lt;/td&gt;
&lt;td&gt;Short-lived spikes during rolling restarts are expected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt; (cluster sum)&lt;/td&gt;
&lt;td&gt;!= 1&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Any deviation is immediately critical; no grace period&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Alert immediately; affected partitions are unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.30&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.20&lt;/td&gt;
&lt;td&gt;Evaluate on a sustained basis, not momentary spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.30&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.20&lt;/td&gt;
&lt;td&gt;Same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk usage&lt;/td&gt;
&lt;td&gt;&amp;gt; 70%&lt;/td&gt;
&lt;td&gt;&amp;gt; 85%&lt;/td&gt;
&lt;td&gt;Kafka stops accepting produce requests when the log directory fills completely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC pause duration&lt;/td&gt;
&lt;td&gt;&amp;gt; 500ms&lt;/td&gt;
&lt;td&gt;&amp;gt; 1 second&lt;/td&gt;
&lt;td&gt;Stop-the-world pauses above 1 second cause ISR shrinks and heartbeat timeouts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Link every production alert to a runbook. When an alert fires, the on-call engineer should be able to identify the root cause and begin remediation without first looking up what the metric means. A useful runbook includes the metric’s JMX MBean source, related broker log entries to check, and step-by-step remediation for the most common causes of that alert.&lt;/p&gt;
&lt;h2 id=&quot;common-broker-issues-and-how-to-diagnose-them&quot;&gt;Common broker issues and how to diagnose them&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Relevant metrics&lt;/th&gt;
&lt;th&gt;Likely root cause&lt;/th&gt;
&lt;th&gt;Diagnosis and remediation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; rising&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;, &lt;code&gt;IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follower broker is slow or unreachable&lt;/td&gt;
&lt;td&gt;Check GC pause times and network connectivity on the lagging follower; check disk I/O on the follower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Produce latency spikes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TotalTimeMs&lt;/code&gt; (Produce), &lt;code&gt;LocalTimeMs&lt;/code&gt;, disk I/O&lt;/td&gt;
&lt;td&gt;Disk write saturation&lt;/td&gt;
&lt;td&gt;Decompose &lt;code&gt;TotalTimeMs&lt;/code&gt; into sub-phases; check disk throughput; consider partition reassignment to less-loaded brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitions&lt;/code&gt; suddenly non-zero&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;, &lt;code&gt;ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Leader broker crashed with no available replicas&lt;/td&gt;
&lt;td&gt;Check broker startup logs; if replication factor was 1, data may be lost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network thread exhaustion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Too many slow consumers or large fetch sizes&lt;/td&gt;
&lt;td&gt;Increase &lt;code&gt;num.network.threads&lt;/code&gt;; investigate slow consumers; consider reducing fetch size on affected consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request handler exhaustion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Too many concurrent requests or heavy compression&lt;/td&gt;
&lt;td&gt;Increase &lt;code&gt;num.io.threads&lt;/code&gt;; check for compression-related CPU spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker JVM out of memory&lt;/td&gt;
&lt;td&gt;JVM heap, GC events&lt;/td&gt;
&lt;td&gt;Heap too small for partition count&lt;/td&gt;
&lt;td&gt;Increase broker heap (&lt;code&gt;-Xmx&lt;/code&gt;); check for large message batches; review partition replica count on that broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker failing to start&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OfflineLogDirectoryCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Corrupt log directory or disk failure&lt;/td&gt;
&lt;td&gt;Check broker startup logs; run &lt;code&gt;kafka-log-dirs.sh&lt;/code&gt; to identify the offline directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISR churning (shrinks and expands)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt;, &lt;code&gt;IsrExpandsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GC pauses or network jitter causing replicas to briefly fall behind&lt;/td&gt;
&lt;td&gt;Check GC logs; consider tuning &lt;code&gt;replica.lag.time.max.ms&lt;/code&gt;; verify network stability between brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk space growing unexpectedly&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uncleanable-bytes&lt;/code&gt;, &lt;code&gt;DeadThreadCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log cleaner thread has failed silently&lt;/td&gt;
&lt;td&gt;Check whether &lt;code&gt;DeadThreadCount&lt;/code&gt; is greater than 0; if log cleaner threads have died, a broker restart is required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;kafka-broker-monitoring-best-practices&quot;&gt;Kafka broker monitoring best practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitor all three layers: Kafka process metrics, JVM metrics, and host OS metrics. A problem at any layer will eventually surface in the others.&lt;/li&gt;
&lt;li&gt;Alert on &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; and &lt;code&gt;ActiveControllerCount&lt;/code&gt; before everything else. These are the leading indicators of cluster health degradation.&lt;/li&gt;
&lt;li&gt;Set disk usage alerts at 70% and 85%. Kafka stops accepting produce requests when the log directory fills completely and does not degrade gracefully before that threshold.&lt;/li&gt;
&lt;li&gt;Use a lightweight process check script as a first-line detector. It can catch broker failures before your metrics pipeline does, particularly in the window between a crash and the first missed metrics scrape.&lt;/li&gt;
&lt;li&gt;Keep your JMX Exporter configuration in version control and treat it as part of your Kafka infrastructure. Changes to it affect what your dashboards and alerts can observe.&lt;/li&gt;
&lt;li&gt;Do not rely on consumer lag alone as a broker health signal. Consumer lag is a symptom; broker metrics tell you the cause.&lt;/li&gt;
&lt;li&gt;In KRaft mode, audit your dashboard configuration during and after the migration. ZooKeeper-related MBeans are removed; KRaft-specific MBeans such as &lt;code&gt;FencedBrokerCount&lt;/code&gt; and &lt;code&gt;LastAppliedRecordLagMs&lt;/code&gt; appear in their place.&lt;/li&gt;
&lt;li&gt;Link every production alert to a runbook that includes the metric’s JMX MBean source, related log entries to check, and remediation steps for the most common causes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;monitor-kafka-brokers-with-factor-house&quot;&gt;Monitor Kafka brokers with Factor House&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; surfaces broker health, partition state, ISR status, and throughput metrics from inside your own network. You get visibility into the metrics covered in this article without running a separate Prometheus stack or maintaining Grafana dashboards. Give it a try with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka cluster monitoring</title><link>https://factorhouse.io/articles/kafka-cluster-monitoring/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-cluster-monitoring/</guid><description>What to monitor at the Kafka cluster level: key JMX metrics, multi-broker collection, alerting thresholds, capacity signals, and a health check script.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kafka clusters are typically monitored from the broker up: process health checks, JVM metrics, disk alerts, per-broker request latencies. That instrumentation is necessary, but it has a blind spot. A broker can report normal metrics in isolation while the cluster as a whole is in a degraded state. Partition leadership can be unbalanced. Replication can be falling behind across the fleet. The controller can fail silently. None of these conditions are visible from a single broker’s perspective.&lt;/p&gt;
&lt;p&gt;This article covers cluster-level monitoring: the aggregated, cross-broker view that reveals how the cluster is functioning as a unit. It covers the metrics that matter at this layer, how to collect them across a broker fleet, how to alert on them effectively, and how to diagnose the most common cluster-level failures.&lt;/p&gt;
&lt;p&gt;Per-broker internals — request thread pools, JVM heap, disk I/O — are covered in the &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;Kafka broker monitoring&lt;/a&gt; article. Consumer-side lag monitoring is covered in the &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;Kafka consumer monitoring&lt;/a&gt; article. See the &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;Kafka monitoring&lt;/a&gt; guide for the full picture.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Cluster monitoring is different from broker monitoring: it gives you an aggregated view of replication health, partition distribution, and control plane state across all brokers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt;, &lt;code&gt;OfflinePartitionsCount&lt;/code&gt;, and &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; are the three cluster-level metrics that most directly signal data availability risk.&lt;/li&gt;
&lt;li&gt;In KRaft mode (Kafka 3.3+ GA, default from 4.0), the control plane metrics change — ZooKeeper-era metrics are replaced by KRaft-native equivalents.&lt;/li&gt;
&lt;li&gt;A cluster health check script that verifies five or six key metrics is usually enough to catch the most serious problems; the value is in running it consistently and alerting on deviations.&lt;/li&gt;
&lt;li&gt;Capacity planning at the cluster level is about trend monitoring, not single-point thresholds — watch bytes-in per broker, disk utilisation rate, and partition count per broker over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-cluster-monitoring&quot;&gt;What is Kafka cluster monitoring?&lt;/h2&gt;
&lt;p&gt;Cluster monitoring is the practice of observing the aggregated state of all brokers rather than the internals of any single one. The distinction matters in practice.&lt;/p&gt;
&lt;p&gt;Broker monitoring tells you whether an individual broker is healthy: its request thread utilisation, JVM heap pressure, disk throughput, and local ISR state. Cluster monitoring tells you whether the cluster as a whole is functioning: whether replication is consistent across the fleet, whether partition leadership is balanced, whether the controller is in a valid state, and whether the cluster has capacity headroom.&lt;/p&gt;
&lt;p&gt;Both layers are necessary. Cluster health checks can miss per-broker pathologies that are not yet visible at the aggregate level. A JVM GC pause on one broker, for example, may not immediately register as a replication deficit on the cluster health dashboard. Conversely, per-broker monitoring alone will not surface a partition imbalance, a controller failure, or a sustained ISR shrink rate that only becomes meaningful when measured across the fleet.&lt;/p&gt;
&lt;p&gt;The same JMX interface exposes data at both levels. The difference is in how you query and aggregate it.&lt;/p&gt;
&lt;h2 id=&quot;key-cluster-level-metrics&quot;&gt;Key cluster-level metrics&lt;/h2&gt;
&lt;p&gt;Cluster-level metrics fall into two categories: metrics that are properties of the cluster as a whole (controller state, partition distribution), and metrics that are only meaningful when aggregated across brokers (replication health, throughput). The four groups below cover both.&lt;/p&gt;
&lt;h3 id=&quot;control-plane-health&quot;&gt;Control plane health&lt;/h3&gt;
&lt;p&gt;The controller is the broker responsible for partition leader elections and cluster metadata management. There is always exactly one active controller in a functioning cluster. Deviations are critical.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Target / alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of active controllers on this broker (0 or 1). Must sum to exactly 1 across all brokers.&lt;/td&gt;
&lt;td&gt;Alert (critical) if cluster-wide sum is not exactly 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of partitions with no available leader. Reported by the active controller.&lt;/td&gt;
&lt;td&gt;Alert (critical) if &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LeaderElectionRateAndTimeMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate and duration of partition leader elections. Frequent or slow elections indicate controller instability or broker churn.&lt;/td&gt;
&lt;td&gt;Alert (warning) if rate is sustained above baseline; alert (critical) if P99 duration is elevated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EventQueueSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=ControllerEventManager,name=EventQueueSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pending administrative events queued on the controller.&lt;/td&gt;
&lt;td&gt;Alert (warning) if &amp;gt; 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AvgIdleRatio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=ControllerEventManager,name=AvgIdleRatio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fraction of time the controller event thread is idle. A value approaching 0 indicates a saturated controller.&lt;/td&gt;
&lt;td&gt;Alert (warning) if approaching 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;KRaft mode:&lt;/strong&gt; In KRaft mode (Kafka 3.3+, default from 4.0), the following metrics replace ZooKeeper-era control plane telemetry. Verify MBean paths against the Kafka version in use.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Target / alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LastAppliedRecordLagMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=LastAppliedRecordLagMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delay between metadata commits on the active controller and local application on standby controllers. Elevated values mean metadata changes are propagating slowly.&lt;/td&gt;
&lt;td&gt;Alert (warning) if elevated and sustained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MetadataErrorCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=KafkaController,name=MetadataErrorCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count of failed metadata log operations.&lt;/td&gt;
&lt;td&gt;Alert (critical) if &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;current-state&lt;/code&gt; (quorum state)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=raft-metrics,name=current-state&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;State of the KRaft quorum member on this broker (Leader, Follower, Candidate).&lt;/td&gt;
&lt;td&gt;Alert if controller broker is not Leader&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commit-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=raft-metrics,name=commit-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average latency for KRaft metadata log commits.&lt;/td&gt;
&lt;td&gt;Alert (warning) if elevated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A KRaft quorum of size &lt;code&gt;n&lt;/code&gt; (typically an odd number: 3 or 5) requires a strict majority of &lt;code&gt;floor(n/2) + 1&lt;/code&gt; active nodes to elect a leader and accept metadata commits. If the active quorum drops below this threshold, the control plane enters a read-only mode. Brokers continue to process produce and consume requests for existing partitions, but topic creation, partition reassignment, and ISR state modifications are blocked. If a partition leader fails while the quorum is unavailable, client requests to that partition will time out.&lt;/p&gt;
&lt;h3 id=&quot;replication-fleet-health&quot;&gt;Replication fleet health&lt;/h3&gt;
&lt;p&gt;These are the most operationally significant cluster-level metrics. They reflect whether data is being replicated as configured across all brokers, not just on one.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Target / alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions where the current ISR count is less than the replication factor. Sum across all brokers for the cluster total.&lt;/td&gt;
&lt;td&gt;Alert (critical) if sum &amp;gt; 0 for more than 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partitions with fewer ISRs than &lt;code&gt;min.insync.replicas&lt;/code&gt;. Producers configured with &lt;code&gt;acks=all&lt;/code&gt; will receive errors.&lt;/td&gt;
&lt;td&gt;Alert (critical) if &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UncleanLeaderElectionsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate of elections where a non-ISR replica was promoted to leader. Indicates data loss has occurred or is imminent.&lt;/td&gt;
&lt;td&gt;Alert (critical) if &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate at which replicas are leaving the ISR. Track per broker and as a cluster sum.&lt;/td&gt;
&lt;td&gt;Alert (warning) if sustained above zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsrExpandsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=IsrExpandsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate at which replicas are rejoining the ISR.&lt;/td&gt;
&lt;td&gt;Monitor alongside &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; — sustained shrinks paired with expands indicate ISR instability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Note that &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; is technically a per-broker metric, but it only becomes useful as a cluster-level aggregate. A URP count of zero on one broker says nothing about the remaining brokers. Sum it across the fleet.&lt;/p&gt;
&lt;h3 id=&quot;partition-distribution&quot;&gt;Partition distribution&lt;/h3&gt;
&lt;p&gt;These metrics indicate whether work is balanced evenly across the broker fleet. Imbalances affect both throughput and fault tolerance.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Target / alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=PartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total partition replicas on this broker. Compare across brokers.&lt;/td&gt;
&lt;td&gt;Alert (warning) if the spread between the highest and lowest broker exceeds 20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LeaderCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=ReplicaManager,name=LeaderCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of partition leaders on this broker. Leaders handle all produce and fetch traffic for their partitions.&lt;/td&gt;
&lt;td&gt;Alert (warning) if leader distribution is significantly uneven across brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Uneven partition or leader counts after a broker restart or rolling upgrade are common and typically self-correct via the preferred leader election mechanism. Alert only on sustained imbalance.&lt;/p&gt;
&lt;h3 id=&quot;throughput-and-capacity-signals&quot;&gt;Throughput and capacity signals&lt;/h3&gt;
&lt;p&gt;These metrics inform both capacity planning and short-term incident response.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric name&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Target / alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BytesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytes received per second per broker. Track per broker and as a cluster total.&lt;/td&gt;
&lt;td&gt;Alert (warning) when a broker’s throughput exceeds 70-80% of its network or disk write capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BytesOutPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytes sent per second per broker. Includes replication traffic.&lt;/td&gt;
&lt;td&gt;Monitor alongside &lt;code&gt;BytesInPerSec&lt;/code&gt; — replication multiplies egress by the replication factor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MessagesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Messages received per second. Useful when message size varies across topics.&lt;/td&gt;
&lt;td&gt;Cross-check against &lt;code&gt;BytesInPerSec&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;cluster-vs-broker-monitoring-where-the-line-sits&quot;&gt;Cluster vs broker monitoring: where the line sits&lt;/h2&gt;
&lt;p&gt;The cluster and broker layers are not always cleanly separable. Several metrics appear in per-broker JMX output but only carry meaning when interpreted at the cluster level.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; is the clearest example: it is exposed per broker, but aggregating it across the fleet gives you the total replication deficit. A single broker reporting zero URPs is not informative if another broker is lagging. &lt;code&gt;ActiveControllerCount&lt;/code&gt; is only interpretable as a cluster-wide sum — a value of 1 on one broker is normal; a cluster-wide sum of 0 or 2 is critical. Individual broker throughput metrics (&lt;code&gt;BytesInPerSec&lt;/code&gt; per broker) are per-broker values that feed cluster-level capacity planning by revealing which brokers are carrying disproportionate load.&lt;/p&gt;
&lt;p&gt;For replication and controller metrics, always aggregate. For throughput, collect per-broker and compare across the fleet. The &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;Kafka broker monitoring&lt;/a&gt; article covers the internals — request thread saturation, JVM heap, disk flush latency — that complement the cluster view covered here.&lt;/p&gt;
&lt;h2 id=&quot;multi-broker-observability-setup&quot;&gt;Multi-broker observability setup&lt;/h2&gt;
&lt;h3 id=&quot;the-collection-challenge&quot;&gt;The collection challenge&lt;/h3&gt;
&lt;p&gt;JMX is a per-process interface. To get a cluster-wide picture, you need to scrape every broker’s JMX endpoint and aggregate the results centrally. In a three-broker cluster this is manageable manually; in a fleet of dozens, service discovery and automated aggregation become essential.&lt;/p&gt;
&lt;p&gt;The most common production approach is Prometheus with the JMX Exporter, because it handles service discovery natively and the aggregation logic lives in PromQL.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-and-jmx-exporter&quot;&gt;Prometheus and JMX Exporter&lt;/h3&gt;
&lt;p&gt;Run the JMX Exporter as an in-process Java agent on each broker. Running it as an agent rather than a remote poller avoids authentication overhead and reduces JVM thread overhead compared to the remote polling approach:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;KAFKA_OPTS=&quot;-javaagent:/opt/prometheus/jmx_prometheus_javaagent-0.16.1.jar=7071:/etc/kafka/kafka-jmx-config.yml&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The agent exposes Kafka’s JMX MBeans as Prometheus-formatted metrics on an HTTP endpoint (typically port 7071). Configure Prometheus with a scrape job that discovers all broker endpoints. In Kubernetes, a &lt;code&gt;ServiceMonitor&lt;/code&gt; resource handles this automatically; in bare-metal or VM deployments, use static targets in &lt;code&gt;prometheus.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Aggregation across brokers happens in PromQL. To get the cluster-wide under-replicated partition count:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sum(kafka_server_replicamanager_underreplicatedpartitions)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kafka-specific JMX Exporter YAML configuration files are maintained by the community. The Bitnami and confluentinc examples on GitHub are widely used starting points and include pre-built allowlists for the metrics covered in this article.&lt;/p&gt;
&lt;h3 id=&quot;other-collection-approaches&quot;&gt;Other collection approaches&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Datadog:&lt;/strong&gt; Uses JMXFetch via the Agent daemon. Autodiscovery maps JMX ports to integrations automatically. The default cap of 350 metrics per instance means you will need to configure which metrics are collected carefully for larger clusters.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; Uses the native Kafka Admin Client and Consumer APIs rather than JMX — no sidecar agent or broker-side configuration changes required.&lt;/p&gt;
&lt;p&gt;If you’re evaluating monitoring tools, the &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;Kafka observability tools comparison&lt;/a&gt; article covers the trade-offs in more detail.&lt;/p&gt;
&lt;h2 id=&quot;cluster-health-check-script&quot;&gt;Cluster health check script&lt;/h2&gt;
&lt;p&gt;A short Python script that checks six cluster-level conditions gives you a pass/fail signal suitable for cron jobs, CI pipelines, or on-call runbooks. The value is in running it consistently on a schedule and routing its output to your alerting channel.&lt;/p&gt;
&lt;h3 id=&quot;what-the-script-checks&quot;&gt;What the script checks&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt; sum equals exactly 1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt; equals 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; sum equals 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt; equals 0&lt;/li&gt;
&lt;li&gt;ISR shrink rate is below a configurable threshold&lt;/li&gt;
&lt;li&gt;Leader count distribution — no single broker holds more than a configurable percentage of all leaders&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;
&lt;p&gt;The Kafka Admin Client (&lt;code&gt;kafka-python&lt;/code&gt; or &lt;code&gt;confluent-kafka&lt;/code&gt;) exposes cluster metadata directly without requiring JMX access. Use &lt;code&gt;AdminClient.describe_cluster()&lt;/code&gt; to retrieve broker and controller state, and &lt;code&gt;list_topics()&lt;/code&gt; with topic metadata to enumerate partition and leader assignments.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; and &lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt; are not exposed via the Admin API; for those, query the JMX Exporter HTTP endpoint if Prometheus is already deployed, or fall back to &lt;code&gt;jmxterm&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; kafka &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaAdminClient&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; requests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;BROKERS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker1:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker2:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker3:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;JMX_EXPORTER_HOSTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker1:7071&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker2:7071&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker3:7071&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LEADER_SKEW_THRESHOLD&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0.20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; check_active_controller&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(admin):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    cluster_meta &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; admin.describe_cluster()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    controller &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; cluster_meta.controller&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; controller &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; None&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; False&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;No active controller&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; True&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Controller: broker &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;controller.id&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; check_urp_from_jmx&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(hosts):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    total_urp &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; host &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; hosts:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            r &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; requests.get(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;http://&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/metrics&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;timeout&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; line &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; r.text.splitlines():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;                    &quot;kafka_server_replicamanager_underreplicatedpartitions&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                    and&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; line.startswith(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;#&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                ):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                    total_urp &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; float&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(line.split()[&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        except&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; e:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;            print&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;  Warning: could not reach &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; total_urp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; check_leader_skew&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(admin):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    topics &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; admin.list_topics()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    leader_counts &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topic_metadata &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topics.topics.values():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; partition &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topic_metadata.partitions.values():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            leader &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; partition.leader&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            leader_counts[leader] &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; leader_counts.get(leader, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; leader_counts:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; True&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;No partitions&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    counts &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(leader_counts.values())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    skew &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;max&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(counts) &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; min&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(counts)) &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; max&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(counts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    ok &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; skew &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; LEADER_SKEW_THRESHOLD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ok, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Leader skew: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;skew&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:.1%&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; (threshold &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{LEADER_SKEW_THRESHOLD&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:.0%&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; run_health_check&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    admin &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaAdminClient(&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;bootstrap_servers&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;BROKERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    results &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    ok, msg &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; check_active_controller(admin)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    results.append((&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;ActiveControllerCount&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, ok, msg))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    urp &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; check_urp_from_jmx(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;JMX_EXPORTER_HOSTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    results.append((&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;UnderReplicatedPartitions&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, urp &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;URP count: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{int&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(urp)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    ok, msg &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; check_leader_skew(admin)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    results.append((&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;LeaderSkew&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, ok, msg))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    admin.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kafka cluster health check&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    all_ok &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; True&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; check, passed, detail &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; results:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        status &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;PASS&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; passed &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;FAIL&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;  [&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;detail&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; passed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            all_ok &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; all_ok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; __name__&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; sys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    sys.exit(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; run_health_check() &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The script reflects point-in-time state. A transient ISR shrink during a rolling restart will register as a failure unless you add a wait-and-recheck delay for replication metrics.&lt;/li&gt;
&lt;li&gt;It does not replace continuous time-series monitoring — it is a runbook tool, not a substitute for alerting.&lt;/li&gt;
&lt;li&gt;JVM and OS metrics are not covered here; those are addressed in the &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;broker monitoring&lt;/a&gt; article.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;consumer monitoring&lt;/a&gt; article contains a consumer lag script that complements this one. Together they cover the three main health dimensions: cluster-wide replication and control plane state (this article), per-broker internals (broker monitoring), and consumer group lag (consumer monitoring).&lt;/p&gt;
&lt;h2 id=&quot;capacity-planning-using-cluster-metrics&quot;&gt;Capacity planning using cluster metrics&lt;/h2&gt;
&lt;p&gt;Capacity planning at the cluster level is about detecting trends early enough to act before they become incidents. The signals below are leading indicators — most warrant investigation and planning rather than immediate paging.&lt;/p&gt;
&lt;h3 id=&quot;when-to-add-brokers&quot;&gt;When to add brokers&lt;/h3&gt;
&lt;p&gt;The primary signals that a new broker is needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BytesInPerSec per broker&lt;/strong&gt; is consistently above 70% of the broker’s network or disk write capacity. A useful rule of thumb is to size for 3x peak traffic to allow for replication overhead and burst headroom. Replication multiplies egress by the replication factor: a cluster with a replication factor of 3 and 1 Gbps of ingest generates roughly 3 Gbps of outbound replication traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RequestHandlerAvgIdlePercent&lt;/strong&gt; is below 20% on multiple brokers over a sustained period, indicating that the request handler thread pool is saturated. This is typically caused by slow disk flushes, high JVM GC pauses, or high concurrent replication load from lagging followers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk utilisation&lt;/strong&gt; is growing at a rate that will exhaust available space within your retention window, with no further compression or retention policy levers available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PartitionCount per broker&lt;/strong&gt; is becoming significantly uneven after broker additions or failures — partition reassignment may be needed before adding a new broker.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;partition-count-planning&quot;&gt;Partition count planning&lt;/h3&gt;
&lt;p&gt;Partitions are the unit of parallelism in Kafka. Too few limits throughput; too many increases controller overhead and replication cost.&lt;/p&gt;
&lt;p&gt;Track total partition count and partition count per broker. The controller’s &lt;code&gt;EventQueueSize&lt;/code&gt; rising during periods of high topic creation activity is a signal that the cluster is approaching the limits of what the controller can manage comfortably.&lt;/p&gt;
&lt;p&gt;For older Kafka versions (pre-2.6), a commonly cited practical limit is approximately 4,000 partitions per broker, though this varies considerably with hardware and workload. With KRaft, the practical limit is significantly higher — the consolidated metadata management architecture removes the ZooKeeper bottleneck that was the primary constraint in earlier versions. Consult the release notes and benchmark reports for the specific version you are running.&lt;/p&gt;
&lt;h3 id=&quot;replication-factor-auditing&quot;&gt;Replication factor auditing&lt;/h3&gt;
&lt;p&gt;Topics created with &lt;code&gt;replication.factor=1&lt;/code&gt; in a multi-broker cluster represent a silent durability risk. A single broker failure takes those partitions offline with no replication fallback.&lt;/p&gt;
&lt;p&gt;Audit replication factors on a schedule using &lt;code&gt;kafka-topics.sh --describe&lt;/code&gt; or the Admin API:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; kafka &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaAdminClient&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;admin &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaAdminClient(&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;bootstrap_servers&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;broker1:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;topic_names &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(admin.list_topics())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;topics &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; admin.describe_topics(topic_names)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topics:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; partition &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; t.partitions.values():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(partition.replicas) &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;            print&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;t.topic&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; partition &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;partition.partition&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;: &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                  f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;replication factor &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(partition.replicas)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;admin.close()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;retention-and-storage-forecasting&quot;&gt;Retention and storage forecasting&lt;/h3&gt;
&lt;p&gt;Monitor disk utilisation per broker and extrapolate the growth rate from the past 7 and 30 days. &lt;code&gt;BytesInPerSec&lt;/code&gt; is the primary driver of storage growth once you account for the replication factor and compression codec.&lt;/p&gt;
&lt;p&gt;If storage is growing faster than expected, the first lever to check is log retention settings — both time-based (&lt;code&gt;retention.ms&lt;/code&gt;) and size-based (&lt;code&gt;retention.bytes&lt;/code&gt;). Reducing retention decreases storage pressure but can break consumers that have fallen behind. Document the trade-off before changing retention on production topics.&lt;/p&gt;
&lt;h2 id=&quot;alerting-strategy-for-cluster-level-monitoring&quot;&gt;Alerting strategy for cluster-level monitoring&lt;/h2&gt;
&lt;p&gt;Not all metrics warrant the same response. Structuring alerts into two tiers avoids alert fatigue and ensures the right response time for each condition.&lt;/p&gt;
&lt;h3 id=&quot;critical-alerts-page-on-call-immediately&quot;&gt;Critical alerts (page on-call immediately)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt; (sum)&lt;/td&gt;
&lt;td&gt;Not exactly 1&lt;/td&gt;
&lt;td&gt;No controller or split-brain — metadata operations are halted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Partitions are completely unavailable to producers and consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Producers with &lt;code&gt;acks=all&lt;/code&gt; are receiving errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 for &amp;gt; 5 minutes&lt;/td&gt;
&lt;td&gt;Sustained replication deficit — data durability is degraded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UncleanLeaderElectionsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;A non-ISR replica was promoted to leader, indicating data loss risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Working Prometheus alert rules for the two most critical conditions:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;groups&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka_cluster_alerts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    rules&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;KafkaOfflinePartitions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka_controller_offline_partitions_count &gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;0m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;critical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kafka offline partitions detected&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;{{ $value }} offline partitions on {{ $labels.instance }}. Read and write requests are blocked.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          runbook_url&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;https://wiki.internal/runbooks/kafka-offline-partitions&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;KafkaUnderReplicatedPartitions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;sum(kafka_server_replicamanager_underreplicatedpartitions) &gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;5m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;critical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kafka under-replicated partitions&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;{{ $value }} under-replicated partitions across the cluster for more than 5 minutes.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          runbook_url&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;https://wiki.internal/runbooks/kafka-under-replicated&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;warning-alerts-alert-team-channel-investigate-within-the-hour&quot;&gt;Warning alerts (alert team channel, investigate within the hour)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sustained above zero&lt;/td&gt;
&lt;td&gt;Replicas are falling behind; investigate before it becomes a URP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LeaderCount&lt;/code&gt; skew&lt;/td&gt;
&lt;td&gt;&amp;gt; 20% spread across brokers&lt;/td&gt;
&lt;td&gt;Producer and consumer traffic is unevenly distributed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BytesInPerSec&lt;/code&gt; per broker&lt;/td&gt;
&lt;td&gt;&amp;gt; 70% of capacity&lt;/td&gt;
&lt;td&gt;Approaching saturation on a burst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EventQueueSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 100&lt;/td&gt;
&lt;td&gt;Controller is backlogged; administrative operations will be slow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;alert-inhibition&quot;&gt;Alert inhibition&lt;/h3&gt;
&lt;p&gt;When a broker fails, it typically triggers a cascade: a critical broker-down alert alongside secondary consumer lag and replication warnings. Alertmanager’s inhibition rules let you suppress those secondary warnings while the root-cause alert is active, keeping the on-call view clean:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;inhibit_rules&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;source_match&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      alertname&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;KafkaOfflinePartitions&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;critical&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    target_match&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;warning&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    equal&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;cluster&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;instance&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Alert fatigue note:&lt;/strong&gt; ISR shrinks are expected during rolling restarts and broker upgrades. Consider suppressing &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; warnings during maintenance windows, or requiring a minimum duration of 10 minutes before the alert fires.&lt;/p&gt;
&lt;h2 id=&quot;common-kafka-cluster-level-issues-and-how-to-resolve-them&quot;&gt;Common Kafka cluster-level issues and how to resolve them&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely metrics&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Remediation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Producers receiving &lt;code&gt;NotEnoughReplicasException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UnderMinIsrPartitionCount &amp;gt; 0&lt;/code&gt;, &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; elevated&lt;/td&gt;
&lt;td&gt;One or more brokers are offline or severely lagging, shrinking the ISR below &lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Investigate the lagging broker: check disk, network, and JVM GC metrics. Restore the broker, or reduce &lt;code&gt;min.insync.replicas&lt;/code&gt; temporarily as a last resort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster metadata operations timing out&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EventQueueSize &amp;gt; 100&lt;/code&gt;, &lt;code&gt;LeaderElectionRateAndTimeMs&lt;/code&gt; elevated&lt;/td&gt;
&lt;td&gt;Controller is overloaded — too many concurrent topic operations, or a large total partition count&lt;/td&gt;
&lt;td&gt;Reduce concurrent topic creation or deletion operations; review total partition count; consider a controller bounce if the queue does not drain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No active controller&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt; sum = 0&lt;/td&gt;
&lt;td&gt;All brokers have lost controller state — typically a ZooKeeper quorum failure (ZK mode) or KRaft quorum loss&lt;/td&gt;
&lt;td&gt;In ZK mode: investigate ZooKeeper ensemble health. In KRaft mode: check the metadata log and quorum state on controller-eligible brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uneven consumer lag across partitions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LeaderCount&lt;/code&gt; skew, &lt;code&gt;BytesInPerSec&lt;/code&gt; per broker uneven&lt;/td&gt;
&lt;td&gt;Partition leader imbalance — one broker holds a disproportionate share of leaders&lt;/td&gt;
&lt;td&gt;Run preferred leader election (&lt;code&gt;kafka-leader-election.sh --type preferred&lt;/code&gt;) or reassign partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk filling faster than expected&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BytesInPerSec&lt;/code&gt; elevated, disk utilisation trending up&lt;/td&gt;
&lt;td&gt;High produce rate, long retention, or insufficient compression&lt;/td&gt;
&lt;td&gt;Review retention settings, check compression codec, evaluate whether a broker addition is needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISR instability (repeated shrinks and expands)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt; and &lt;code&gt;IsrExpandsPerSec&lt;/code&gt; both elevated&lt;/td&gt;
&lt;td&gt;Network instability between brokers, or JVM GC pauses causing follower replicas to miss heartbeats&lt;/td&gt;
&lt;td&gt;Check network error rates between brokers; review GC pause durations on the lagging broker; consider increasing &lt;code&gt;replica.lag.time.max.ms&lt;/code&gt; if GC pauses are a one-time event&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;best-practices-for-kafka-cluster-monitoring&quot;&gt;Best practices for Kafka cluster monitoring&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitor &lt;code&gt;ActiveControllerCount&lt;/code&gt; as a cluster-wide sum, not per broker. A per-broker reading of 1 is normal; only the sum tells you whether the cluster has exactly one controller.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; as a cluster-level aggregate. Sum it across all brokers. A URP count of zero on one broker says nothing if another broker is lagging.&lt;/li&gt;
&lt;li&gt;Separate replication traffic from consumer traffic in your throughput metrics. If &lt;code&gt;BytesOutPerSec&lt;/code&gt; includes both, you cannot tell whether a spike is from a consumer or from replication catch-up.&lt;/li&gt;
&lt;li&gt;Set alert durations on replication metrics, not just thresholds. An ISR shrink during a rolling restart is expected; one that persists for five minutes is not.&lt;/li&gt;
&lt;li&gt;Audit replication factors on a schedule. Topics created with non-standard replication factors are common after high-velocity development cycles and represent silent durability risk.&lt;/li&gt;
&lt;li&gt;In KRaft mode, add &lt;code&gt;LastAppliedRecordLagMs&lt;/code&gt; and &lt;code&gt;MetadataErrorCount&lt;/code&gt; to your standard cluster health dashboard. These have no ZooKeeper equivalents and are easy to overlook when migrating an existing monitoring setup.&lt;/li&gt;
&lt;li&gt;Run a cluster health check script on a cron schedule — every five minutes works well — and route its output to your alerting channel. It catches conditions that continuous metric alerting can miss during gaps in scrape coverage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;monitor-kafka-clusters-with-kpow&quot;&gt;Monitor Kafka clusters with Kpow&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; connects to any Kafka cluster using the native Admin Client and Consumer APIs — no JMX Exporter, no sidecar agent, and no broker-side configuration changes required. It surfaces the cluster-level metrics covered in this article — under-replicated partitions, controller state, partition distribution, throughput per broker — on a single dashboard, with replication health and partition distribution views that update in real time.&lt;/p&gt;
&lt;p&gt;You can give Kpow a try with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. Connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka consumer monitoring and performance tuning</title><link>https://factorhouse.io/articles/kafka-consumer-monitoring/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-consumer-monitoring/</guid><description>Learn which Kafka consumer metrics matter most, how to interpret them, and which configuration changes will improve performance and reduce lag.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Consumer lag is the primary indicator of pipeline health, but it tells you only that a consumer has fallen behind, not why&lt;/li&gt;
&lt;li&gt;Poll idle ratio and rebalance rate reveal problems that lag alone misses&lt;/li&gt;
&lt;li&gt;Effective performance tuning requires matching the right metric to the right configuration parameter&lt;/li&gt;
&lt;li&gt;JMX gives you per-client visibility; tools that query broker metadata directly fill the gaps that client-side instrumentation leaves&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-consumer-monitoring&quot;&gt;What is Kafka consumer monitoring?&lt;/h2&gt;
&lt;p&gt;Kafka consumer monitoring is the practice of collecting and interpreting metrics that describe how consumer applications are reading from Kafka topics. It sits within &lt;a href=&quot;/articles/kafka-monitoring&quot;&gt;Kafka monitoring&lt;/a&gt; as a broader discipline, which also covers broker health, topic throughput, and replication state.&lt;/p&gt;
&lt;p&gt;Consumer monitoring is distinct from broker monitoring in one important way: the metrics you care about are generated on the client side. Kafka consumers expose instrumentation through Java Management Extensions (JMX), which means visibility depends on what the consumer JVM process exposes at runtime. Brokers can be fully operational while consumers fall behind, and broker metrics alone will not surface that.&lt;/p&gt;
&lt;h2 id=&quot;the-most-important-consumer-monitoring-metric&quot;&gt;The most important consumer monitoring metric&lt;/h2&gt;
&lt;h3 id=&quot;consumer-lag&quot;&gt;Consumer lag&lt;/h3&gt;
&lt;p&gt;Consumer lag is the difference between the latest offset written to a partition (the log end offset) and the last offset the consumer group has committed. For each partition:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lag = log_end_offset - committed_offset&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lag tells you how many records a consumer group has yet to process. It is the primary indicator of pipeline health because it reflects the gap between what producers are writing and what consumers have actually acknowledged.&lt;/p&gt;
&lt;p&gt;The JMX metric &lt;code&gt;records-lag-max&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-fetch-manager-metrics&lt;/code&gt;) reports the maximum lag across all partitions assigned to a single consumer instance. This is a per-instance metric. For a full picture of group-level lag across every partition of a topic, you need tooling that queries the broker directly rather than aggregating what individual clients report about themselves.&lt;/p&gt;
&lt;p&gt;One thing worth understanding early: absolute lag values are context-dependent. A lag of 50,000 on a high-throughput analytics topic may represent a few seconds of processing backlog. The same figure on a payment processing topic could represent hours of critical delay. What matters more than the absolute number is whether lag is growing, stable, or draining. More on alerting thresholds below.&lt;/p&gt;
&lt;p&gt;For a detailed treatment of consumer lag specifically, including how to instrument it and respond to it, see the article on &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;Kafka consumer lag monitoring&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;other-key-metrics-to-monitor&quot;&gt;Other key metrics to monitor&lt;/h2&gt;
&lt;h3 id=&quot;throughput&quot;&gt;Throughput&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;records-consumed-rate&lt;/code&gt; and &lt;code&gt;bytes-consumed-rate&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-fetch-manager-metrics&lt;/code&gt;) measure how fast the consumer is reading from the broker. These are your baseline throughput indicators.&lt;/p&gt;
&lt;p&gt;A consumer may have low lag while still running at a fraction of its expected throughput, which can point to producers writing slowly or to fetch configuration that unnecessarily limits batch size. Tracking throughput alongside lag helps distinguish between a consumer that is healthy and one that is barely keeping up.&lt;/p&gt;
&lt;h3 id=&quot;poll-idle-ratio&quot;&gt;Poll idle ratio&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;poll-idle-ratio-avg&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-metrics&lt;/code&gt;) measures the fraction of time the consumer’s poll loop is idle, waiting for the broker to return records. A value close to 1.0 means the consumer is spending most of its time waiting; a value close to 0 means it is spending almost all of its time processing records.&lt;/p&gt;
&lt;p&gt;When poll idle ratio drops consistently toward 0, the consumer’s processing logic is the bottleneck, not the fetch pipeline. In this state, tuning fetch configuration has little effect. The correct response is to reduce per-record processing time, add consumer instances, or increase the topic’s partition count to allow more parallelism.&lt;/p&gt;
&lt;h3 id=&quot;error-rate&quot;&gt;Error rate&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;fetch-error-rate&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-fetch-manager-metrics&lt;/code&gt;) counts failed fetch requests per second. A non-zero error rate points to connectivity issues between the consumer and the broker, authentication failures, or broker-side quota violations. On its own, occasional fetch errors may not cause visible lag if the consumer retries successfully. A sustained error rate typically will. Monitoring it alongside fetch latency helps you determine whether errors are causing delays or being absorbed by the retry logic.&lt;/p&gt;
&lt;h3 id=&quot;rebalance-rate&quot;&gt;Rebalance rate&lt;/h3&gt;
&lt;p&gt;A consumer group rebalance occurs whenever a consumer joins or leaves the group, or when partitions are reassigned. During a rebalance, all consumption for the affected group pauses. This is usually brief, but rebalances that happen frequently, or that take a long time to complete, cause visible lag spikes.&lt;/p&gt;
&lt;p&gt;A rebalance storm occurs when repeated rebalances prevent the group from making meaningful progress between them. Common causes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slow processing loops where the consumer exceeds &lt;code&gt;max.poll.interval.ms&lt;/code&gt; before calling &lt;code&gt;poll()&lt;/code&gt; again&lt;/li&gt;
&lt;li&gt;Overly short &lt;code&gt;session.timeout.ms&lt;/code&gt; values that cause the broker to consider a consumer dead during garbage collection pauses&lt;/li&gt;
&lt;li&gt;Ungraceful shutdowns during rolling deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;join-time-avg&lt;/code&gt; and &lt;code&gt;sync-time-avg&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-coordinator-metrics&lt;/code&gt;) measure the time taken to complete the join and sync phases of each rebalance. If these values are consistently high, rebalances are expensive when they do occur, which amplifies any instability in group membership.&lt;/p&gt;
&lt;h3 id=&quot;commit-rate-and-commit-latency&quot;&gt;Commit rate and commit latency&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;commit-rate&lt;/code&gt; and &lt;code&gt;commit-latency-avg&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-coordinator-metrics&lt;/code&gt;) describe how frequently and how quickly offset commits are completing.&lt;/p&gt;
&lt;p&gt;Commit latency matters because elevated values signal broker responsiveness issues or network degradation. It also has a direct consequence for correctness: if a consumer crashes before its most recent offsets are committed, it reprocesses messages from the last successfully committed point. Higher commit latency widens that reprocessing window.&lt;/p&gt;
&lt;h3 id=&quot;fetch-latency&quot;&gt;Fetch latency&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;fetch-latency-avg&lt;/code&gt; (MBean: &lt;code&gt;kafka.consumer:type=consumer-fetch-manager-metrics&lt;/code&gt;) measures the round-trip time for fetch requests to the broker. This metric is useful as a bridge between consumer-side and broker-side observability. When fetch latency is high, the cause is usually upstream: high broker disk I/O, network saturation, or broker-side throttling. Consumer monitoring surfaces the symptom first; broker monitoring tells you the cause.&lt;/p&gt;
&lt;h3 id=&quot;partition-offsets&quot;&gt;Partition offsets&lt;/h3&gt;
&lt;p&gt;Beyond lag, it is worth tracking how your consumer group manages offsets over time. The &lt;code&gt;auto.offset.reset&lt;/code&gt; configuration determines where a consumer starts reading when no committed offset exists for a partition. The &lt;code&gt;latest&lt;/code&gt; setting means it starts from the newest message, potentially skipping records written before the consumer joined. The &lt;code&gt;earliest&lt;/code&gt; setting means it reads from the beginning of the partition’s retained log. Knowing which policy is in effect is important context for interpreting gaps in consumption, particularly after a new deployment or a consumer group reset.&lt;/p&gt;
&lt;p&gt;Commit frequency also matters operationally. Committing too infrequently widens the reprocessing window after a crash; committing too frequently increases metadata load on the group coordinator.&lt;/p&gt;
&lt;h3 id=&quot;broker-and-infrastructure-metrics&quot;&gt;Broker and infrastructure metrics&lt;/h3&gt;
&lt;p&gt;High broker disk I/O, network saturation, or replica lag will surface in consumer monitoring as elevated fetch latency or increased error rates. The relationship is worth understanding: consumers do not operate independently of the brokers they read from. If consumer fetch latency is climbing but all consumer-side metrics look normal, the issue is upstream. For a full treatment of broker-side monitoring, refer to the upcoming Kafka broker monitoring article.&lt;/p&gt;
&lt;h2 id=&quot;alerting-thresholds&quot;&gt;Alerting thresholds&lt;/h2&gt;
&lt;p&gt;The main risk with consumer lag alerting is relying on static absolute thresholds. A threshold of 10,000 messages means something very different on a topic receiving one million messages per second versus one receiving one hundred. Absolute thresholds in the former case would fire constantly; in the latter, a genuine problem could sit below the threshold for hours.&lt;/p&gt;
&lt;p&gt;A more reliable approach is to alert on the rate of change in lag rather than its absolute value. If lag is stable at 50,000 messages, the consumer is processing at roughly the same rate the producer is writing, and the situation may be acceptable. If lag is growing at 500 messages per second and has been doing so for five minutes, the consumer is falling behind and action is warranted. The Prometheus &lt;code&gt;deriv()&lt;/code&gt; function is useful for this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;deriv(kafka_consumergroup_lag[10m]) &gt; 500&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Complement this with a stall detection alert for cases where the consumer has stopped committing entirely despite having an active backlog:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sum(delta(kafka_consumergroup_current_offset[5m])) by (consumergroup, topic) == 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;and sum(kafka_consumergroup_lag) by (consumergroup, topic) &gt; 1000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A stalled consumer with a growing backlog typically indicates a deadlocked thread, a poison-pill message blocking the processing loop, or a crashed consumer group that has not been restarted.&lt;/p&gt;
&lt;p&gt;For partition-level alerting, &lt;code&gt;records-lag-max&lt;/code&gt; from JMX lets you alert on the worst-performing partition assigned to a given consumer instance. Group-level tooling that aggregates across all partitions of a topic provides a broader view than any single client can offer, and is particularly useful for identifying partition lag skew, where one partition falls significantly behind the average.&lt;/p&gt;
&lt;h2 id=&quot;tooling-for-collecting-and-monitoring-metrics&quot;&gt;Tooling for collecting and monitoring metrics&lt;/h2&gt;
&lt;h3 id=&quot;jmx-and-the-prometheus-jmx-exporter&quot;&gt;JMX and the Prometheus JMX Exporter&lt;/h3&gt;
&lt;p&gt;By default, Kafka consumer JMX metrics are not remotely accessible. To enable scraping, you configure the JMX Prometheus Java Agent at JVM startup:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KAFKA_OPTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;-javaagent:/path/to/jmx_prometheus_javaagent.jar=9404:/path/to/kafka-jmx-config.yml&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This exposes the JMX MBeans on a Prometheus-compatible endpoint that you can scrape and visualise in Grafana. The metrics listed in this article all become available through this approach.&lt;/p&gt;
&lt;p&gt;JMX-based monitoring has a meaningful limitation: the metrics are produced by the client process itself. If the consumer crashes, the metric stream stops. A dead consumer group looks identical to one that has never reported metrics. You do not automatically get a “consumer is down” signal; you get an absence of data.&lt;/p&gt;
&lt;p&gt;For a look at building more effective Kafka dashboards on top of JMX data, see &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Beyond JMX: supercharging Grafana dashboards with high-fidelity metrics&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;external-lag-exporters&quot;&gt;External lag exporters&lt;/h3&gt;
&lt;p&gt;To address the liveness gap in JMX monitoring, external lag exporters query broker metadata directly rather than relying on what each consumer client reports. These tools read the &lt;code&gt;__consumer_offsets&lt;/code&gt; internal topic to track committed offsets and evaluate group health without depending on a live consumer JVM. This means the monitoring plane continues to report accurate lag even when a consumer group is offline.&lt;/p&gt;
&lt;p&gt;Burrow, the open-source lag monitoring service built at LinkedIn, takes this approach. It evaluates consumer group health over a sliding window of recent commits, classifying groups as OK, WARN, ERR, STALL, or STOP based on commit activity and lag trend, without requiring you to set static thresholds. This distinction matters in practice: a group that is STALL (committing the same offsets repeatedly) is a different problem from one that is STOP (no commits at all), and treating them the same way with a simple lag threshold would miss the difference.&lt;/p&gt;
&lt;h3 id=&quot;opentelemetry&quot;&gt;OpenTelemetry&lt;/h3&gt;
&lt;p&gt;OpenTelemetry support for Kafka consumer metrics is available through the OpenTelemetry Java Agent and community instrumentation libraries. Integration depth varies by Kafka client version, and Prometheus-based JMX scraping remains the more established approach for Kafka-specific observability. If your organisation already operates an OpenTelemetry pipeline, it is worth evaluating whether Kafka consumer metrics can be routed through it consistently, though broker-side visibility will still likely require a dedicated exporter.&lt;/p&gt;
&lt;h2 id=&quot;kafka-consumer-performance-tuning&quot;&gt;Kafka consumer performance tuning&lt;/h2&gt;
&lt;h3 id=&quot;reducing-consumer-lag&quot;&gt;Reducing consumer lag&lt;/h3&gt;
&lt;p&gt;The first step when lag is growing is to check poll idle ratio. If the value is well above 0, the consumer has capacity to process more records per fetch cycle, and the bottleneck is likely in how data is being fetched rather than how it is being processed.&lt;/p&gt;
&lt;p&gt;To increase fetch efficiency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Raise &lt;code&gt;fetch.min.bytes&lt;/code&gt; (default: 1 byte) to instruct the broker to wait until it has a meaningful batch of data before responding. This reduces fetch request frequency and improves throughput at the cost of slightly higher latency per fetch.&lt;/li&gt;
&lt;li&gt;Raise &lt;code&gt;fetch.max.wait.ms&lt;/code&gt; (default: 500ms) to control how long the broker waits to accumulate &lt;code&gt;fetch.min.bytes&lt;/code&gt; worth of data. Raising this allows the broker to return larger batches in each response.&lt;/li&gt;
&lt;li&gt;Increase &lt;code&gt;max.poll.records&lt;/code&gt; to allow the consumer to process more records per poll loop iteration. If you do this, confirm that your batch processing time stays within &lt;code&gt;max.poll.interval.ms&lt;/code&gt;; exceeding it triggers a rebalance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If poll idle ratio is near 0, the bottleneck is in processing logic, not in fetching. Tuning fetch parameters will have little effect. The options are to reduce per-record processing time (for example, by batching downstream writes or switching to asynchronous I/O), to add consumer instances up to the partition count, or to increase the topic’s partition count to raise the parallelism ceiling.&lt;/p&gt;
&lt;h3 id=&quot;reducing-rebalance-frequency&quot;&gt;Reducing rebalance frequency&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;session.timeout.ms&lt;/code&gt; and &lt;code&gt;heartbeat.interval.ms&lt;/code&gt; configuration pair is worth understanding precisely. The session timeout is the window within which the consumer must send at least one heartbeat before the group coordinator declares it dead. If the poll loop takes too long due to slow message processing or a GC pause, the heartbeat thread may not run within that window, and the consumer will be evicted from the group.&lt;/p&gt;
&lt;p&gt;A configuration that provides more margin for transient pauses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;session.timeout.ms=45000&lt;/code&gt; gives the consumer 45 seconds to heartbeat before being considered dead&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heartbeat.interval.ms=15000&lt;/code&gt; sends heartbeats every 15 seconds, one-third of the session timeout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;max.poll.interval.ms&lt;/code&gt; is separate from the session timeout. It defines the maximum allowed time between successive &lt;code&gt;poll()&lt;/code&gt; calls. If processing a batch takes longer than this value, the consumer will be removed from the group regardless of whether its heartbeats are current. If your processing is legitimately slow, increase this value to match expected batch processing time rather than masking the issue by reducing batch size.&lt;/p&gt;
&lt;p&gt;For deployments on Kafka 2.4 and later, switching to &lt;code&gt;CooperativeStickyAssignor&lt;/code&gt; (&lt;code&gt;partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor&lt;/code&gt;) means that during a rebalance, only the partitions being migrated are paused. The rest of the group continues consuming. This significantly reduces the impact of rebalances caused by rolling restarts or transient consumer joins.&lt;/p&gt;
&lt;h3 id=&quot;improving-throughput&quot;&gt;Improving throughput&lt;/h3&gt;
&lt;p&gt;Consumer parallelism is bounded by partition count. You cannot have more active consumer instances in a group than there are partitions; additional consumers will sit idle. If the throughput ceiling for a consumer group is a concern, increasing partition count on the topic is the only way to raise the maximum parallelism available to you.&lt;/p&gt;
&lt;p&gt;For bulk throughput, &lt;code&gt;fetch.max.bytes&lt;/code&gt; (default: 50MB) and &lt;code&gt;max.partition.fetch.bytes&lt;/code&gt; (default: 1MB) control how much data the consumer requests per fetch. Increasing &lt;code&gt;max.partition.fetch.bytes&lt;/code&gt; is relevant when topics carry large messages, since the default can limit how many records come back in each fetch response. Be aware that larger fetch sizes increase memory pressure on the consumer, as fetched records are buffered before processing begins.&lt;/p&gt;
&lt;h2 id=&quot;monitor-more-effectively-with-kpow&quot;&gt;Monitor more effectively with Kpow&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides group-level lag visibility across all partitions of a topic, partition-level drill-down for isolating slow consumers, and configurable alerting without building and maintaining a custom Prometheus pipeline. You can connect it to any Kafka cluster and start monitoring consumer groups in minutes.&lt;/p&gt;
&lt;p&gt;Give it a try with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. You can deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka monitoring: a complete guide for platform engineers</title><link>https://factorhouse.io/articles/kafka-monitoring/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-monitoring/</guid><description>A practical guide to Kafka monitoring for platform engineers: the metrics that matter, alert thresholds, JVM tuning, consumer lag, and KRaft changes.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apache Kafka is designed to be fast and fault-tolerant, but those properties only hold when you can see what is happening inside the cluster. Without instrumentation, a broker disk filling up, a follower falling behind, or a consumer group stalling can each go unnoticed until the damage is done.&lt;/p&gt;
&lt;p&gt;This guide covers every layer of the Kafka monitoring surface: brokers, replication, consumers, producers, JVM, storage, and tooling. It is written for platform engineers and data engineers who are responsible for running Kafka in production and who need concrete, actionable guidance rather than a catalogue of metric names.&lt;/p&gt;
&lt;h2 id=&quot;what-is-kafka-monitoring&quot;&gt;What is Kafka monitoring?&lt;/h2&gt;
&lt;p&gt;Kafka monitoring is the practice of collecting, observing, and alerting on the operational signals that describe the health and performance of a running Kafka cluster. The monitoring surface spans three layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JMX metrics&lt;/strong&gt; exposed by the broker and client JVMs through Java Management Extensions, covering everything from replication state to request handler utilisation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OS and infrastructure metrics&lt;/strong&gt; that describe the host resources Kafka depends on: disk I/O, network throughput, file descriptors, and page cache pressure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application-level metrics&lt;/strong&gt; that measure the end-to-end impact of Kafka on the business: consumer lag as a time delay, producer error rates, and pipeline latency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Metrics are exposed via two separate registries. Server-side internals use the Yammer Metrics library; Java-based clients use the Kafka Metrics registry. Both expose their telemetry through JMX, making performance data available to external systems such as Prometheus exporters, Datadog, and Dynatrace.&lt;/p&gt;
&lt;p&gt;By default, remote JMX access is disabled to prevent unauthenticated access to the JVM. To enable it, set the &lt;code&gt;JMX_PORT&lt;/code&gt; environment variable in your broker startup script, or configure the &lt;code&gt;com.sun.management.jmxremote.*&lt;/code&gt; system properties explicitly. You can verify metric exposure with Kafka’s built-in &lt;code&gt;kafka-run-class.sh kafka.tools.JmxTool&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kraft-and-zookeeper&quot;&gt;KRaft and ZooKeeper&lt;/h3&gt;
&lt;p&gt;ZooKeeper was deprecated in Kafka 3.x and removed entirely in Kafka 4.0. Modern clusters run in KRaft mode, where metadata is managed via a Raft-based consensus protocol inside the broker JVM itself. This architectural shift changes what you monitor: ZooKeeper session health, &lt;code&gt;ZkClient/SessionExpiredPerSec&lt;/code&gt;, and the &lt;code&gt;ControlPlaneNetworkProcessorAvgIdlePercent&lt;/code&gt; metric are no longer present. In their place, KRaft introduces consensus metrics under &lt;code&gt;kafka.server:type=raft-metrics&lt;/code&gt; and a new set of metadata loader and controller queue metrics. The relevant KRaft-specific sections are called out throughout this guide.&lt;/p&gt;
&lt;h2 id=&quot;why-kafka-monitoring-matters&quot;&gt;Why Kafka monitoring matters&lt;/h2&gt;
&lt;p&gt;Kafka is designed to be resilient, but specific failure modes are silent until they cause data loss or downstream outages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Replication lag becomes data loss.&lt;/strong&gt; When a follower falls out of the In-Sync Replicas (ISR) set, the cluster’s effective replication factor drops. If a second broker fails before the first recovers, you can lose data permanently. This happens in minutes; without an alert on &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;, you will not know until a producer starts receiving &lt;code&gt;NotEnoughReplicasException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag is an invisible SLA breach.&lt;/strong&gt; A consumer group can be running, committing offsets, and processing records while accumulating a lag of millions of messages behind the producer. The application looks healthy. Downstream dashboards, notifications, and fraud checks are operating on data that is hours old.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker failure cascades.&lt;/strong&gt; A single slow broker can trigger ISR shrinks across every partition it leads, which can then cause leader elections and controller re-elections if the GC pause lasts long enough. What starts as a noisy garbage collection cycle on one node can propagate into a cluster-wide availability event within a minute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The business cost is concrete.&lt;/strong&gt; A post-incident analysis of a production Kubernetes-hosted Kafka failure documented $240,000 in SLA penalties over 62 minutes, driven by a cascading broker crash that pushed consumer lag to 14 hours across 10,000 topics.&lt;/p&gt;
&lt;h2 id=&quot;the-10-most-critical-kafka-metrics&quot;&gt;The 10 most critical Kafka metrics&lt;/h2&gt;
&lt;p&gt;The table below is the minimum monitoring footprint for any production Kafka cluster. Every metric here has caused production incidents when left unmonitored.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Healthy range&lt;/th&gt;
&lt;th&gt;Alert threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderReplicatedPartitions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&lt;/td&gt;
&lt;td&gt;Partitions whose current ISR count is smaller than the replication factor&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Page if &amp;gt; 0 sustained for 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OfflinePartitionsCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=KafkaController,name=OfflinePartitionsCount&lt;/td&gt;
&lt;td&gt;Partitions with no active leader — reads and writes fail immediately&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Page immediately if &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ActiveControllerCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=KafkaController,name=ActiveControllerCount&lt;/td&gt;
&lt;td&gt;Whether this broker is the active controller; sum across cluster must equal exactly 1&lt;/td&gt;
&lt;td&gt;1 on leader, 0 on all others&lt;/td&gt;
&lt;td&gt;Page if cluster sum is not 1 for 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;records-lag-max&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.consumer:type=consumer-fetch-manager-metrics,client-id=(…),name=records-lag-max&lt;/td&gt;
&lt;td&gt;Maximum offset lag across all partitions assigned to this consumer&lt;/td&gt;
&lt;td&gt;Topic-dependent&lt;/td&gt;
&lt;td&gt;Alert on rate of growth, not absolute value — see consumer section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RequestHandlerAvgIdlePercent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent&lt;/td&gt;
&lt;td&gt;Fraction of time I/O request handler threads are idle&lt;/td&gt;
&lt;td&gt;&amp;gt; 0.6 (60%)&lt;/td&gt;
&lt;td&gt;Warning at &amp;lt; 0.3; critical at &amp;lt; 0.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NetworkProcessorAvgIdlePercent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent&lt;/td&gt;
&lt;td&gt;Fraction of time network processor threads are idle&lt;/td&gt;
&lt;td&gt;&amp;gt; 0.3 (30%)&lt;/td&gt;
&lt;td&gt;Warning if sustained &amp;lt; 0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BytesInPerSec / BytesOutPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec / BytesOutPerSec&lt;/td&gt;
&lt;td&gt;Total inbound and outbound byte throughput&lt;/td&gt;
&lt;td&gt;Varies by cluster&lt;/td&gt;
&lt;td&gt;Alert if approaching NIC saturation; track trend not absolute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GC pause duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;java.lang:type=GarbageCollector,name=G1 Young Generation (or ZGC equivalent)&lt;/td&gt;
&lt;td&gt;Stop-the-world pause time in milliseconds&lt;/td&gt;
&lt;td&gt;&amp;lt; 200ms (G1GC), &amp;lt; 5ms (ZGC)&lt;/td&gt;
&lt;td&gt;Warning at &amp;gt; 200ms P99; critical at &amp;gt; 500ms P99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk usage %&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OS metric (or kafka.log:type=Log,name=Size,topic=(…),partition=(…))&lt;/td&gt;
&lt;td&gt;Disk capacity consumed relative to total; Kafka fills disks faster than expected due to replication&lt;/td&gt;
&lt;td&gt;&amp;lt; 70%&lt;/td&gt;
&lt;td&gt;Warning at &amp;gt; 70%; critical at &amp;gt; 85%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsrShrinksPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=IsrShrinksPerSec&lt;/td&gt;
&lt;td&gt;Rate at which replicas are being dropped from the ISR&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Warn on any sustained positive rate; page if growing rapidly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;kafka-monitoring-best-practices&quot;&gt;Kafka monitoring best practices&lt;/h2&gt;
&lt;h3 id=&quot;1-instrument-with-jmx-exporter-and-prometheus-from-day-one&quot;&gt;1. Instrument with JMX exporter and Prometheus from day one&lt;/h3&gt;
&lt;p&gt;Retrofitting observability onto a running Kafka cluster is painful. The Prometheus JMX Exporter runs as a Java agent inside the broker JVM, translating nested MBean attributes into flat, label-enriched Prometheus series. Configuring it requires a static YAML rule file and a JVM flag at startup:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export KAFKA_OPTS=&quot;$KAFKA_OPTS -javaagent:./jmx_prometheus_javaagent.jar=8080:./kafka-metrics.yml&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consumer group lag is not exposed through JMX directly; it requires a separate &lt;a href=&quot;https://github.com/danielqsj/kafka_exporter&quot;&gt;kafka-exporter&lt;/a&gt; that polls the AdminClient API and calculates the delta between log end offsets and committed consumer offsets. Deploy both from the start.&lt;/p&gt;
&lt;h3 id=&quot;2-alert-on-underreplicatedpartitions--0-without-delay&quot;&gt;2. Alert on &lt;code&gt;UnderReplicatedPartitions &amp;gt; 0&lt;/code&gt; without delay&lt;/h3&gt;
&lt;p&gt;A non-zero under-replicated partition count means your replication factor is not being honoured. The cluster is operating with reduced durability. If one more broker fails before the ISR recovers, you can permanently lose data. There is no steady-state condition that produces a non-zero reading in a healthy cluster, so any positive value should trigger an immediate alert and investigation.&lt;/p&gt;
&lt;p&gt;Pay equal attention to the ISR shrink rate (&lt;code&gt;IsrShrinksPerSec&lt;/code&gt;). Many teams watch only for offline partitions, which means silent degradation: the ISR can shrink from 3 to 2 to 1 over hours without triggering an alert, only surfacing as a critical incident when the last replica fails.&lt;/p&gt;
&lt;h3 id=&quot;3-track-consumer-lag-as-a-rate-of-change-not-just-an-absolute-value&quot;&gt;3. Track consumer lag as a rate of change, not just an absolute value&lt;/h3&gt;
&lt;p&gt;A lag of 50,000 messages on a topic that processes 100,000 messages per second represents 500ms of delay. On a topic that processes 10 messages per second, it represents more than 80 minutes. An absolute offset threshold is meaningless without context.&lt;/p&gt;
&lt;p&gt;Implement time-based lag thresholds wherever possible. A practical formula for topics where time-based monitoring is not directly available:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Critical threshold (in messages) = target_SLO_seconds × consumption_rate_per_second × (1 + safety_margin)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a payment processor consuming 100 messages per second with a 120-second SLO and a 20% safety margin: 120 × 100 × 1.2 = 14,400 messages.&lt;/p&gt;
&lt;p&gt;Alert on a growing lag trend before the lag itself becomes critical. LinkedIn’s open-source Burrow evaluates lag over a sliding window of offset commits rather than at an absolute threshold, categorising consumer group health as OK, WARNING, ERROR, or STALL, catching stalled consumers even when their lag is temporarily low.&lt;/p&gt;
&lt;h3 id=&quot;4-set-jvm-heap-and-gc-budgets-before-tuning-anything-else&quot;&gt;4. Set JVM heap and GC budgets before tuning anything else&lt;/h3&gt;
&lt;p&gt;Kafka brokers run on the JVM. A garbage collection pause on a follower broker suspends its &lt;code&gt;ReplicaFetcherThread&lt;/code&gt;. If that pause exceeds &lt;code&gt;replica.lag.time.max.ms&lt;/code&gt; (default 30 seconds), the partition leader evicts the follower from the ISR. The resulting ISR shrink can cascade into write failures for producers using &lt;code&gt;acks=all&lt;/code&gt; if the ISR drops below &lt;code&gt;min.insync.replicas&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For brokers with heap sizes under 16GB, G1GC with &lt;code&gt;-XX:MaxGCPauseMillis=20&lt;/code&gt; and &lt;code&gt;-XX:InitiatingHeapOccupancyPercent=35&lt;/code&gt; is a sensible baseline. Set &lt;code&gt;-XX:G1HeapRegionSize=16M&lt;/code&gt; to prevent large message batches from being classified as humongous objects. For large heaps (32GB+), Generational ZGC (&lt;code&gt;-XX:+UseZGC -XX:+ZGenerational&lt;/code&gt;) delivers sub-millisecond pause times at the cost of roughly 10-20% more CPU.&lt;/p&gt;
&lt;p&gt;The key monitoring signal: a GC pause exceeding 200ms P99 should trigger a warning. A pause exceeding 500ms P99 should trigger a page.&lt;/p&gt;
&lt;h3 id=&quot;5-distinguish-broker-side-lag-from-consumer-side-lag-before-taking-action&quot;&gt;5. Distinguish broker-side lag from consumer-side lag before taking action&lt;/h3&gt;
&lt;p&gt;When consumer lag is growing, the cause is either upstream (the producer is writing faster than the consumer can keep up) or downstream (the consumer is processing too slowly). These have different remediation paths.&lt;/p&gt;
&lt;p&gt;Start by comparing &lt;code&gt;BytesInPerSec&lt;/code&gt; (producer throughput) against consumer polling rate. If throughput has spiked, add partitions and consumer instances. If throughput is flat but lag is growing, look at consumer processing time: check &lt;code&gt;records-consumed-rate&lt;/code&gt; versus &lt;code&gt;records-lag-max&lt;/code&gt;, and look for slow synchronous I/O inside the poll loop, such as database writes, external HTTP calls, or CPU-bound transformations. These are the most common source of consumer lag in practice.&lt;/p&gt;
&lt;p&gt;Also check for JVM GC pauses on the consumer side. A consumer GC pause exceeding &lt;code&gt;session.timeout.ms&lt;/code&gt; (default 45 seconds) causes the broker to evict the consumer and trigger a rebalance, which itself pauses consumption and accelerates lag accumulation.&lt;/p&gt;
&lt;h3 id=&quot;6-use-per-topic-and-per-partition-views-not-just-cluster-level-aggregates&quot;&gt;6. Use per-topic and per-partition views, not just cluster-level aggregates&lt;/h3&gt;
&lt;p&gt;Cluster-level aggregates mask partition-level problems. A single hot partition receiving 80% of traffic, or a single topic with a &lt;code&gt;replication.factor&lt;/code&gt; of 1 in a three-broker cluster, will not show up in aggregate health dashboards.&lt;/p&gt;
&lt;p&gt;Monitor &lt;code&gt;LeaderCount&lt;/code&gt; and &lt;code&gt;PartitionCount&lt;/code&gt; per broker to detect partition imbalance. An uneven leader distribution means some brokers are handling a disproportionate share of read and write operations. Use &lt;code&gt;kafka-leader-election.sh&lt;/code&gt; for preferred leader elections when imbalance is detected.&lt;/p&gt;
&lt;p&gt;For consumer lag, alert at the consumer group + topic + partition level, not just at the group level. Partition hot-spotting, where a single partition receives a disproportionate share of traffic due to key skew, concentrates lag on one consumer thread while others are idle.&lt;/p&gt;
&lt;h3 id=&quot;7-test-your-alerting-pipeline-regularly&quot;&gt;7. Test your alerting pipeline regularly&lt;/h3&gt;
&lt;p&gt;An alert that fires but never reaches the on-call engineer is operationally equivalent to having no alert at all. Run periodic drills where you deliberately trigger alert conditions in a non-production environment and verify end-to-end delivery: metric fires, alert routes, notification delivers, runbook is accessible.&lt;/p&gt;
&lt;p&gt;For KRaft clusters, also verify that your monitoring tooling handles the new metric namespaces. Several tools still default to ZooKeeper-era MBean paths. Metrics like &lt;code&gt;ControlPlaneNetworkProcessorAvgIdlePercent&lt;/code&gt; no longer exist in Kafka 4.0 clusters; dashboards using them will show no data without erroring, creating silent gaps.&lt;/p&gt;
&lt;h2 id=&quot;cluster-and-broker-health-monitoring&quot;&gt;Cluster and broker health monitoring&lt;/h2&gt;
&lt;h3 id=&quot;broker-health-metrics&quot;&gt;Broker health metrics&lt;/h3&gt;
&lt;p&gt;A healthy broker has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt; equal to 1 (on exactly one broker in the cluster)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt; equal to 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; equal to 0&lt;/li&gt;
&lt;li&gt;Request handler and network processor idle percentages above 30%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The request processing model uses two thread pools. Network processor threads handle socket connections and read incoming data frames. Request handler (I/O) threads dequeue those frames and execute the actual disk reads and writes. The two idle percentage metrics reflect the load on each pool.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt; falls below 0.3, network threads are saturated, likely from a connection storm or a sudden spike in short-lived connections. When &lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt; falls below 0.2, the I/O subsystem is the bottleneck, which typically means disk write pressure or GC-induced slowdowns.&lt;/p&gt;
&lt;p&gt;Total request latency breaks down into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RequestQueueTimeMs&lt;/code&gt; — time waiting in queue for a handler thread&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LocalTimeMs&lt;/code&gt; — active processing on the partition leader&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RemoteTimeMs&lt;/code&gt; — waiting for follower acknowledgment (when &lt;code&gt;acks=all&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ResponseSendTimeMs&lt;/code&gt; — serialising and writing the response to the socket&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A spike in &lt;code&gt;RemoteTimeMs&lt;/code&gt; points to follower lag or network issues between brokers. A spike in &lt;code&gt;LocalTimeMs&lt;/code&gt; points to disk pressure on the leader.&lt;/p&gt;
&lt;p&gt;Other key broker metrics:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Alert note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LeaderElectionRateAndTimeMs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs&lt;/td&gt;
&lt;td&gt;Any election is notable; frequent elections indicate instability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UncleanLeaderElectionsPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec&lt;/td&gt;
&lt;td&gt;Any non-zero value means data loss has occurred — page immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RequestQueueSize&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.network:type=RequestChannel,name=RequestQueueSize&lt;/td&gt;
&lt;td&gt;Alert if sustained &amp;gt; 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PurgatorySize (Produce)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=DelayedOperationPurgatory,name=PurgatorySize,delayedOperation=Produce&lt;/td&gt;
&lt;td&gt;Growing size indicates slow followers or disk saturation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;jvm-and-gc-monitoring&quot;&gt;JVM and GC monitoring&lt;/h3&gt;
&lt;p&gt;Kafka brokers are JVM processes. GC pause behaviour is not a background concern: it directly causes ISR shrinks and consumer lag spikes.&lt;/p&gt;
&lt;p&gt;The mechanics are straightforward. When a follower broker experiences a stop-the-world GC pause, its &lt;code&gt;ReplicaFetcherThread&lt;/code&gt; is suspended. If the pause exceeds &lt;code&gt;replica.lag.time.max.ms&lt;/code&gt; (default 30 seconds), the partition leader evaluates the replica as a “slow follower” using:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;val&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt; laggingReplicas&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; candidateReplicas.filter(r &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  (time.milliseconds &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; r.lastCaughtUpTimeMs) &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; maxLagMs)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The leader then evicts the paused follower from the ISR, raising &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; and driving &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; above zero. If &lt;code&gt;min.insync.replicas&lt;/code&gt; is not met after the eviction, producers with &lt;code&gt;acks=all&lt;/code&gt; receive &lt;code&gt;NotEnoughReplicasException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same dynamic affects consumers. A consumer JVM GC pause that exceeds &lt;code&gt;session.timeout.ms&lt;/code&gt; (default 45 seconds) causes the Group Coordinator to evict the consumer and trigger a rebalance. All consumers in the group pause during the rebalance, accumulating lag.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key JVM metrics to monitor:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What to look for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GC pause duration (P99)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Warning at &amp;gt; 200ms; critical at &amp;gt; 500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GC frequency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Warning if major GC is occurring more than once every 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heap utilisation post-GC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Warning at &amp;gt; 70% of max heap; critical at &amp;gt; 85%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;G1GC vs ZGC at a glance:&lt;/strong&gt; G1GC is the sensible default for heaps under 16GB. Its main risk is humongous object allocation: any object over 50% of the G1 region size is allocated directly into the Old Generation, bypassing Young Generation copy collection and causing heap fragmentation. Setting &lt;code&gt;-XX:G1HeapRegionSize=16M&lt;/code&gt; raises this threshold to 8MB, which covers most Kafka message batches. Generational ZGC (available in JDK 21+) delivers sub-millisecond pause times by running all collection phases concurrently with the application, but requires at least 8 vCPUs and 32GB of heap to run stably, and consumes 10-20% more CPU than G1GC. For containerised deployments with cgroup CPU limits below 4 vCPUs, G1GC is the safer choice.&lt;/p&gt;
&lt;h3 id=&quot;kraft-metadata-monitoring&quot;&gt;KRaft metadata monitoring&lt;/h3&gt;
&lt;p&gt;Kafka 4.0 removes ZooKeeper entirely. In KRaft mode, a subset of brokers act as controllers, maintaining the cluster metadata log (&lt;code&gt;__cluster_metadata&lt;/code&gt;) via the Raft consensus protocol.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What ZooKeeper monitoring you can remove:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;zookeeper.connect&lt;/code&gt;, &lt;code&gt;zookeeper.session.timeout.ms&lt;/code&gt;, &lt;code&gt;ZkClient/SessionExpiredPerSec&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ControlPlaneNetworkProcessorAvgIdlePercent&lt;/code&gt; (replaced by the unified &lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;ZooKeeper &lt;code&gt;ruok&lt;/code&gt; port-2181 health checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What to add for KRaft:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;current-state&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=raft-metrics,name=current-state&lt;/td&gt;
&lt;td&gt;Node’s role: leader, follower, candidate, or observer. Sustained “candidate” indicates election instability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;high-watermark&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=raft-metrics,name=high-watermark&lt;/td&gt;
&lt;td&gt;Highest committed offset in the metadata log. Primary reference for consensus progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;log-end-offset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=raft-metrics,name=log-end-offset&lt;/td&gt;
&lt;td&gt;Latest offset written to local disk. Lag vs. high-watermark reveals replication delay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;commit-latency-avg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=raft-metrics,name=commit-latency-avg&lt;/td&gt;
&lt;td&gt;Average time to commit a metadata operation. Spikes indicate disk write bottlenecks on the controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MetadataLoaderIdleRatio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=MetadataLoader,name=MetadataLoaderIdleRatio&lt;/td&gt;
&lt;td&gt;Fraction of time the metadata loader thread is waiting. Near 0.0 means thread saturation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;metadata-apply-error-count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=broker-metadata-metrics,name=metadata-apply-error-count&lt;/td&gt;
&lt;td&gt;Failed metadata operations — must always be 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventQueueSize&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=ControllerEventManager,name=EventQueueSize&lt;/td&gt;
&lt;td&gt;Pending administrative tasks. Alert if &amp;gt; 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TimedOutBrokerHeartbeatCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=KafkaController,name=TimedOutBrokerHeartbeatCount&lt;/td&gt;
&lt;td&gt;Broker heartbeat timeouts. A growing rate indicates network saturation or broker GC issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The broker registration state (&lt;code&gt;kafka.controller:type=KafkaController,name=BrokerRegistrationState,broker=X&lt;/code&gt;) has three states: &lt;strong&gt;Fenced&lt;/strong&gt; (not participating in leadership), &lt;strong&gt;Controlled Shutdown&lt;/strong&gt; (graceful exit), and &lt;strong&gt;Active&lt;/strong&gt; (fully operational). Fenced brokers do not serve client traffic.&lt;/p&gt;
&lt;p&gt;For manual diagnostics, use &lt;code&gt;kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status&lt;/code&gt; to inspect active leader, current epoch, high watermark, and voter node IDs. This replaces &lt;code&gt;zookeeper-shell.sh&lt;/code&gt; for quorum health checks.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;/articles/kafka-cluster-monitoring&quot;&gt;Kafka cluster monitoring&lt;/a&gt; and &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;Kafka broker monitoring&lt;/a&gt; for more.&lt;/p&gt;
&lt;h2 id=&quot;topic-and-partition-health-monitoring&quot;&gt;Topic and partition health monitoring&lt;/h2&gt;
&lt;p&gt;Topic-level metrics reveal problems that broker-level aggregates hide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MessagesInPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec,topic=(…)&lt;/td&gt;
&lt;td&gt;Per-topic message ingestion rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BytesInPerSec / BytesOutPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec,topic=(…)&lt;/td&gt;
&lt;td&gt;Per-topic byte throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderReplicatedPartitions per topic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.cluster:type=Partition,topic=(…),name=UnderReplicated,partition=(…)&lt;/td&gt;
&lt;td&gt;Partition-level under-replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LogEndOffset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.log:type=Log,name=LogEndOffset,topic=(…),partition=(…)&lt;/td&gt;
&lt;td&gt;Latest written offset per partition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Partition imbalance is detected by comparing &lt;code&gt;LeaderCount&lt;/code&gt; across brokers. In a healthy cluster, partition leadership is distributed proportionally. If one broker holds 60% of leaders while others hold 20% each, read and write traffic is concentrated on one node, driving up its request handler utilisation while others sit idle.&lt;/p&gt;
&lt;p&gt;A spike in &lt;code&gt;MessagesInPerSec&lt;/code&gt; that is not accompanied by a proportional increase in &lt;code&gt;BytesInPerSec&lt;/code&gt; can indicate a change in average message size, which can affect whether messages are classified as humongous objects in the G1GC allocator.&lt;/p&gt;
&lt;h2 id=&quot;consumer-lag-monitoring&quot;&gt;Consumer lag monitoring&lt;/h2&gt;
&lt;p&gt;Consumer lag is the most operationally significant Kafka metric for most teams. It is the direct measurement of how far behind downstream processing is from real-time.&lt;/p&gt;
&lt;p&gt;The formula is simple:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Consumer lag = Log End Offset (partition) - Consumer Committed Offset (partition)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The complexity lies in what that number means. A consumer group may be processing 9,000 messages per second while an upstream producer pushes 10,000. The consumer is running. Throughput dashboards show it as healthy. But it is accumulating 1,000 messages of lag every second.&lt;/p&gt;
&lt;h3 id=&quot;consumer-lag-patterns&quot;&gt;Consumer lag patterns&lt;/h3&gt;
&lt;p&gt;Lag does not accumulate uniformly. Different accumulation patterns indicate different root causes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;Typical cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gradual drift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slow linear climb over hours&lt;/td&gt;
&lt;td&gt;Minor throughput mismatch; application logic slower than required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sudden spike&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Near-vertical jump&lt;/td&gt;
&lt;td&gt;Consumer crash, network partition, upstream traffic burst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flat high lag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large stable gap, neither growing nor shrinking&lt;/td&gt;
&lt;td&gt;Consumer at capacity, capped by partition count or resource limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sawtooth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cyclical rise and fall&lt;/td&gt;
&lt;td&gt;Batch processing, JVM GC pauses, database commit throttling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;what-causes-consumer-lag-in-practice&quot;&gt;What causes consumer lag in practice&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Slow processing inside the poll loop.&lt;/strong&gt; Synchronous database writes, blocking HTTP calls, and CPU-bound transformations inside the record processing loop are the most frequent cause. Each blocked thread cannot call &lt;code&gt;poll()&lt;/code&gt;, causing the broker to eventually view the consumer as dead and trigger a rebalance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JVM GC pauses.&lt;/strong&gt; A consumer GC pause exceeding &lt;code&gt;session.timeout.ms&lt;/code&gt; (default 45 seconds) causes the Group Coordinator to evict the consumer and trigger a full group rebalance. All consumers pause during rebalance. The recovered consumer then faces a large backlog, which can exceed &lt;code&gt;max.poll.interval.ms&lt;/code&gt; if processing is slow, triggering another rebalance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Poison pill records.&lt;/strong&gt; A single malformed record that a consumer cannot process without throwing an exception will cause that consumer to crash-restart in an infinite loop against the same offset. Lag accumulates linearly on that partition while the consumer cycles. Every record queued behind the bad offset is blocked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition hot-spotting.&lt;/strong&gt; When a business key is highly concentrated (for example, a major tenant ID), one partition receives a disproportionate share of traffic. One consumer thread is at 100% CPU while the rest are idle. Lag accumulates on the hot partition while aggregate lag metrics look manageable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transactional producer stalls.&lt;/strong&gt; Consumers in &lt;code&gt;read_committed&lt;/code&gt; isolation mode will not receive messages from open, uncommitted transactions. If a transactional producer crashes with an open transaction, downstream consumers stall until the transaction coordinator times out the transaction, which defaults to &lt;code&gt;transaction.timeout.ms = 60000&lt;/code&gt; (60 seconds).&lt;/p&gt;
&lt;h3 id=&quot;consumer-lag-monitoring-approach&quot;&gt;Consumer lag monitoring approach&lt;/h3&gt;
&lt;p&gt;Use time-based thresholds where possible, with different SLOs per topic type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Fraud detection:       Warning at 10s lag  |  Critical at 30s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Payment processor:     Warning at 30s lag  |  Critical at 2m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Analytics ETL:         Warning at 10m lag  |  Critical at 30m&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For offset-based thresholds where time-based metrics are unavailable:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Critical threshold = target_SLO_seconds × consumption_rate_per_second × (1 + safety_margin)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a payment processor consuming 100 messages/second with a 120-second SLO and a 20% safety margin: 120 × 100 × 1.2 = 14,400 messages.&lt;/p&gt;
&lt;p&gt;Alert on the &lt;strong&gt;rate of change&lt;/strong&gt; of lag, not just its current value. A lag that is 5,000 messages and growing at 1,000 messages per minute needs attention before it crosses any absolute threshold.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;Kafka consumer monitoring&lt;/a&gt; for more.&lt;/p&gt;
&lt;h2 id=&quot;producer-monitoring&quot;&gt;Producer monitoring&lt;/h2&gt;
&lt;p&gt;Kafka producers expose client-side metrics under &lt;code&gt;kafka.producer:type=producer-metrics,client-id=(...)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key producer metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;What to watch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;record-send-rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;record-send-rate&lt;/td&gt;
&lt;td&gt;Average records sent per second; a drop indicates backpressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;record-error-rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;record-error-rate&lt;/td&gt;
&lt;td&gt;Any sustained non-zero value requires investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;request-latency-avg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;request-latency-avg&lt;/td&gt;
&lt;td&gt;Warning at &amp;gt; 100ms avg; critical at &amp;gt; 500ms P99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;buffer-available-bytes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;buffer-available-bytes&lt;/td&gt;
&lt;td&gt;Alert when approaching zero — producer is blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;waiting-threads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;waiting-threads&lt;/td&gt;
&lt;td&gt;Number of application threads blocked on buffer exhaustion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;produce-throttle-time-max&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;produce-throttle-time-max&lt;/td&gt;
&lt;td&gt;Non-zero means the broker is rate-limiting this producer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The failure cascade for a saturated producer works as follows: the broker enforces a quota and delays its response to the producer. The producer retains batches longer, &lt;code&gt;buffer-available-bytes&lt;/code&gt; falls. Once the buffer is exhausted, application threads calling &lt;code&gt;send()&lt;/code&gt; block, &lt;code&gt;waiting-threads&lt;/code&gt; spikes, and the application is effectively paused.&lt;/p&gt;
&lt;p&gt;A sudden rise in &lt;code&gt;record-error-rate&lt;/code&gt; can indicate network problems, broker-side disk issues, ACL misconfigurations, or the producer hitting &lt;code&gt;max.message.bytes&lt;/code&gt;. Check the corresponding broker metric &lt;code&gt;BytesRejectedPerSec&lt;/code&gt; (&lt;code&gt;kafka.server:type=BrokerTopicMetrics,name=BytesRejectedPerSec&lt;/code&gt;) to determine if the broker is actively rejecting oversized batches.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;/articles/kafka-producer-monitoring&quot;&gt;Kafka producer monitoring&lt;/a&gt; for more.&lt;/p&gt;
&lt;h2 id=&quot;replication-and-data-durability-monitoring&quot;&gt;Replication and data durability monitoring&lt;/h2&gt;
&lt;p&gt;Replication metrics are the most critical class of Kafka metrics. They describe whether your data is actually protected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The key metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;What a non-zero value means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderReplicatedPartitions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&lt;/td&gt;
&lt;td&gt;ISR is smaller than replication factor — durability is degraded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderMinIsrPartitionCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=UnderMinIsrPartitionCount&lt;/td&gt;
&lt;td&gt;ISR has dropped below min.insync.replicas — acks=all writes are failing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsrShrinksPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=IsrShrinksPerSec&lt;/td&gt;
&lt;td&gt;Replicas are being dropped from ISR; investigate immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsrExpandsPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=ReplicaManager,name=IsrExpandsPerSec&lt;/td&gt;
&lt;td&gt;Replicas are recovering; should follow a shrink event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UncleanLeaderElectionsPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec&lt;/td&gt;
&lt;td&gt;An out-of-sync replica was elected leader — data has been lost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;UncleanLeaderElectionsPerSec &amp;gt; 0&lt;/code&gt; indicates a data loss event. An out-of-sync replica became the partition leader, which means any records that were committed to the previous leader’s log but not yet replicated to this replica are permanently gone. This is not a warning state; it is an incident that requires forensic investigation.&lt;/p&gt;
&lt;p&gt;The relationship between &lt;code&gt;replication.factor&lt;/code&gt;, &lt;code&gt;min.insync.replicas&lt;/code&gt;, and ISR count determines your durability envelope. With a 3-replica topic and &lt;code&gt;min.insync.replicas=2&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISR = 3: full durability, normal operation&lt;/li&gt;
&lt;li&gt;ISR = 2: degraded redundancy, writes succeed, one more failure loses data&lt;/li&gt;
&lt;li&gt;ISR = 1: &lt;code&gt;acks=all&lt;/code&gt; writes fail; the cluster is protecting you from data loss by refusing writes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Monitor &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; as a leading indicator before checking &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;. The shrink rate tells you the cluster is in the process of degrading; the partition count tells you how far it has degraded.&lt;/p&gt;
&lt;h2 id=&quot;performance-and-throughput-monitoring&quot;&gt;Performance and throughput monitoring&lt;/h2&gt;
&lt;p&gt;Throughput metrics reveal whether the cluster is approaching a resource ceiling, and which resource is the binding constraint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Throughput metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BytesInPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BytesOutPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MessagesInPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TotalTimeMs (Produce P99)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TotalTimeMs (FetchConsumer P99)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchConsumer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To identify whether a throughput ceiling is a network, disk, or CPU constraint:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Network saturation:&lt;/strong&gt; &lt;code&gt;BytesInPerSec + BytesOutPerSec&lt;/code&gt; approaching NIC bandwidth, combined with a drop in &lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk saturation:&lt;/strong&gt; &lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt; dropping while &lt;code&gt;LocalTimeMs&lt;/code&gt; increases; elevated &lt;code&gt;LogFlushRateAndTimeMs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU saturation:&lt;/strong&gt; Host CPU above 70% sustained, often correlated with message format conversions — check &lt;code&gt;MessageConversionsTimeMs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that &lt;code&gt;BytesOutPerSec&lt;/code&gt; is typically two to three times &lt;code&gt;BytesInPerSec&lt;/code&gt; in a cluster with a replication factor of 3, because follower fetch traffic is counted as outbound. A sudden rise in &lt;code&gt;BytesOutPerSec&lt;/code&gt; without a corresponding rise in &lt;code&gt;BytesInPerSec&lt;/code&gt; can indicate a catch-up read: a consumer group that fell behind is now reading cold log segments from disk, which also degrades page cache for all other consumers on that broker.&lt;/p&gt;
&lt;h2 id=&quot;storage-and-retention-monitoring&quot;&gt;Storage and retention monitoring&lt;/h2&gt;
&lt;p&gt;Kafka fills disks faster than most teams expect. The calculation is: &lt;code&gt;data_rate × replication_factor × retention_period&lt;/code&gt;. A 1 GB/s ingest rate with replication factor 3 and 7-day retention requires 1.8 TB of raw disk per broker at minimum, before accounting for segment overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storage metrics to track:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk bytes used (OS metric)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alert at &amp;gt; 70% capacity; critical at &amp;gt; 85%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka.log:type=Log,name=Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Log size per topic-partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka.log:type=LogFlushStats,name=LogFlushRateAndTimeMs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Log flush P99 latency — warning at &amp;gt; 500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka.log:type=Log,name=LogEndOffset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track for unexpected jumps or stalls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Log compaction can fall behind write rate in high-throughput compacted topics, causing the log to grow beyond what compaction alone would maintain. The leading indicator is a growing &lt;code&gt;LogEndOffset&lt;/code&gt; on compacted topics without corresponding &lt;code&gt;LogStartOffset&lt;/code&gt; advancement. Compaction threads are configured via &lt;code&gt;log.cleaner.threads&lt;/code&gt;; if the disk is I/O bound, adding cleaner threads will not help — you need faster storage.&lt;/p&gt;
&lt;p&gt;Retention policy misconfiguration is a common source of disk exhaustion. Time-based retention (&lt;code&gt;log.retention.hours&lt;/code&gt;) and size-based retention (&lt;code&gt;log.retention.bytes&lt;/code&gt;) interact: Kafka enforces whichever threshold is hit first. A topic configured for 7-day retention with no size limit on a high-throughput cluster can consume terabytes before the time window triggers.&lt;/p&gt;
&lt;h2 id=&quot;infrastructure-and-os-metrics-monitoring&quot;&gt;Infrastructure and OS metrics monitoring&lt;/h2&gt;
&lt;p&gt;Kafka is sensitive to the underlying host in ways that most applications are not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disk I/O.&lt;/strong&gt; Kafka is a disk-bound workload. It relies on the OS page cache for low-latency reads and sequential writes. Monitor &lt;code&gt;iowait&lt;/code&gt; and disk read/write throughput at the OS level. Elevated &lt;code&gt;iowait&lt;/code&gt; correlates directly with increased &lt;code&gt;LocalTimeMs&lt;/code&gt; in broker request latency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network.&lt;/strong&gt; With a replication factor of 3, every byte written to a broker is retransmitted twice to followers. Monitor both inbound and outbound interface utilisation. On cloud instances, also watch for traffic-shaping events (AWS CloudWatch &lt;code&gt;TrafficShaping&lt;/code&gt; metric): cloud providers will silently throttle network traffic when a burstable instance hits its baseline, causing latency spikes that are difficult to attribute without this metric.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Page cache.&lt;/strong&gt; The OS page cache is Kafka’s primary read acceleration mechanism. Consumer groups that are actively tailing the log read from page cache, not from disk. A consumer group that falls significantly behind forces the broker into cold disk reads, which evict hot data from the page cache and degrade all other consumers on that broker. Monitor OS-level memory pressure: if the page cache is under pressure, &lt;code&gt;iowait&lt;/code&gt; and request latency will climb together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File descriptors.&lt;/strong&gt; Kafka opens one file descriptor per log segment. In a cluster with many topics and partitions, file descriptor exhaustion is a real failure mode. Set &lt;code&gt;nofile&lt;/code&gt; limits to at least 100,000 per process and monitor current FD usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Memory swappiness.&lt;/strong&gt; Set &lt;code&gt;vm.swappiness = 1&lt;/code&gt; to keep JVM heap pages in physical RAM. Swapping JVM pages to disk during garbage collection cycles introduces severe disk I/O latency, compounding pause times.&lt;/p&gt;
&lt;h2 id=&quot;kafka-connect-monitoring&quot;&gt;Kafka Connect monitoring&lt;/h2&gt;
&lt;p&gt;Kafka Connect is a separate JVM process with its own monitoring surface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connector and task state.&lt;/strong&gt; The primary health signal for Connect is the connector and task status. A connector can be in &lt;code&gt;RUNNING&lt;/code&gt;, &lt;code&gt;PAUSED&lt;/code&gt;, or &lt;code&gt;FAILED&lt;/code&gt; state. Tasks have the same states, plus &lt;code&gt;UNASSIGNED&lt;/code&gt;. A &lt;code&gt;FAILED&lt;/code&gt; task that does not recover automatically requires investigation of the Connect worker log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Connect metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;connector-status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=connector-metrics,connector=(…)&lt;/td&gt;
&lt;td&gt;Check for FAILED state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;task-status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=connector-task-metrics,connector=(…),task=(…)&lt;/td&gt;
&lt;td&gt;Track FAILED and UNASSIGNED tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;connector-total-record-errors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=connector-metrics,connector=(…)&lt;/td&gt;
&lt;td&gt;Error count per connector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;source-record-write-rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=source-task-metrics,connector=(…),task=(…)&lt;/td&gt;
&lt;td&gt;Write rate to Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sink-record-send-rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=sink-task-metrics,connector=(…),task=(…)&lt;/td&gt;
&lt;td&gt;Delivery rate to downstream system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;put-batch-max-time-ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.connect:type=sink-task-metrics,connector=(…),task=(…)&lt;/td&gt;
&lt;td&gt;P99 write latency to the sink&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For source connectors, monitor the lag between the source system and the Kafka topic. For database CDC connectors, this is typically the replication slot lag in the source database.&lt;/p&gt;
&lt;p&gt;For sink connectors, monitor the offset commit latency (&lt;code&gt;offset-commit-success-percentage&lt;/code&gt;) and any increase in task restart counts. A task that restarts repeatedly is often encountering a poison-pill record in the topic, a downstream write failure that is not being handled by the dead-letter queue configuration, or a connection timeout to the downstream system.&lt;/p&gt;
&lt;p&gt;Error records delivered to dead-letter queues should also be monitored. A growing DLQ depth indicates that the connector is encountering records it cannot process, which may signal a schema change, a data quality issue, or a configuration mismatch.&lt;/p&gt;
&lt;h2 id=&quot;kafka-streams-monitoring&quot;&gt;Kafka Streams monitoring&lt;/h2&gt;
&lt;p&gt;Kafka Streams applications expose metrics under &lt;code&gt;kafka.streams&lt;/code&gt; MBeans.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thread and task state:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX MBean path&lt;/th&gt;
&lt;th&gt;What to watch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;thread-state&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.streams:type=stream-thread-metrics,thread-id=(…)&lt;/td&gt;
&lt;td&gt;Alert if any thread is in DEAD or PARTITIONS_REVOKED state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;process-rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.streams:type=stream-thread-metrics,thread-id=(…)&lt;/td&gt;
&lt;td&gt;Processing rate; a drop indicates a downstream bottleneck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;commit-latency-avg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;kafka.streams:type=stream-thread-metrics,thread-id=(…)&lt;/td&gt;
&lt;td&gt;Latency to commit processed records; a spike often indicates state store pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;For stateful topologies with RocksDB state stores:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rocksdb-total-block-cache-usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track memory usage; exhaustion causes state store reads to hit disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rocksdb-estimate-num-keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Useful for capacity planning and detecting key accumulation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rocksdb-compaction-pending&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pending compaction indicates write pressure on the state store&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The most common operational issue in Kafka Streams is excessive state store size causing the application to lag behind its input topic. Monitor the processing rate (&lt;code&gt;process-rate&lt;/code&gt;) against the input topic’s &lt;code&gt;MessagesInPerSec&lt;/code&gt;. If the Streams application is consistently slower than its input, the topology needs more parallelism, or the state store operations need to be profiled.&lt;/p&gt;
&lt;h2 id=&quot;application-and-business-level-monitoring&quot;&gt;Application and business-level monitoring&lt;/h2&gt;
&lt;p&gt;Infrastructure metrics tell you that the cluster is running. Business-level metrics tell you whether it is doing what you need it to do.&lt;/p&gt;
&lt;p&gt;For most teams, the most useful business-level signal derived from Kafka metrics is end-to-end latency: the time between a record being written by a producer and that record being processed by a consumer. This is consumer lag expressed as wall-clock time rather than offset distance.&lt;/p&gt;
&lt;p&gt;How to build this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Produce a timestamp in the record metadata (or use the Kafka producer timestamp if reliable)&lt;/li&gt;
&lt;li&gt;At the consumer, calculate &lt;code&gt;now - record.timestamp&lt;/code&gt; for each processed record&lt;/li&gt;
&lt;li&gt;Track this as a histogram; alert on P95 and P99, not just the mean&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Concrete examples of business-level Kafka observability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An order processing pipeline: lag in the order-confirmed topic expressed as seconds of latency between order placement and confirmation email dispatch&lt;/li&gt;
&lt;li&gt;A fraud detection pipeline: lag in the transactions topic as a direct multiplier on fraud window size — a 5-minute lag means 5 additional minutes of unchecked transactions&lt;/li&gt;
&lt;li&gt;A real-time dashboard: lag in the events topic as the staleness of displayed data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building a “Kafka health to business outcome” dashboard that shows consumer lag alongside the downstream metric it affects gives on-call engineers the context to treat lag alerts with appropriate urgency. When it is clear that 2 minutes of lag on the payment-events topic means 2 minutes of delayed fraud detection, the alert becomes easier to prioritise correctly.&lt;/p&gt;
&lt;h2 id=&quot;alerting-and-incident-detection&quot;&gt;Alerting and incident detection&lt;/h2&gt;
&lt;p&gt;The following table covers the minimum required alert set for a production Kafka cluster. Use this as a starting point and adjust thresholds based on your SLOs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OfflinePartitionsCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Partitions are completely unreachable; client reads and writes fail immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ActiveControllerCount (cluster sum)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not equal to 1 for 5 minutes&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Split-brain or total controller loss; metadata operations halted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderReplicatedPartitions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 sustained for 10+ minutes&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Durability degraded; one more failure risks data loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UncleanLeaderElectionsPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Data loss has occurred; investigation required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnderMinIsrPartitionCount&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Producers with acks=all are failing; client-visible write errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsrShrinksPerSec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sustained positive rate&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Leading indicator of under-replication; investigate cause&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RequestHandlerAvgIdlePercent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.3 (30%) sustained&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Request queue building up; latency degradation imminent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RequestHandlerAvgIdlePercent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.2 (20%)&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Severe I/O saturation; client timeouts likely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NetworkProcessorAvgIdlePercent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.3 (30%)&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Network thread saturation; connection drops possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GC pause P99&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 200ms&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Risk of ISR shrink on affected broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GC pause P99&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 500ms&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;ISR shrink likely; acks=all writes at risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JVM heap post-GC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 70% of max heap&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Increasing GC pressure; Full GC risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JVM heap post-GC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 85% of max heap&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Imminent OutOfMemoryError or stop-the-world Full GC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 70%&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Review retention configuration; add capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 85%&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Disk exhaustion will crash the broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumer lag rate of change&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Growing trend for 5+ minutes&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Consumer falling behind; investigate root cause&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumer time-lag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exceeds per-topic SLO threshold&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Downstream SLA breach in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;metadata-apply-error-count (KRaft)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;td&gt;Page&lt;/td&gt;
&lt;td&gt;Metadata corruption on broker; restart required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventQueueSize (KRaft)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 100&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Controller overloaded with administrative tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Alert philosophy notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alert on &lt;code&gt;OfflinePartitions&lt;/code&gt; and &lt;code&gt;UncleanLeaderElections&lt;/code&gt; unconditionally — there is no benign explanation for these conditions in a healthy cluster.&lt;/li&gt;
&lt;li&gt;Alert on &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; only after a sustained window (5-10 minutes) to avoid false positives during normal broker restarts and rolling updates.&lt;/li&gt;
&lt;li&gt;Avoid alerting on consumer lag using a raw offset count. Express it relative to throughput (time-based) or use a trend-based approach like Burrow.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsrShrinksPerSec&lt;/code&gt; is a leading indicator; &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; is a lagging one. Alert on both, at different severities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;capacity-planning-and-forecasting&quot;&gt;Capacity planning and forecasting&lt;/h2&gt;
&lt;p&gt;Capacity planning for Kafka requires tracking how quickly the cluster is consuming its three primary resources: network, disk, and broker count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disk capacity projection.&lt;/strong&gt; The formula for disk required per broker is:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(ingestion_rate × replication_factor × retention_period) / broker_count&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At 500 MB/s ingestion with replication factor 3 and 7-day retention across 6 brokers: 500 MB/s × 3 × 604,800s / 6 = ~150 TB raw storage per broker.&lt;/p&gt;
&lt;p&gt;Track the rate of disk growth via &lt;code&gt;BytesInPerSec&lt;/code&gt; and project forward at current growth rate. Alert when you have less than 30 days of headroom at current trajectory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition count and broker density.&lt;/strong&gt; In KRaft mode, the historical ZooKeeper-era limit of roughly 4,000 partitions per broker and 200,000 partitions per cluster no longer applies. KRaft’s Raft-based metadata management supports millions of partitions. However, each partition still consumes file descriptors, open segment handles, and memory for replication tracking. A practical upper bound of 10,000-20,000 partitions per broker remains sensible for most workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rebalancing cost.&lt;/strong&gt; When adding brokers to expand capacity, Kafka does not automatically redistribute partitions. Use Cruise Control or &lt;code&gt;kafka-reassign-partitions.sh&lt;/code&gt; to generate and execute reassignment plans. Always throttle reassignment throughput using &lt;code&gt;--throttle &amp;lt;bytes_per_second&amp;gt;&lt;/code&gt; to prevent replication traffic from saturating broker network interfaces during business hours.&lt;/p&gt;
&lt;h2 id=&quot;kafka-monitoring-tools&quot;&gt;Kafka monitoring tools&lt;/h2&gt;
&lt;p&gt;The choice of &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;monitoring tool&lt;/a&gt; affects both what you get out of the box and how much engineering is required to reach full coverage.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Ingestion method&lt;/th&gt;
&lt;th&gt;Consumer lag&lt;/th&gt;
&lt;th&gt;Dashboard setup&lt;/th&gt;
&lt;th&gt;Broker footprint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prometheus + Grafana&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pull via JMX Exporter sidecar agent&lt;/td&gt;
&lt;td&gt;Requires separate kafka-exporter deployment&lt;/td&gt;
&lt;td&gt;Manual JSON dashboard import&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datadog&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pull via JMXFetch daemon (350 metric limit)&lt;/td&gt;
&lt;td&gt;Native via Data Streams Monitoring&lt;/td&gt;
&lt;td&gt;Pre-packaged SaaS dashboards&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynatrace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Process-bound OneAgent injection&lt;/td&gt;
&lt;td&gt;Native via OneAgent introspection&lt;/td&gt;
&lt;td&gt;Out-of-the-box Kafka Overview dashboard&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream-based; requires Metrics Reporter JAR on every broker&lt;/td&gt;
&lt;td&gt;Native in Normal mode&lt;/td&gt;
&lt;td&gt;Proprietary; no external integrations&lt;/td&gt;
&lt;td&gt;Very high (8-16 GB heap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native AdminClient API polling — no JMX required&lt;/td&gt;
&lt;td&gt;Native via AdminClient offset polling&lt;/td&gt;
&lt;td&gt;Pre-packaged native UI&lt;/td&gt;
&lt;td&gt;Near-zero (no broker-side agent)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;What requires custom configuration in each tool:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prometheus/Grafana:&lt;/strong&gt; Consumer lag (separate kafka-exporter), custom MBean patterns in the &lt;code&gt;kafka-2_0_0.yml&lt;/code&gt; YAML rule file, Grafana dashboard variable mapping. JMX histogram metrics are converted to static gauges, losing cumulative sum data needed for accurate rate calculations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Datadog:&lt;/strong&gt; JMX-enabled agent image (&lt;code&gt;-jmx&lt;/code&gt; tag), pod annotations for Autodiscovery, custom &lt;code&gt;metrics.yaml&lt;/code&gt; filter to stay within the 350-metric-per-instance limit. AWS MSK clusters require a separate CloudWatch integration — the standard Kafka agent check cannot query MSK directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynatrace:&lt;/strong&gt; Topic name filters to prevent metric cardinality explosion (and the associated Davis Data Unit billing overage), and a custom &lt;code&gt;plugin.json&lt;/code&gt; schema for non-standard MBeans. The Davis AI anomaly detection engine baselines normal behaviour automatically, which is particularly valuable for consumer lag patterns that vary with traffic shape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confluent Control Center:&lt;/strong&gt; Proprietary Metrics Reporter JAR on every broker classpath, rolling cluster restart to activate. Legacy versions (CP 7.x) use client-side monitoring interceptors that create roughly 50 internal topics and significant metadata overhead. CP 8.0 migrates to Prometheus-based pull collection, requiring a migration if upgrading from the interceptor model. Not compatible with non-Confluent distributions (OSS Kafka, MSK, Aiven).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kpow:&lt;/strong&gt; No JMX or broker-side agent. Kpow queries the AdminClient API directly, eliminating JMX serialisation overhead on the broker. Streams topology visualisation requires embedding the &lt;code&gt;kpow-streams-agent&lt;/code&gt; dependency in the application code. Prometheus egress requires enabling &lt;code&gt;PROMETHEUS_EGRESS=true&lt;/code&gt; and configuring basic authentication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For organisations that need to minimise broker resource overhead while maintaining full observability, running Prometheus paired with Kpow is a practical architecture: Kpow’s metadata engine eliminates JMX sidecars on the brokers, and its Prometheus egress endpoint feeds clean telemetry into a centralised Prometheus TSDB. You can &lt;a href=&quot;/products/kpow&quot;&gt;try Kpow free for 30 days&lt;/a&gt; — it connects to any Kafka cluster in minutes and can be deployed via Docker, Helm, or JAR.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Kafka monitoring is not a single dashboard. It is a set of overlapping layers: broker internals, replication state, JVM health, consumer lag, producer behaviour, storage capacity, and OS resources. Each layer has distinct failure modes, and a blind spot in any one of them can become a production incident.&lt;/p&gt;
&lt;p&gt;The minimum viable monitoring set for any production cluster:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alert immediately on &lt;code&gt;OfflinePartitionsCount &amp;gt; 0&lt;/code&gt; and &lt;code&gt;UncleanLeaderElectionsPerSec &amp;gt; 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Alert after a sustained window on &lt;code&gt;UnderReplicatedPartitions &amp;gt; 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Track &lt;code&gt;IsrShrinksPerSec&lt;/code&gt; as a leading indicator of durability degradation&lt;/li&gt;
&lt;li&gt;Use time-based consumer lag thresholds, not raw offset counts&lt;/li&gt;
&lt;li&gt;Monitor JVM GC pause duration as a direct predictor of ISR instability&lt;/li&gt;
&lt;li&gt;Watch &lt;code&gt;RequestHandlerAvgIdlePercent&lt;/code&gt; and &lt;code&gt;NetworkProcessorAvgIdlePercent&lt;/code&gt; for broker saturation&lt;/li&gt;
&lt;li&gt;For KRaft clusters, add quorum health metrics from &lt;code&gt;kafka.server:type=raft-metrics&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The rest — per-topic metrics, Connect and Streams coverage, business-level lag thresholds — is built on top of this foundation.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>A detailed guide to Kafka producer monitoring</title><link>https://factorhouse.io/articles/kafka-producer-monitoring/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-producer-monitoring/</guid><description>A practical guide to Kafka producer metrics, JMX collection, alerting thresholds, and diagnostic scripts for Java-based Kafka producers.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The Kafka Java producer client exposes metrics through JMX under the &lt;code&gt;kafka.producer:type=producer-metrics&lt;/code&gt; MBean, covering throughput, latency, buffering, error rates, and connection state.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;record-error-rate&lt;/code&gt; and &lt;code&gt;bufferpool-wait-time&lt;/code&gt; are the two most important metrics to alert on. The first signals delivery failures; the second signals back-pressure that can stall application threads.&lt;/li&gt;
&lt;li&gt;Producer metrics should be correlated with broker-side metrics. Elevated &lt;code&gt;request-latency-avg&lt;/code&gt; on the client often originates from broker disk saturation, replication lag, or quota throttling rather than from the producer itself.&lt;/li&gt;
&lt;li&gt;Configuration choices — &lt;code&gt;acks&lt;/code&gt;, &lt;code&gt;linger.ms&lt;/code&gt;, &lt;code&gt;batch.size&lt;/code&gt;, &lt;code&gt;max.in.flight.requests.per.connection&lt;/code&gt;, and &lt;code&gt;compression.type&lt;/code&gt; — directly affect observable metrics and should inform how you interpret them.&lt;/li&gt;
&lt;li&gt;This article covers the Apache Kafka Java producer client, targeting Kafka 3.x (with notes on 2.x where relevant). Clients based on librdkafka, used by Python, Go, and C producers, expose a different metric surface and are not covered here.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-producer-monitoring&quot;&gt;What is Kafka producer monitoring?&lt;/h2&gt;
&lt;p&gt;Kafka producer monitoring is the practice of observing the runtime state of a Kafka producer client: how many records it is sending, how quickly, whether any are failing, and whether back-pressure from the broker is affecting the application.&lt;/p&gt;
&lt;p&gt;The Apache Kafka Java producer client uses an asynchronous two-thread architecture. When your application calls &lt;code&gt;producer.send()&lt;/code&gt;, records are serialized and written to the RecordAccumulator, a memory buffer that groups records into batches by partition. A background Sender thread then drains those batches over persistent TCP connections to the appropriate broker leaders.&lt;/p&gt;
&lt;p&gt;This design decouples your application thread from network I/O, but it also means failures and bottlenecks can be indirect. A broker that is slow to acknowledge writes does not immediately cause &lt;code&gt;send()&lt;/code&gt; to fail. Instead, batches accumulate in the buffer until &lt;code&gt;buffer.memory&lt;/code&gt; is exhausted, at which point your application thread blocks. Understanding this cascade is central to interpreting producer metrics correctly.&lt;/p&gt;
&lt;h2 id=&quot;key-producer-metrics-to-monitor&quot;&gt;Key producer metrics to monitor&lt;/h2&gt;
&lt;p&gt;All Java producer metrics are exposed through JMX. Producer-level metrics use the MBean pattern:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kafka.producer:type=producer-metrics,client-id={client-id}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Topic-scoped metrics are available separately under:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kafka.producer:type=producer-topic-metrics,client-id={client-id},topic={topic}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The metric names below apply to Kafka 3.x. Most were introduced in Kafka 2.x and are consistent across both versions.&lt;/p&gt;
&lt;h3 id=&quot;throughput-metrics&quot;&gt;Throughput metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-send-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-send-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Records sent per second to a specific topic&lt;/td&gt;
&lt;td&gt;Baseline for producer health; a drop to zero on a running producer indicates a stall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;byte-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;byte-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytes sent per second to a specific topic&lt;/td&gt;
&lt;td&gt;Measures network load; correlate with broker &lt;code&gt;BytesInPerSec&lt;/code&gt; to confirm delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both of these are topic-scoped metrics available under the &lt;code&gt;producer-topic-metrics&lt;/code&gt; MBean and require a &lt;code&gt;topic&lt;/code&gt; label in your query.&lt;/p&gt;
&lt;h3 id=&quot;latency-metrics&quot;&gt;Latency metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;request-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;request-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average round-trip time in ms for produce requests&lt;/td&gt;
&lt;td&gt;Rising values indicate broker degradation, replication lag, or network issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;request-latency-max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;request-latency-max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum observed produce request latency in ms&lt;/td&gt;
&lt;td&gt;Useful for detecting outlier latency, particularly under &lt;code&gt;acks=all&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-queue-time-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-queue-time-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average time in ms that a batch spends in the RecordAccumulator before being sent&lt;/td&gt;
&lt;td&gt;Indicates whether the Sender thread is keeping up with the application; target below 50ms under normal conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;error-and-retry-metrics&quot;&gt;Error and retry metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-error-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-error-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Failed record sends per second&lt;/td&gt;
&lt;td&gt;Should be 0.0 at all times; any sustained non-zero value means records are being dropped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-retry-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-retry-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Record send retries per second&lt;/td&gt;
&lt;td&gt;Brief spikes are expected during leader elections; sustained elevation suggests persistent instability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;batching-efficiency-metrics&quot;&gt;Batching efficiency metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;batch-size-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;batch-size-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average batch size in bytes at the time of send&lt;/td&gt;
&lt;td&gt;Compare to your &lt;code&gt;batch.size&lt;/code&gt; config; significantly lower values suggest the producer is flushing batches too early&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;records-per-request-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;records-per-request-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average number of records per produce request&lt;/td&gt;
&lt;td&gt;High values indicate efficient batching; low values under heavy load suggest &lt;code&gt;linger.ms&lt;/code&gt; is too short&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compression-rate-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;compression-rate-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average compression ratio across sent batches&lt;/td&gt;
&lt;td&gt;Lower values mean better compression; useful for comparing before and after a &lt;code&gt;compression.type&lt;/code&gt; change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;buffer-and-back-pressure-metrics&quot;&gt;Buffer and back-pressure metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;buffer-available-bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;buffer-available-bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remaining send buffer capacity in bytes&lt;/td&gt;
&lt;td&gt;Should stay close to the configured &lt;code&gt;buffer.memory&lt;/code&gt;; dropping toward zero means the producer is accumulating faster than the Sender thread can drain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bufferpool-wait-time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bufferpool-wait-time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fraction of time the appender thread is waiting for buffer space&lt;/td&gt;
&lt;td&gt;Target: 0.0; any sustained non-zero value indicates back-pressure; values above 0.10 warrant investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;waiting-threads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;waiting-threads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of application threads currently blocked in &lt;code&gt;send()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target: 0; any non-zero value means application threads are stalling, which can cascade into upstream service timeouts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;connection-metrics&quot;&gt;Connection metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;JMX attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connection-count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;connection-count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of active TCP connections to brokers&lt;/td&gt;
&lt;td&gt;Should equal the number of brokers the producer is actively writing to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;io-wait-time-ns-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io-wait-time-ns-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average nanoseconds the Sender thread spent waiting for ready sockets&lt;/td&gt;
&lt;td&gt;A very high value may indicate low throughput; a near-zero value means the thread is continuously busy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connection-creation-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;connection-creation-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New TCP connections established per second&lt;/td&gt;
&lt;td&gt;Elevated rates suggest connections are being dropped and re-established, typically due to network instability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connection-close-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;connection-close-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sockets closed per second&lt;/td&gt;
&lt;td&gt;Elevated alongside &lt;code&gt;connection-creation-rate&lt;/code&gt; indicates connection flapping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;producer-configuration-effects-on-metrics&quot;&gt;Producer configuration effects on metrics&lt;/h2&gt;
&lt;p&gt;The configuration you pass to the producer at startup has a direct and measurable effect on the metrics you observe at runtime. Understanding these relationships helps you interpret metric values correctly and tune configuration to meet your performance objectives.&lt;/p&gt;
&lt;h3 id=&quot;acks-setting&quot;&gt;acks setting&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;acks&lt;/code&gt; parameter controls how many broker acknowledgments the producer waits for before considering a record successfully delivered.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;acks=0&lt;/code&gt;: the producer does not wait for any acknowledgment. &lt;code&gt;request-latency-avg&lt;/code&gt; will be minimal, but &lt;code&gt;record-error-rate&lt;/code&gt; will not reflect broker-side failures — records can be lost silently without the client being aware.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acks=1&lt;/code&gt;: the leader broker acknowledges after writing to its local log. This produces moderate &lt;code&gt;request-latency-avg&lt;/code&gt; values and captures leader-side failures.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acks=all&lt;/code&gt; (or &lt;code&gt;acks=-1&lt;/code&gt;): the leader waits for all in-sync replicas to acknowledge before responding. This produces the highest &lt;code&gt;request-latency-avg&lt;/code&gt; values — typically two to five times higher than &lt;code&gt;acks=1&lt;/code&gt; in a healthy cluster — but provides the strongest durability guarantee.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under &lt;code&gt;acks=all&lt;/code&gt;, any replication lag on follower replicas will appear directly in &lt;code&gt;request-latency-avg&lt;/code&gt;. Correlate elevated latency values with the broker-side &lt;code&gt;PurgatorySize&lt;/code&gt; metric to determine whether the delay originates from the replication layer.&lt;/p&gt;
&lt;h3 id=&quot;idempotence-and-transactions&quot;&gt;Idempotence and transactions&lt;/h3&gt;
&lt;p&gt;Setting &lt;code&gt;enable.idempotence=true&lt;/code&gt; provides exactly-once delivery semantics per partition. The broker assigns each producer a producer ID and tracks sequence numbers per partition, discarding any duplicates that result from retries.&lt;/p&gt;
&lt;p&gt;Idempotent producers tend to show higher baseline &lt;code&gt;record-retry-rate&lt;/code&gt; values than non-idempotent producers, because the client retries aggressively on transient failures. This is expected behavior; the broker deduplicates on its side.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;enable.idempotence=true&lt;/code&gt; is configured, &lt;code&gt;max.in.flight.requests.per.connection&lt;/code&gt; must be 5 or lower. Exceeding this limit prevents the broker from guaranteeing ordering of sequence numbers across concurrent in-flight batches, which can produce &lt;code&gt;OutOfOrderSequenceException&lt;/code&gt; errors that surface as elevated &lt;code&gt;record-error-rate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For transactional producers, two additional metrics are worth monitoring: &lt;code&gt;flush-time-ns-total&lt;/code&gt;, which records cumulative nanoseconds spent blocked inside &lt;code&gt;.flush()&lt;/code&gt; during transactional boundary commits, and &lt;code&gt;txn-send-offsets-time-ns-total&lt;/code&gt;, which tracks time spent publishing offset maps within transactions.&lt;/p&gt;
&lt;h3 id=&quot;lingerms-and-batchsize&quot;&gt;linger.ms and batch.size&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;linger.ms&lt;/code&gt; controls how long the Sender thread waits before dispatching an incomplete batch. &lt;code&gt;batch.size&lt;/code&gt; sets the maximum batch size in bytes.&lt;/p&gt;
&lt;p&gt;A higher &lt;code&gt;linger.ms&lt;/code&gt; value — for example, 50 to 100ms — allows more records to accumulate per batch. You will observe higher &lt;code&gt;batch-size-avg&lt;/code&gt; and &lt;code&gt;records-per-request-avg&lt;/code&gt;, and lower network overhead per record. The trade-off is increased &lt;code&gt;record-queue-time-avg&lt;/code&gt;, since records wait longer in the accumulator before being sent.&lt;/p&gt;
&lt;p&gt;A lower &lt;code&gt;linger.ms&lt;/code&gt; value — 0 to 5ms — minimises &lt;code&gt;record-queue-time-avg&lt;/code&gt;, which is appropriate for latency-sensitive workloads, but typically produces smaller batches and lower throughput per network request.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;batch-size-avg&lt;/code&gt; is consistently close to your configured &lt;code&gt;batch.size&lt;/code&gt;, the producer is filling batches before the linger timer fires. This is a sign of a high-throughput workload where &lt;code&gt;linger.ms&lt;/code&gt; has less influence on batch efficiency.&lt;/p&gt;
&lt;h3 id=&quot;maxinflightrequestsperconnection&quot;&gt;max.in.flight.requests.per.connection&lt;/h3&gt;
&lt;p&gt;This setting controls how many unacknowledged produce requests the Sender thread can pipeline on a single broker connection before blocking.&lt;/p&gt;
&lt;p&gt;Higher values increase throughput by allowing more requests in flight simultaneously, but they introduce ordering risks. If a broker returns an error for request N and the producer retries, request N+1 may arrive before the retry completes, resulting in out-of-order delivery on that partition.&lt;/p&gt;
&lt;p&gt;For idempotent producers, the broker’s sequence number tracking handles this risk — but only up to 5 concurrent in-flight requests per connection. Beyond that, idempotence guarantees do not hold.&lt;/p&gt;
&lt;p&gt;Monitor &lt;code&gt;requests-in-flight&lt;/code&gt; alongside &lt;code&gt;record-retry-rate&lt;/code&gt;. If retries are frequent and &lt;code&gt;requests-in-flight&lt;/code&gt; is consistently at the configured maximum, reducing the in-flight limit may improve delivery stability at some cost to throughput.&lt;/p&gt;
&lt;h3 id=&quot;compressiontype&quot;&gt;compression.type&lt;/h3&gt;
&lt;p&gt;Compression is applied per batch before the batch is dispatched by the Sender thread. The available options are &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt;, &lt;code&gt;snappy&lt;/code&gt;, &lt;code&gt;lz4&lt;/code&gt;, and &lt;code&gt;zstd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;compression-rate-avg&lt;/code&gt; reflects the effectiveness of the chosen algorithm: a value of 0.4 means the compressed batch is 40% of its original size. Lower values indicate better compression, which reduces network bandwidth and broker disk usage.&lt;/p&gt;
&lt;p&gt;The CPU cost varies significantly by algorithm. &lt;code&gt;lz4&lt;/code&gt; and &lt;code&gt;snappy&lt;/code&gt; offer fast compression with moderate ratios, which suits latency-sensitive workloads. &lt;code&gt;zstd&lt;/code&gt; and &lt;code&gt;gzip&lt;/code&gt; achieve better compression ratios but are more CPU-intensive. If you observe higher &lt;code&gt;request-latency-avg&lt;/code&gt; after enabling compression, check producer host CPU utilisation — the Sender thread may be CPU-bound.&lt;/p&gt;
&lt;p&gt;Compression shifts CPU work from the broker to the producer at write time, and to the consumer at read time. Weigh the network and storage savings against the CPU cost on both sides.&lt;/p&gt;
&lt;h3 id=&quot;buffermemory-and-maxblockms&quot;&gt;buffer.memory and max.block.ms&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;buffer.memory&lt;/code&gt; sets the total size of the RecordAccumulator pool in bytes (default: 32 MB). &lt;code&gt;max.block.ms&lt;/code&gt; sets how long an application thread will block in &lt;code&gt;send()&lt;/code&gt; waiting for buffer space before throwing a &lt;code&gt;TimeoutException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When broker throughput drops — due to disk saturation, replication lag, or quota throttling — the Sender thread drains batches more slowly. Batches accumulate in the accumulator, &lt;code&gt;buffer-available-bytes&lt;/code&gt; drops toward zero, and application threads begin to block. &lt;code&gt;bufferpool-wait-time&lt;/code&gt; rises above zero. If a thread remains blocked for longer than &lt;code&gt;max.block.ms&lt;/code&gt;, the call fails with a &lt;code&gt;TimeoutException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;waiting-threads&lt;/code&gt; rising above zero is always a signal that the producer is experiencing back-pressure. Increasing &lt;code&gt;buffer.memory&lt;/code&gt; gives more headroom before application threads block, but does not address the underlying cause of the back-pressure.&lt;/p&gt;
&lt;h2 id=&quot;collecting-producer-metrics&quot;&gt;Collecting producer metrics&lt;/h2&gt;
&lt;h3 id=&quot;jmx-collection-from-the-producer-process&quot;&gt;JMX collection from the producer process&lt;/h3&gt;
&lt;p&gt;The Kafka Java producer client registers JMX MBeans with the platform MBean server of its JVM automatically. No additional configuration is required for local JMX access.&lt;/p&gt;
&lt;p&gt;To enable remote JMX access for external tooling, pass the following JVM flags when starting your producer application:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;-Dcom.sun.management.jmxremote&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;-Dcom.sun.management.jmxremote.port&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;=9999&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;-Dcom.sun.management.jmxremote.authenticate&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;-Dcom.sun.management.jmxremote.ssl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For production environments, replace the &lt;code&gt;authenticate=false&lt;/code&gt; and &lt;code&gt;ssl=false&lt;/code&gt; flags with appropriate authentication and TLS configuration.&lt;/p&gt;
&lt;p&gt;Once the JMX port is open, you can connect using tools like &lt;code&gt;jconsole&lt;/code&gt;, &lt;code&gt;jmxterm&lt;/code&gt;, or any JMX-compatible client to inspect MBeans under &lt;code&gt;kafka.producer:type=producer-metrics,client-id={client-id}&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-jmx-exporter&quot;&gt;Prometheus JMX exporter&lt;/h3&gt;
&lt;p&gt;The most common production approach is to run the &lt;a href=&quot;https://github.com/prometheus/jmx_exporter&quot;&gt;Prometheus JMX Exporter&lt;/a&gt; as a Java agent inside the producer JVM. It polls the local MBean server and exposes the metrics as a Prometheus-compatible HTTP endpoint, without requiring remote JMX access.&lt;/p&gt;
&lt;p&gt;Add the agent to your JVM startup arguments:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; JAVA_OPTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;-javaagent:/usr/share/java/prometheus/jmx_prometheus_javaagent-0.20.0.jar=9404:/etc/prometheus/kafka-producer-jmx-rules.yml&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A rules configuration file that captures both producer-level and topic-level metrics:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;lowercaseOutputName&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;lowercaseOutputLabelNames&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;rules&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Producer-level metrics (throughput, latency, buffer, errors)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;pattern&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;kafka.producer&amp;#x3C;type=producer-metrics, client-id=(.+)&gt;&amp;#x3C;&gt;([^:]+):&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka_producer_$2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;GAUGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      client_id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;$1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Topic-level metrics (per-topic record-send-rate, byte-rate)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;pattern&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;kafka.producer&amp;#x3C;type=producer-topic-metrics, client-id=(.+), topic=(.+)&gt;&amp;#x3C;&gt;([^:]+):&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka_producer_topic_$3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;GAUGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      client_id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;$1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      topic&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;$2&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration exposes metrics like &lt;code&gt;kafka_producer_record_error_rate{client_id=&quot;orders-service&quot;}&lt;/code&gt; and &lt;code&gt;kafka_producer_topic_record_send_rate{client_id=&quot;orders-service&quot;,topic=&quot;orders&quot;}&lt;/code&gt; at &lt;code&gt;http://localhost:9404/metrics&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In Strimzi-managed Kubernetes deployments, you can keep the JMX port (typically 9999) open for tooling while dedicating port 9404 to Prometheus scraping.&lt;/p&gt;
&lt;h3 id=&quot;multi-producer-aggregation&quot;&gt;Multi-producer aggregation&lt;/h3&gt;
&lt;p&gt;When running multiple producer instances — across replicated services or multiple application pods — you need a strategy for identifying individual producers in your metrics pipeline.&lt;/p&gt;
&lt;p&gt;Each producer instance is identified by its &lt;code&gt;client.id&lt;/code&gt; configuration. Set this to something that includes both the service name and the instance identifier, for example &lt;code&gt;orders-service-pod-0&lt;/code&gt;. This value becomes the &lt;code&gt;client_id&lt;/code&gt; label in Prometheus, allowing you to filter and aggregate metrics per producer.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;client.id&lt;/code&gt; is left at its default value of &lt;code&gt;producer-1&lt;/code&gt;, all instances in a fleet will appear identical in your metrics store. When a single instance begins experiencing elevated &lt;code&gt;record-error-rate&lt;/code&gt; or &lt;code&gt;bufferpool-wait-time&lt;/code&gt;, you will have no way to isolate it.&lt;/p&gt;
&lt;p&gt;Tag producer metrics with at least:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;client_id&lt;/code&gt;: the producer instance identifier&lt;/li&gt;
&lt;li&gt;&lt;code&gt;topic&lt;/code&gt;: the target topic (available on topic-scoped metrics)&lt;/li&gt;
&lt;li&gt;Any additional labels your platform uses for environment, region, or service ownership&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;micrometer-and-opentelemetry-integration&quot;&gt;Micrometer and OpenTelemetry integration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Micrometer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are using Spring Kafka or another framework that integrates with Micrometer, the &lt;code&gt;KafkaClientMetrics&lt;/code&gt; binder forwards JMX metrics to any Micrometer-supported registry. Metric names follow the same naming convention as the JMX attributes but are prefixed with &lt;code&gt;kafka.producer.&lt;/code&gt; and use dots rather than underscores — for example, &lt;code&gt;kafka.producer.record-error-rate&lt;/code&gt; instead of &lt;code&gt;kafka_producer_record_error_rate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The OpenTelemetry JMX Metric Gatherer supports Kafka producers natively through its &lt;code&gt;kafka-producer&lt;/code&gt; target system. The gatherer runs as a sidecar or separate process, queries the producer JVM over JMX/RMI, and pushes metrics to an OpenTelemetry Collector:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;receivers&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  jmx/producer&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    jar_path&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/opt/opentelemetry-jmx-metrics.jar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    endpoint&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;localhost:9999&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    target_system&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-producer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    collection_interval&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;10s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;exporters&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  otlp&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    endpoint&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;otel-collector.monitoring.svc.cluster.local:4317&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    tls&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      insecure&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;service&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  pipelines&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    metrics&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      receivers&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;jmx/producer&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      exporters&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;otlp&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The OTel target system exposes metric names prefixed with &lt;code&gt;kafka.producer.&lt;/code&gt; — for example, &lt;code&gt;kafka.producer.record-retry-rate&lt;/code&gt; and &lt;code&gt;kafka.producer.request-latency-avg&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;producer-health-check-script&quot;&gt;Producer health check script&lt;/h2&gt;
&lt;p&gt;The script below provides a point-in-time diagnostic for a running Kafka producer. It queries a Prometheus JMX Exporter endpoint and evaluates the key indicators of producer health against fixed thresholds.&lt;/p&gt;
&lt;p&gt;Companion scripts covering broker-side and consumer-side checks are described in the &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;Kafka broker monitoring article&lt;/a&gt; and the &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;Kafka consumer monitoring article&lt;/a&gt;. The three scripts share the same pattern: query the metrics endpoint, evaluate against thresholds, and exit with a status code compatible with standard monitoring integrations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Prometheus JMX Exporter is running on the producer JVM (default port: 9404)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt; are available in the shell environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Thresholds:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-error-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;&amp;gt; 0.0 per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bufferpool-wait-time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;&amp;gt; 0.10 (10% of appender time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;waiting-threads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-queue-time-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;&amp;gt; 500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;request-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;&amp;gt; 200ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;record-retry-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;&amp;gt; 10 per second&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# kafka-producer-health-check.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Checks the health of a running Kafka producer by querying its&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Prometheus JMX Exporter endpoint.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Usage:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   ./kafka-producer-health-check.sh [client_id] [metrics_port]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Arguments:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   client_id    (optional) Filter output to a specific producer client.id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   metrics_port (optional) JMX Exporter HTTP port. Default: 9404&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Exit codes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   0 = OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   1 = WARNING&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   2 = CRITICAL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#   3 = UNKNOWN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CLIENT_ID&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;${1&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;METRICS_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;${2&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9404}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;METRICS_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;http://localhost:${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;METRICS_PORT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}/metrics&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Thresholds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CRIT_RECORD_ERROR_RATE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;0.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CRIT_BUFFERPOOL_WAIT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;0.10&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CRIT_WAITING_THREADS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;WARN_QUEUE_TIME_MS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;500&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;WARN_REQUEST_LATENCY_MS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;WARN_RETRY_RATE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;10.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; label_filter&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$2&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$label_filter&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    curl&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -sf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$METRICS_URL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;^${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}{&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;label_filter&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;{print $2}&apos;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; head&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    curl&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -sf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$METRICS_URL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;^${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;{print $2}&apos;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; head&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$2&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; threshold&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$3&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; op&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$4&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; severity&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;$5&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-z&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$value&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;%-10s %s (metric not found)\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;UNKNOWN&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$label&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -lt&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; triggered&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  triggered&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;awk&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;BEGIN { print (${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;op&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;threshold&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}) ? 1 : 0 }&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$triggered&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -eq&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;%-10s %s = %s  [threshold %s %s]\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$severity&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$label&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$value&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$op&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$threshold&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$severity&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;CRITICAL&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$severity&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;WARNING&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  ] &amp;#x26;&amp;#x26; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -lt&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;%-10s %s = %s\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;OK&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$label&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$value&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$CLIENT_ID&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;client_id=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CLIENT_ID&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Kafka producer health check&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Endpoint:  ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;METRICS_URL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Client ID: ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;CLIENT_ID&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;---&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;record-error-rate&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_record_error_rate    &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$CRIT_RECORD_ERROR_RATE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;   &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;CRITICAL&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;bufferpool-wait-time&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_bufferpool_wait_time &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$CRIT_BUFFERPOOL_WAIT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;     &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;CRITICAL&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;waiting-threads&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_waiting_threads      &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$CRIT_WAITING_THREADS&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;     &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;CRITICAL&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;record-queue-time-avg (ms)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_record_queue_time_avg &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$WARN_QUEUE_TIME_MS&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;       &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;WARNING&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;request-latency-avg (ms)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_request_latency_avg  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$WARN_REQUEST_LATENCY_MS&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;WARNING&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;record-retry-rate&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_record_retry_rate    &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$WARN_RETRY_RATE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;          &quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;WARNING&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;AVAIL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_buffer_available_bytes&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;TOTAL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;fetch_metric&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka_producer_buffer_total_bytes&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;     &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$LABEL_FILTER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$AVAIL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; [ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$TOTAL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ] &amp;#x26;&amp;#x26; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$TOTAL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;0&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  USED_PCT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;awk&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;BEGIN { printf &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;%.1f&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;, (1 - ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;AVAIL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;} / ${&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;TOTAL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}) * 100 }&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  printf&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;%-10s %s = %s%%  (%s of %s bytes available)\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;INFO&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;buffer-utilisation&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$USED_PCT&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$AVAIL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$TOTAL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;---&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  0&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Result: OK&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;       ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  1&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Result: WARNING&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  2&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Result: CRITICAL&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  3&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Result: UNKNOWN&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;esac&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$EXIT_CODE&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt; This script evaluates one point in time. It does not detect trends, multi-instance comparisons, or intermittent spikes. It also does not query broker-side metrics — a clean result here does not rule out a broker problem contributing to producer behaviour. Use it for incident investigation or pre-deployment checks, not as a substitute for continuous alerting.&lt;/p&gt;
&lt;h2 id=&quot;alerting-strategy-for-kafka-producer-monitoring&quot;&gt;Alerting strategy for Kafka producer monitoring&lt;/h2&gt;
&lt;p&gt;The table below describes a baseline alert set for Kafka producer monitoring. For each alert, the condition reflects sustained behaviour rather than instantaneous spikes, which reduces false positives during normal transient events like leader elections.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alert&lt;/th&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Suggested response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delivery failures&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-error-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 sustained for 2 minutes&lt;/td&gt;
&lt;td&gt;Broker unavailable, serialization error, authorization failure&lt;/td&gt;
&lt;td&gt;Check producer logs for exception type; verify broker health and topic ACLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Producer back-pressure&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bufferpool-wait-time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0.10 for 1 minute&lt;/td&gt;
&lt;td&gt;Broker too slow to accept records; send buffer exhausted&lt;/td&gt;
&lt;td&gt;Correlate with broker &lt;code&gt;TotalTimeMs&lt;/code&gt; and &lt;code&gt;PurgatorySize&lt;/code&gt;; consider temporarily increasing &lt;code&gt;buffer.memory&lt;/code&gt; while diagnosing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application threads blocked&lt;/td&gt;
&lt;td&gt;&lt;code&gt;waiting-threads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 0 for 30 seconds&lt;/td&gt;
&lt;td&gt;Buffer memory depleted under sustained back-pressure&lt;/td&gt;
&lt;td&gt;Same as above; blocked application threads can cascade into upstream service timeouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rising request latency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;request-latency-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt; 200ms for 5 minutes&lt;/td&gt;
&lt;td&gt;Broker disk degradation, replication lag, or quota throttling&lt;/td&gt;
&lt;td&gt;Check broker &lt;code&gt;TotalTimeMs&lt;/code&gt; component breakdown; inspect ISR count and &lt;code&gt;PurgatorySize&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poor batching efficiency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;batch-size-avg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Consistently below 25% of configured &lt;code&gt;batch.size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Producer sending too frequently relative to &lt;code&gt;linger.ms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Increase &lt;code&gt;linger.ms&lt;/code&gt;; verify that the producer is handling meaningful load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Silent producer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record-send-rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;= 0 with producer process running&lt;/td&gt;
&lt;td&gt;Application-level stall, topic ACL block, or misconfiguration&lt;/td&gt;
&lt;td&gt;Check application logs; verify the topic exists and the producer has write permission&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For use with Prometheus Alertmanager, the following rule definitions cover the most critical conditions:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;groups&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-producer-alerts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    rules&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ProducerDeliveryFailures&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;rate(kafka_producer_record_error_total[5m]) &gt; 0.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;2m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;critical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Producer {{ $labels.client_id }} is dropping records&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            The record error rate on client {{ $labels.client_id }} has exceeded 0.1 errors/second&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            for 2 minutes. Records are failing to deliver and may be lost.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ProducerBackPressure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;rate(kafka_producer_bufferpool_wait_time_total[2m]) &gt; 0.15&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;1m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;critical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Producer {{ $labels.client_id }} buffer exhaustion&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            Memory allocation wait rate for client {{ $labels.client_id }} has exceeded 15% over&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            2 minutes. Application threads are blocking on .send() calls.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ProducerQueueDelay&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka_producer_record_queue_time_avg &gt; 500&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;5m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;warning&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Producer {{ $labels.client_id }} accumulator queue delay&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            Average record queue wait time has exceeded 500ms for 5 minutes. This may indicate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            broker degradation, network bottlenecks, or quota throttling.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;alert&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ProducerHighRetryRate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        expr&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;rate(kafka_producer_record_retry_total[5m]) &gt; 10.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;3m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        labels&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          severity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;warning&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        annotations&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          summary&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;High retry rate on producer {{ $labels.client_id }}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            The record retry rate is exceeding 10 retries/second for 3 minutes. This suggests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;            transient network instability or a cluster rebalancing event.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The thresholds above are starting points rather than universal standards. A &lt;code&gt;request-latency-avg&lt;/code&gt; of 200ms may be acceptable for a batch analytics pipeline but not for a payment processing service. Adjust thresholds to match your SLOs and your cluster’s normal operating range.&lt;/p&gt;
&lt;h2 id=&quot;common-kafka-producer-issues-and-how-to-diagnose&quot;&gt;Common Kafka producer issues and how to diagnose&lt;/h2&gt;
&lt;h3 id=&quot;producer-stalling-due-to-full-send-buffer&quot;&gt;Producer stalling due to full send buffer&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Application threads block on &lt;code&gt;send()&lt;/code&gt;, eventually throwing &lt;code&gt;TimeoutException&lt;/code&gt; after &lt;code&gt;max.block.ms&lt;/code&gt; elapses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metric signals:&lt;/strong&gt; &lt;code&gt;bufferpool-wait-time&lt;/code&gt; approaching or above 0.10; &lt;code&gt;buffer-available-bytes&lt;/code&gt; near zero; &lt;code&gt;waiting-threads&lt;/code&gt; above 0; &lt;code&gt;record-queue-time-avg&lt;/code&gt; elevated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Diagnostic steps:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Confirm that &lt;code&gt;buffer-available-bytes&lt;/code&gt; is depleted and &lt;code&gt;bufferpool-wait-time&lt;/code&gt; is non-zero. This pattern confirms back-pressure rather than a serialization issue — serialization failures drop records before they reach the buffer, leaving &lt;code&gt;buffer-available-bytes&lt;/code&gt; near its maximum.&lt;/li&gt;
&lt;li&gt;Check broker &lt;code&gt;TotalTimeMs&lt;/code&gt; and its component latencies. If the local log append component (&lt;code&gt;t_local&lt;/code&gt;) is elevated, the broker disk may be saturated. If the replication wait component (&lt;code&gt;t_remote&lt;/code&gt;) is elevated, there may be ISR lag.&lt;/li&gt;
&lt;li&gt;Check broker &lt;code&gt;RequestQueueSize&lt;/code&gt;. A growing queue indicates that broker network threads are saturated and cannot process incoming requests fast enough.&lt;/li&gt;
&lt;li&gt;If the broker is healthy, review &lt;code&gt;batch-size-avg&lt;/code&gt; relative to &lt;code&gt;batch.size&lt;/code&gt;. An oversized &lt;code&gt;batch.size&lt;/code&gt; can cause the buffer to fill faster than the Sender thread can drain it, particularly at low throughput where batches take longer to fill.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;retry-storm-from-leader-election&quot;&gt;Retry storm from leader election&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; A spike in &lt;code&gt;record-retry-rate&lt;/code&gt; and &lt;code&gt;request-latency-avg&lt;/code&gt;, typically coinciding with a broker restart or failure event.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metric signals:&lt;/strong&gt; &lt;code&gt;record-retry-rate&lt;/code&gt; spikes; &lt;code&gt;request-latency-avg&lt;/code&gt; increases; &lt;code&gt;record-error-rate&lt;/code&gt; may briefly rise before returning to zero.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; During a partition leader election, brokers return &lt;code&gt;NotLeaderForPartitionException&lt;/code&gt; for produce requests targeting the old leader. The Kafka client retries these requests automatically once metadata is refreshed and a new leader is elected.&lt;/p&gt;
&lt;p&gt;Brief retry spikes during failover are expected and do not indicate data loss, as long as &lt;code&gt;record-error-rate&lt;/code&gt; returns to zero after the election completes and &lt;code&gt;record-retry-rate&lt;/code&gt; subsides. If retries persist, check that &lt;code&gt;retries&lt;/code&gt; and &lt;code&gt;retry.backoff.ms&lt;/code&gt; are set appropriately for your cluster’s typical election duration, and verify that the affected topic’s ISR is healthy.&lt;/p&gt;
&lt;h3 id=&quot;idempotent-producer-failure-under-high-in-flight-requests&quot;&gt;Idempotent producer failure under high in-flight requests&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; The broker returns &lt;code&gt;OutOfOrderSequenceException&lt;/code&gt;; the producer surfaces this as a delivery failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metric signals:&lt;/strong&gt; &lt;code&gt;record-error-rate&lt;/code&gt; spikes; &lt;code&gt;record-retry-rate&lt;/code&gt; may be elevated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; When &lt;code&gt;enable.idempotence=true&lt;/code&gt; is set, the broker tracks sequence numbers per partition to detect and discard duplicates. If &lt;code&gt;max.in.flight.requests.per.connection&lt;/code&gt; exceeds 5, the broker cannot reliably order sequence numbers across concurrent in-flight batches, which produces this exception.&lt;/p&gt;
&lt;p&gt;To resolve this, set &lt;code&gt;max.in.flight.requests.per.connection&lt;/code&gt; to 5 or lower when &lt;code&gt;enable.idempotence=true&lt;/code&gt; is configured. From Kafka 3.0 onward, the client enforces this constraint automatically when idempotence is explicitly enabled; earlier versions do not.&lt;/p&gt;
&lt;h3 id=&quot;serialization-errors-surfacing-as-send-failures&quot;&gt;Serialization errors surfacing as send failures&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; &lt;code&gt;record-error-rate&lt;/code&gt; spikes, but broker metrics and network metrics are unchanged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metric signals:&lt;/strong&gt; &lt;code&gt;record-error-rate&lt;/code&gt; elevated; &lt;code&gt;request-latency-avg&lt;/code&gt;, &lt;code&gt;record-queue-time-avg&lt;/code&gt;, and broker-side metrics all remain at normal levels; &lt;code&gt;buffer-available-bytes&lt;/code&gt; stays near its maximum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Serialization failures occur before a record is written to the RecordAccumulator. If the serializer throws an exception — for example, when a value does not conform to an Avro schema — the record is never queued and no network activity occurs. This is the key distinction from network or broker failures, where buffer utilisation also rises.&lt;/p&gt;
&lt;p&gt;Check producer logs for &lt;code&gt;SerializationException&lt;/code&gt; or schema registry errors. If you are using Confluent Schema Registry, a schema compatibility violation will produce this pattern: &lt;code&gt;record-error-rate&lt;/code&gt; rises while all broker and buffer metrics remain stable.&lt;/p&gt;
&lt;h3 id=&quot;compression-cpu-overhead-causing-latency-regression&quot;&gt;Compression CPU overhead causing latency regression&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; &lt;code&gt;request-latency-avg&lt;/code&gt; increases after enabling or changing compression, even though broker health is unchanged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metric signals:&lt;/strong&gt; &lt;code&gt;request-latency-avg&lt;/code&gt; elevated; &lt;code&gt;compression-rate-avg&lt;/code&gt; low (indicating good compression, but at a CPU cost); producer host CPU utilisation elevated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Compression is performed by the Sender thread before each batch is dispatched. On a CPU-constrained host, heavyweight algorithms such as &lt;code&gt;gzip&lt;/code&gt; or &lt;code&gt;zstd&lt;/code&gt; can slow the Sender thread enough to increase observed request latency, even though the broker is responding quickly.&lt;/p&gt;
&lt;p&gt;To diagnose, compare &lt;code&gt;request-latency-avg&lt;/code&gt; before and after enabling compression, and monitor producer host CPU. If compression is causing a latency regression, consider switching to &lt;code&gt;lz4&lt;/code&gt;, which typically offers a better throughput-to-CPU ratio than &lt;code&gt;gzip&lt;/code&gt; or &lt;code&gt;zstd&lt;/code&gt; for latency-sensitive workloads. Weigh the network bandwidth and broker disk savings against the producer CPU cost before deciding.&lt;/p&gt;
&lt;h2 id=&quot;best-practices-for-kafka-producer-monitoring&quot;&gt;Best practices for Kafka producer monitoring&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Monitor &lt;code&gt;record-error-rate&lt;/code&gt; and &lt;code&gt;bufferpool-wait-time&lt;/code&gt; as the minimum baseline for every producer. These two metrics surface the most operationally significant failure conditions — delivery failures and upstream stalling — and should be alerting targets in any environment.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;acks=all&lt;/code&gt; and &lt;code&gt;enable.idempotence=true&lt;/code&gt; for any workload where data loss is unacceptable, and account for the resulting increase in &lt;code&gt;request-latency-avg&lt;/code&gt; when setting alert thresholds. A latency threshold calibrated for &lt;code&gt;acks=1&lt;/code&gt; will produce false positives under &lt;code&gt;acks=all&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;linger.ms&lt;/code&gt; greater than 0 in throughput-optimised workloads, and verify the effect by monitoring &lt;code&gt;batch-size-avg&lt;/code&gt; and &lt;code&gt;records-per-request-avg&lt;/code&gt;. If batch sizes do not increase after raising &lt;code&gt;linger.ms&lt;/code&gt;, the producer may already be limited by &lt;code&gt;batch.size&lt;/code&gt; or by low topic throughput.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;client.id&lt;/code&gt; explicitly on every producer instance. In environments running multiple producer instances, undifferentiated client IDs make it impossible to isolate a misbehaving instance when &lt;code&gt;record-error-rate&lt;/code&gt; or &lt;code&gt;bufferpool-wait-time&lt;/code&gt; rises in your aggregated metrics.&lt;/li&gt;
&lt;li&gt;Align alert thresholds with your service-level objectives rather than generic defaults. The threshold values in this article are reasonable starting points, but what constitutes a normal or degraded baseline varies by workload type and cluster configuration.&lt;/li&gt;
&lt;li&gt;Correlate producer metrics with broker and consumer metrics before concluding that a producer is the source of a problem. Elevated &lt;code&gt;request-latency-avg&lt;/code&gt; is frequently a symptom of broker disk saturation, replication lag, or quota throttling. For a complete picture, refer to the &lt;a href=&quot;/articles/kafka-broker-monitoring&quot;&gt;Kafka broker monitoring article&lt;/a&gt; and the &lt;a href=&quot;/articles/kafka-consumer-monitoring&quot;&gt;Kafka consumer monitoring article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For transactional producers, extend your monitoring to include &lt;code&gt;flush-time-ns-total&lt;/code&gt; and &lt;code&gt;txn-send-offsets-time-ns-total&lt;/code&gt;. Spikes in these values indicate bottlenecks in the transactional commit path that would not appear in the standard metric set but can cause application threads to stall.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;monitor-kafka-producers-with-factor-house&quot;&gt;Monitor Kafka producers with Factor House&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides real-time visibility into your Kafka producers without requiring a separate metrics pipeline. You can inspect producer metrics, configuration, and per-topic throughput directly from the UI, alongside broker and consumer monitoring in the same tool.&lt;/p&gt;
&lt;p&gt;Give Kpow a try for yourself with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. You can connect it to any Kafka cluster in minutes and deploy it via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>How Adidas uses Apache Kafka in production</title><link>https://factorhouse.io/articles/adidas-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/adidas-kafka-architecture/</guid><description>A deep-dive into Adidas&apos;s Kafka architecture — covering observability at 100 billion messages per day, self-service topic provisioning, and custom GoLang tooling.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Adidas runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; at the centre of two distinct platform layers: a business activity monitoring pipeline introduced around 2015, and an internal observability platform called HOLMES that scaled to 100 billion messages per day during the shift to digital commerce in 2020. The engineering decisions behind those platforms — custom GoLang Kafka tooling to avoid JVM overhead, ksqlDB user-defined functions for field-level data masking, and an AsyncAPI-driven GitOps pipeline for self-service topic provisioning — are documented in detail across the company’s engineering blog and Confluent conference talks.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Adidas is a global sportswear manufacturer and retailer operating e-commerce, retail, and supply chain systems across multiple regions. Its platform engineering team maintains the streaming infrastructure that underpins real-time business monitoring, observability, and data integration across those systems.&lt;/p&gt;
&lt;p&gt;Kafka adoption at Adidas began around 2015 with the Business Activity Monitoring 2.0 project. By mid-2018 that platform was ingesting events from 29 source systems across 74 topics at 6 million messages per day. The more significant inflection came in 2020, when Adidas’s shift toward direct digital commerce drove the HOLMES observability platform to 100 billion messages per day — a roughly 16,000-fold increase in two years that required architectural decisions well beyond standard Kafka configurations.&lt;/p&gt;
&lt;p&gt;The most recent publicly documented development, presented at Current London 2025, is a self-service Kafka platform that replaced manual central-team provisioning with a GitOps pipeline backed by a custom domain-specific language and AsyncAPI specifications, cutting provisioning time from days to seconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;~2015:&lt;/strong&gt; Business Activity Monitoring 2.0 initiated; Apache Kafka selected as the streaming backbone&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;April 2018:&lt;/strong&gt; Adidas attends Kafka Summit London alongside Apple, Audi, IBM, BBC, and ING&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;June 2018:&lt;/strong&gt; Iñaki Alzorriz publishes “From Monitoring to Data Streaming” on adidoescode; platform at 74 topics, 29 sources, 6 million messages per day&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020:&lt;/strong&gt; HOLMES observability platform scales to 100 billion messages per day during Adidas’s digital commerce expansion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2021:&lt;/strong&gt; Jose Manuel Cristobal presents “Navigating the Observability Storm with Kafka” at Kafka Summit Europe 2021&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;July 2021:&lt;/strong&gt; Adil Houmadi publishes article on extending ksqlDB with custom UDFs for regional data masking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2023:&lt;/strong&gt; Gabriel Barreras documents Kafka Connect event sourcing pitfalls and solutions on adidoescode&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2025:&lt;/strong&gt; Jose Manuel Cristobal and Guillermo Lagunas present the self-service Kafka platform at Current London 2025&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;adidass-kafka-use-cases&quot;&gt;Adidas’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;business-activity-monitoring&quot;&gt;Business activity monitoring&lt;/h3&gt;
&lt;p&gt;Kafka serves as the event transport layer for Adidas’s Business Activity Monitoring 2.0 platform, carrying events from 29 source systems into a complex event processing engine for real-time analytics and reporting. The platform implements a fan-out pub-sub pattern for data extraction, stateful event processing, and data replication across internal teams. Iñaki Alzorriz described this architecture in a &lt;a href=&quot;https://medium.com/adidoescode/data-streaming-initiative-in-adidas-3f8305d2376e&quot;&gt;June 2018 post on the adidoescode Medium publication&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;observability-and-sre-holmes&quot;&gt;Observability and SRE (HOLMES)&lt;/h3&gt;
&lt;p&gt;HOLMES is Adidas’s internal observability system. Kafka is the streaming backbone that ingests all infrastructure logs and metrics from Kubernetes-based services, enabling problem detection, root cause analysis, and predictive alerting across e-commerce systems. At peak adoption in 2020, the platform processed 100 billion messages per day. Jose Manuel Cristobal covered the full architecture in a &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-europe-2021/navigating-the-obdervability-storm-with-kafka/&quot;&gt;talk at Kafka Summit Europe 2021&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;event-sourcing-via-kafka-connect&quot;&gt;Event sourcing via Kafka Connect&lt;/h3&gt;
&lt;p&gt;Adidas uses Kafka Connect with a JDBC Source Connector to capture inserts and updates from Oracle 19c (deployed on AWS RDS) and transform them into Kafka events for downstream consumers. Gabriel Barreras documented the implementation and its pitfalls in a &lt;a href=&quot;https://medium.com/adidoescode/event-sourcing-with-kafka-connect-inconsistency-pitfalls-solutions-11a771eb697&quot;&gt;February 2023 adidoescode post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;regional-data-streaming-with-ksqldb&quot;&gt;Regional data streaming with ksqlDB&lt;/h3&gt;
&lt;p&gt;ksqlDB handles the splitting of a main Kafka topic into regional sub-topics, filtering data by region and masking sensitive fields using a custom user-defined function. Adil Houmadi described this pattern in a &lt;a href=&quot;https://medium.com/adidoescode/extending-ksqldb-built-in-capability-54b9a84c06b&quot;&gt;July 2021 adidoescode post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;self-service-kafka-platform&quot;&gt;Self-service Kafka platform&lt;/h3&gt;
&lt;p&gt;The current state of the platform, presented at Current London 2025, enables Kafka stakeholders to directly manage topics, permissions, schemas, and connectors without involving the central platform team. A GitOps pipeline backed by a custom DSL and AsyncAPI specifications handles all asset provisioning. Guillermo Lagunas and Jose Manuel Cristobal presented the details in &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;“From Days to Seconds: Adidas’ Journey to Scalable Kafka Self-Service”&lt;/a&gt; at Current London 2025.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;100 billion messages per day&lt;/strong&gt; processed by HOLMES at peak adoption in 2020 (&lt;a href=&quot;https://www.slideshare.net/HostedbyConfluent/navigating-the-obdervability-storm-with-kafka-jose-manuel-cristobal-adidas&quot;&gt;Jose Manuel Cristobal, Kafka Summit Europe 2021&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;6 million messages per day, 74 topics, 29 source systems&lt;/strong&gt; at the BAM 2.0 pilot stage in mid-2018 (&lt;a href=&quot;https://medium.com/adidoescode/data-streaming-initiative-in-adidas-3f8305d2376e&quot;&gt;Iñaki Alzorriz, 2018&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single region, 3 availability zones&lt;/strong&gt; for the HOLMES Kafka deployment, with a multitenant configuration serving 2 tenants at the time of the 2021 talk (&lt;a href=&quot;https://www.slideshare.net/HostedbyConfluent/navigating-the-obdervability-storm-with-kafka-jose-manuel-cristobal-adidas&quot;&gt;Jose Manuel Cristobal, 2021&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;adidass-kafka-architecture&quot;&gt;Adidas’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;holmes-observability-platform&quot;&gt;HOLMES observability platform&lt;/h3&gt;
&lt;p&gt;HOLMES is structured around four layers, deployed on Kubernetes in a single region across 3 availability zones.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;ingestion layer&lt;/strong&gt; collects Prometheus metrics per Kubernetes namespace using a Kubernetes Operator deployed with Helm Charts. A custom GoLang application called Prom2Kafka uses the Prometheus Remote Write protocol with protobuf data models to push those metrics into Kafka. Log collection is handled by Fluentd, which is auto-deployed to Kubernetes namespaces using annotations, requiring no manual configuration per service.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;streaming layer&lt;/strong&gt; is Apache Kafka, deployed open-source with SSL and mutual TLS for all client connections. The cluster is multitenant, with ACLs and connection quotas enforcing isolation between tenants.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;storage layer&lt;/strong&gt; uses two custom GoLang tools: KafkaToPromMetrics consumes from Kafka and writes metrics into Victoria Metrics tenants; Filebeat and Logstash handle log forwarding with suppression capabilities into OpenDistro (an Apache 2.0 build of Elasticsearch). Kafka Streams suppressors filter non-compliant logs and high-rate metrics before they reach storage, controlling ingest volume at 100 billion messages per day.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;consumption layer&lt;/strong&gt; provides Grafana for metrics dashboarding and alerting, and Kibana for log analysis with multi-tenancy support.&lt;/p&gt;
&lt;h3 id=&quot;self-service-kafka-platform-1&quot;&gt;Self-service Kafka platform&lt;/h3&gt;
&lt;p&gt;The current platform is described as vendor-agnostic and non-opinionated, meaning it does not depend on Confluent-specific managed services. Teams manage topics, permissions, schemas, and connectors through a GitOps pipeline using a custom DSL specification. AsyncAPI specifications serve as both the documentation standard and the authoritative source of truth for resource provisioning. The data catalogue is built on top of these AsyncAPI specs, allowing teams to discover available topics, message schemas, and ownership metadata without consulting the platform team.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Metrics are ingested via Prom2Kafka using the Prometheus Remote Write protocol and protobuf serialisation. For application event pipelines, Avro is the documented serialisation format with Schema Registry enforcement. Spring Boot (Java) is used in Kafka consumer and producer services for application-layer event streaming.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;For HOLMES, KafkaToPromMetrics is a GoLang Kafka consumer writing metrics from Kafka into Victoria Metrics. The GoLang client was chosen specifically to avoid the JVM overhead that would be significant at 100 billion messages per day. ksqlDB consumers handle regional topic splitting with a single persistent query.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Kafka Streams is used for stateful stream processing in HOLMES, specifically for suppressing high-rate metrics and filtering non-compliant log events before they reach the storage layer. ksqlDB handles SQL-based stream transformations, including region filtering and field-level hashing via custom UDFs.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Kafka Connect with a JDBC Source Connector pulls inserts and updates from Oracle 19c on AWS RDS. The source connector polls the database on a configurable interval using timestamp-based watermarking.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;custom-golang-kafka-tooling-to-avoid-jvm-overhead&quot;&gt;Custom GoLang Kafka tooling to avoid JVM overhead&lt;/h3&gt;
&lt;p&gt;At 100 billion messages per day, Adidas chose to build two bespoke GoLang applications rather than use standard JVM-based Kafka clients. Prom2Kafka handles ingestion from Prometheus into Kafka via the Remote Write protocol and protobuf, and KafkaToPromMetrics consumes from Kafka and writes into Victoria Metrics. Both were written specifically to avoid the memory footprint and latency characteristics of JVM-based clients at that throughput. Jose Manuel Cristobal documented this decision in the &lt;a href=&quot;https://www.slideshare.net/HostedbyConfluent/navigating-the-obdervability-storm-with-kafka-jose-manuel-cristobal-adidas&quot;&gt;Kafka Summit Europe 2021 slide deck&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ksqldb-user-defined-functions-for-field-level-data-masking&quot;&gt;ksqlDB user-defined functions for field-level data masking&lt;/h3&gt;
&lt;p&gt;Rather than building a separate pipeline stage for PII handling, Adidas extended ksqlDB with a custom Java UDF that applies SHA-256 hashing to sensitive fields. A single persistent query filters events by region and applies the UDF to produce separate regional topics with masked data. Adil Houmadi documented the implementation, including the Java class structure and deployment steps, in the &lt;a href=&quot;https://medium.com/adidoescode/extending-ksqldb-built-in-capability-54b9a84c06b&quot;&gt;July 2021 adidoescode post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;asyncapi-as-infrastructure-config-not-just-documentation&quot;&gt;AsyncAPI as infrastructure config, not just documentation&lt;/h3&gt;
&lt;p&gt;The self-service platform uses AsyncAPI specifications as the source of truth for provisioning Kafka resources including topics, schemas, connectors, and ACLs. This goes beyond the typical documentation use case for AsyncAPI: the specifications are consumed directly by a GitOps pipeline that provisions resources on commit. The result is that teams create and modify Kafka infrastructure through pull requests against AsyncAPI specs rather than through tickets to a central team. Guillermo Lagunas and Jose Manuel Cristobal presented this architecture at &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;Current London 2025&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kafka-streams-suppressors-for-ingest-cost-control&quot;&gt;Kafka Streams suppressors for ingest cost control&lt;/h3&gt;
&lt;p&gt;HOLMES uses Kafka Streams suppressors to filter events before they reach Victoria Metrics and OpenDistro. Non-compliant logs and metrics producing at unusually high rates are suppressed at the streaming layer rather than at the storage layer, which limits the storage write volume at 100 billion messages per day without requiring changes to the producing services.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Self-managed open-source Kafka on Kubernetes, deployed in a single region across 3 availability zones. The HOLMES cluster uses a multitenant configuration with SSL and mTLS for all client connections. Adidas does not document use of a managed Kafka service for these workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security and multi-tenancy:&lt;/strong&gt; All Kafka clusters enforce authentication and encryption via SSL and mutual TLS. Quotas and ACLs provide isolation between tenants within multitenant clusters. The Adidas API Guidelines document TLS requirements and ACL patterns as platform standards for all Kafka topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitOps-driven lifecycle management:&lt;/strong&gt; Topics, permissions, schemas, and connectors are managed declaratively through a GitOps pipeline backed by AsyncAPI specifications. Provisioning changes are introduced as pull requests; the pipeline applies them automatically on merge. This eliminates the need for the central platform team to manually provision resources and makes the state of Kafka infrastructure version-controlled and auditable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SLA tracking and adoption KPIs:&lt;/strong&gt; After moving to the self-service model, Adidas tracks resolution time SLAs and adoption KPIs to measure operational improvement. The published outcome is a reduction from days to seconds for provisioning tasks, which the team uses as an ongoing measure of platform health.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prometheus and Helm Chart deployment:&lt;/strong&gt; The Prometheus collection layer in HOLMES is deployed via Kubernetes Operator with Helm Charts. Fluentd is deployed automatically using Kubernetes namespace annotations, keeping the observability ingest layer fully infrastructure-as-code without requiring per-service configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data catalogue via AsyncAPI:&lt;/strong&gt; Adidas’s Platform and Engineering team maintains a data catalogue built on AsyncAPI specifications so teams can discover available topics, message schemas, field definitions, headers, and ownership metadata. This replaces informal knowledge of what topics exist and who owns them with a structured, searchable registry backed by the same specs that provision the infrastructure.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;central-provisioning-became-a-bottleneck-as-adoption-grew&quot;&gt;Central provisioning became a bottleneck as adoption grew&lt;/h3&gt;
&lt;p&gt;As Kafka adoption scaled across Adidas, the central platform team was responsible for manually creating topics, assigning permissions, registering schemas, and configuring connectors for every team that needed access. The process took days per request and was a source of delays and configuration errors.&lt;/p&gt;
&lt;p&gt;Adidas built a vendor-agnostic self-service platform using a custom DSL and AsyncAPI specifications as the provisioning layer, fed through a GitOps pipeline. Individual teams now manage their own Kafka resources through pull requests. Provisioning time dropped from days to seconds. Guillermo Lagunas and Jose Manuel Cristobal presented the before and after at &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;Current London 2025&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;race-condition-in-kafka-connect-jdbc-source-connector-causing-silent-data-loss&quot;&gt;Race condition in Kafka Connect JDBC Source Connector causing silent data loss&lt;/h3&gt;
&lt;p&gt;When concurrent database transactions overlapped with the JDBC Source Connector’s query window, records whose commit timestamps fell between the connector’s query executions were silently skipped. The connector’s watermark advanced past those records, and they were never re-ingested.&lt;/p&gt;
&lt;p&gt;The immediate fix was tuning &lt;code&gt;timestamp.delay.interval.ms&lt;/code&gt; to introduce a buffer period, ensuring that pending transactions complete before the next poll cycle advances the watermark. Gabriel Barreras documented the root cause and the configuration fix in the &lt;a href=&quot;https://medium.com/adidoescode/event-sourcing-with-kafka-connect-inconsistency-pitfalls-solutions-11a771eb697&quot;&gt;February 2023 adidoescode post&lt;/a&gt;, and also noted that for use cases requiring consistent low-latency change data capture, Debezium or a CDC-native database approach is preferable to JDBC Source Connectors.&lt;/p&gt;
&lt;h3 id=&quot;scaling-observability-ingest-to-100-billion-messages-per-day&quot;&gt;Scaling observability ingest to 100 billion messages per day&lt;/h3&gt;
&lt;p&gt;The shift to digital commerce during 2020 required HOLMES to ingest an order of magnitude more traffic than it was originally designed for, without a proportional increase in infrastructure cost.&lt;/p&gt;
&lt;p&gt;Adidas handled this through a combination of architectural choices: GoLang Kafka clients for the ingestion and storage layers to avoid JVM overhead, Kafka Streams suppressors to reduce the volume of events reaching storage, and Victoria Metrics as a cost-effective time-series backend. The result was a platform that reached 100 billion messages per day without requiring a full re-architecture.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Open-source deployment, self-managed on Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;Stateful processing; used for log and metrics suppression in HOLMES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;ksqlDB&lt;/td&gt;
&lt;td&gt;SQL-based transformations; regional topic splitting and PII masking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client (custom)&lt;/td&gt;
&lt;td&gt;Prom2Kafka (GoLang)&lt;/td&gt;
&lt;td&gt;Prometheus Remote Write ingestion into Kafka using protobuf&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client (custom)&lt;/td&gt;
&lt;td&gt;KafkaToPromMetrics (GoLang)&lt;/td&gt;
&lt;td&gt;Kafka consumer writing metrics from Kafka into Victoria Metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect (JDBC Source)&lt;/td&gt;
&lt;td&gt;Database event sourcing from Oracle 19c (AWS RDS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema management&lt;/td&gt;
&lt;td&gt;Schema Registry&lt;/td&gt;
&lt;td&gt;Schema management and enforcement for Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Message serialisation format used with Schema Registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log collection&lt;/td&gt;
&lt;td&gt;Fluentd&lt;/td&gt;
&lt;td&gt;Auto-deployed in Kubernetes via namespace annotations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics collection&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;Per-namespace metrics collection; deployed via Kubernetes Operator and Helm Charts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics storage&lt;/td&gt;
&lt;td&gt;Victoria Metrics&lt;/td&gt;
&lt;td&gt;Time-series metrics storage backend; described as fast, cost-effective, scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log forwarding&lt;/td&gt;
&lt;td&gt;Filebeat + Logstash&lt;/td&gt;
&lt;td&gt;Lightweight log forwarding with suppression into OpenDistro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log storage&lt;/td&gt;
&lt;td&gt;OpenDistro (Elasticsearch)&lt;/td&gt;
&lt;td&gt;Apache 2.0 Elasticsearch build for log indexing and search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboards and alerting&lt;/td&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;Metrics dashboarding and alerting on top of Victoria Metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log analysis&lt;/td&gt;
&lt;td&gt;Kibana&lt;/td&gt;
&lt;td&gt;Multi-tenant log analysis and visualisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Container orchestration for HOLMES and the data streaming platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure config&lt;/td&gt;
&lt;td&gt;AsyncAPI&lt;/td&gt;
&lt;td&gt;Documentation standard and GitOps provisioning DSL for all Kafka resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source database&lt;/td&gt;
&lt;td&gt;Oracle 19c (AWS RDS)&lt;/td&gt;
&lt;td&gt;Source for Kafka Connect JDBC event sourcing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application framework&lt;/td&gt;
&lt;td&gt;Spring Boot (Java)&lt;/td&gt;
&lt;td&gt;Used in Kafka consumer and producer services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Iñaki Alzorriz&lt;/strong&gt; (Director of Platform Engineering, Adidas): Authored the 2018 adidoescode post describing the Kafka-based data streaming initiative and Business Activity Monitoring 2.0. &lt;a href=&quot;https://medium.com/adidoescode/data-streaming-initiative-in-adidas-3f8305d2376e&quot;&gt;adidoescode, June 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jose Manuel Cristobal&lt;/strong&gt; (Senior Platform Engineer / Director Platform Engineering, Adidas): Presented “Navigating the Observability Storm with Kafka” at Kafka Summit Europe 2021; co-presented “From Days to Seconds” at Current London 2025. &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-europe-2021/navigating-the-obdervability-storm-with-kafka/&quot;&gt;Kafka Summit Europe 2021&lt;/a&gt;; &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;Current London 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guillermo Lagunas&lt;/strong&gt; (Platform Engineering, Adidas): Co-presented the self-service Kafka platform at Current London 2025. &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;Current London 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gabriel Barreras&lt;/strong&gt; (Platform Engineering, Adidas): Documented Kafka Connect JDBC Source Connector race conditions and solutions. &lt;a href=&quot;https://medium.com/adidoescode/event-sourcing-with-kafka-connect-inconsistency-pitfalls-solutions-11a771eb697&quot;&gt;adidoescode, February 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adil Houmadi&lt;/strong&gt; (Platform Engineering, Adidas): Documented ksqlDB UDF extension for regional data masking. &lt;a href=&quot;https://medium.com/adidoescode/extending-ksqldb-built-in-capability-54b9a84c06b&quot;&gt;adidoescode, July 2021&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Choose your Kafka client language based on throughput requirements.&lt;/strong&gt; At 100 billion messages per day, Adidas replaced JVM-based clients with GoLang to reduce memory footprint and latency. If you are running high-throughput observability or telemetry pipelines, the client runtime overhead is worth evaluating early.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ksqlDB user-defined functions let you extend SQL-based pipelines without a separate processing stage.&lt;/strong&gt; Adidas used a custom Java UDF to apply SHA-256 hashing within a persistent ksqlDB query, handling both regional routing and PII masking in a single step. If your stream processing requirements push beyond what built-in ksqlDB functions cover, UDFs are worth considering before adding a separate processing tier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AsyncAPI can serve as infrastructure config, not just documentation.&lt;/strong&gt; Adidas uses AsyncAPI specifications as the source of truth for a GitOps pipeline that provisions topics, schemas, connectors, and ACLs directly. If you are managing Kafka resources manually or via tickets, treating API specifications as executable configuration is an approach that scales better as the number of topics and teams grows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JDBC Source Connectors require careful watermark configuration for correctness.&lt;/strong&gt; The timestamp-based polling model in the Kafka Connect JDBC Source Connector can miss records when concurrent transactions span query boundaries. Setting &lt;code&gt;timestamp.delay.interval.ms&lt;/code&gt; to buffer the watermark advance is a necessary tuning step; for strict correctness requirements, a CDC-native connector such as Debezium is worth evaluating instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suppression at the streaming layer is more cost-effective than suppression at storage.&lt;/strong&gt; Adidas used Kafka Streams suppressors to filter high-rate metrics and non-compliant logs before they reached Victoria Metrics and Elasticsearch. This limits storage write volume without requiring changes to producing services and avoids paying ingestion costs for data you will filter out anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Iñaki Alzorriz, “&lt;a href=&quot;https://medium.com/adidoescode/data-streaming-initiative-in-adidas-3f8305d2376e&quot;&gt;From Monitoring to Data Streaming — Data Streaming Initiative in Adidas&lt;/a&gt;” (June 2018)&lt;/li&gt;
&lt;li&gt;Jose Manuel Cristobal, “&lt;a href=&quot;https://www.confluent.io/events/kafka-summit-europe-2021/navigating-the-obdervability-storm-with-kafka/&quot;&gt;Navigating the Observability Storm with Kafka&lt;/a&gt;” — Kafka Summit Europe 2021&lt;/li&gt;
&lt;li&gt;Jose Manuel Cristobal, “&lt;a href=&quot;https://www.slideshare.net/HostedbyConfluent/navigating-the-obdervability-storm-with-kafka-jose-manuel-cristobal-adidas&quot;&gt;Navigating the Observability Storm with Kafka&lt;/a&gt;” — slide deck&lt;/li&gt;
&lt;li&gt;Guillermo Lagunas and Jose Manuel Cristobal, “&lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/from-days-to-seconds-adidas-journey-to-scalable-kafka-self-service-lnd25&quot;&gt;From Days to Seconds: Adidas’ Journey to Scalable Kafka Self-Service&lt;/a&gt;” — Current London 2025&lt;/li&gt;
&lt;li&gt;Gabriel Barreras, “&lt;a href=&quot;https://medium.com/adidoescode/event-sourcing-with-kafka-connect-inconsistency-pitfalls-solutions-11a771eb697&quot;&gt;Event Sourcing with Kafka Connect: Inconsistency, Pitfalls &amp;amp; Solutions&lt;/a&gt;” (February 2023)&lt;/li&gt;
&lt;li&gt;Adil Houmadi, “&lt;a href=&quot;https://medium.com/adidoescode/extending-ksqldb-built-in-capability-54b9a84c06b&quot;&gt;Extending ksqlDB Built-in Capability&lt;/a&gt;” (July 2021)&lt;/li&gt;
&lt;li&gt;Adidas Platform &amp;amp; Engineering, &lt;a href=&quot;https://adidas.gitbook.io/api-guidelines/asynchronous-api-guidelines/kafka-asynchronous-guidelines/a_introduction/why-asyncapi&quot;&gt;Adidas API Guidelines — Kafka Asynchronous Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are monitoring a Kafka cluster at any scale, you can try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Afterpay uses Apache Kafka in production</title><link>https://factorhouse.io/articles/afterpay-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/afterpay-kafka-architecture/</guid><description>A deep-dive into Afterpay&apos;s Kafka architecture — covering PCI-compliant payment streaming, two-hop AWS PrivateLink isolation, and the Project Teleport cross-region data pipeline.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Afterpay runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; as the event stream for its Global Payments Platform, operating it under PCI DSS constraints that required a custom network architecture and bespoke Kafka client libraries. Payment events leave the PCI zone via a two-hop AWS PrivateLink design, serialised with Avro and signed with AWS KMS-backed HMAC signatures before reaching the shared Kafka cluster. A separate challenge emerged after Block’s 2022 acquisition: Kafka-archived data from Afterpay’s Sydney AWS region was feeding Delta merge operations running in US West, generating more than USD 1,500 per day in S3 cross-region egress. The solution, Project Teleport, restructured the pipeline to keep compute co-located with data at each stage and delivered annual savings of approximately USD 540,000.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Afterpay is a buy-now-pay-later platform founded in Sydney, Australia in 2014 and acquired by Block Inc. in 2022. It operates in Australia, New Zealand, the United States, the United Kingdom, Canada, and Europe, processing instalment payment agreements for millions of consumers at retail and online merchants. Afterpay’s infrastructure runs on AWS, with its primary data engineering operations historically anchored in the Sydney region.&lt;/p&gt;
&lt;p&gt;The acquisition by Block brought an integration challenge: Afterpay’s Kafka-sourced data needed to flow into Block’s consolidated data platform, which operated in US West. Reconciling two distinct AWS regions — with different data ownership, egress cost profiles, and pipeline architectures — is the defining data engineering problem that shaped Afterpay’s most recent Kafka work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-2022:&lt;/strong&gt; Afterpay runs Kafka for PCI-compliant payment event streaming from its Global Payments Platform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2022 (acquisition):&lt;/strong&gt; Block acquires Afterpay. Afterpay’s Kafka data in Sydney (APSE2) needs to integrate with Block’s US West (USW2) data platform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;September 2022:&lt;/strong&gt; Jing Li publishes “Implementing Kafka in the Payments PCI World,” documenting the two-hop PrivateLink architecture, custom Kafka client libraries, and PCI compliance controls deployed across all Afterpay payment environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;November 2024:&lt;/strong&gt; Bulk migration of Afterpay Kafka topics to Project Teleport commences.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;March 2025:&lt;/strong&gt; Project Teleport migration completes. Unni Krishnan publishes the full case study, reporting annual savings of approximately USD 540,000 and zero-downtime migration of approximately 120 Kafka topics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;afterpays-kafka-use-cases&quot;&gt;Afterpay’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;pci-compliant-payment-event-streaming&quot;&gt;PCI-compliant payment event streaming&lt;/h3&gt;
&lt;p&gt;Afterpay’s primary Kafka use case is streaming payment events out of the PCI DSS zone to downstream consumers. Every payment processed by Afterpay’s Global Payments Platform generates events that need to reach risk, analytics, and reporting systems without those systems having direct access to the PCI network. Kafka provides the durable, decoupled transport layer; the custom client libraries and network architecture enforce the compliance boundary.&lt;/p&gt;
&lt;h3 id=&quot;risk-decisioning&quot;&gt;Risk decisioning&lt;/h3&gt;
&lt;p&gt;Kafka-sourced payment event data feeds Afterpay’s Risk Decisioning systems downstream. These are among the approximately 200 datasets delivered from Afterpay’s Kafka pipelines via Project Teleport, providing risk teams with timely event data for fraud and credit decisions.&lt;/p&gt;
&lt;h3 id=&quot;business-intelligence-and-financial-reporting&quot;&gt;Business intelligence and financial reporting&lt;/h3&gt;
&lt;p&gt;Afterpay’s business intelligence and financial reporting teams are major consumers of the Delta Lake pipeline fed by Kafka-archived data. Project Teleport consolidates approximately 200 downstream datasets from the Sydney Kafka archive into the Block data platform in US West, making them available to BI and finance tooling.&lt;/p&gt;
&lt;h3 id=&quot;machine-learning-pipelines&quot;&gt;Machine learning pipelines&lt;/h3&gt;
&lt;p&gt;Databricks Spark Declarative Pipelines read streaming data from Afterpay’s Kafka topics to feed machine learning workloads. The data lake uses a medallion architecture (bronze, silver, gold) on Delta Lake, with Unity Catalog and AWS Glue providing governance and cataloguing.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Daily pipeline volume:&lt;/strong&gt; 9 TB of Kafka-archived data processed per day by Afterpay’s data team (Unni Krishnan, March 2025).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topics migrated:&lt;/strong&gt; Approximately 120 Kafka topics migrated under Project Teleport (Unni Krishnan, March 2025).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downstream datasets:&lt;/strong&gt; Approximately 200 datasets delivered to risk, BI, and financial reporting from Kafka pipelines (Unni Krishnan, March 2025).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payment events published:&lt;/strong&gt; “Millions of messages” published since the PCI Kafka implementation was deployed to all payments environments (Jing Li, September 2022).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compression ratio:&lt;/strong&gt; Approximately 50% reduction in data volume from Avro-to-Parquet conversion in Project Teleport Stage 1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Egress saving:&lt;/strong&gt; Approximately USD 540,000 per year from restructuring the cross-region pipeline (Unni Krishnan, March 2025).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;afterpays-kafka-architecture&quot;&gt;Afterpay’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Afterpay’s Kafka infrastructure runs on AWS, with the primary archival cluster anchored in Sydney (APSE2). The payments Kafka cluster is operated by Block’s Platform Engineering team. Cross-region data processing targets AWS US West (USW2) via Project Teleport.&lt;/p&gt;
&lt;h3 id=&quot;pci-zone-network-design&quot;&gt;PCI zone network design&lt;/h3&gt;
&lt;p&gt;PCI DSS compliance prohibits mixing cardholder data environments with general infrastructure. For Kafka, this means the network path from the PCI zone to the Kafka broker must be isolated from shared transit networks.&lt;/p&gt;
&lt;p&gt;AWS Transit Gateway was evaluated but rejected on two grounds: it lacked support for overlapping VPC CIDR ranges across Block’s network topology, and it would have created a direct routing path between the PCI zone and other Block networks. Afterpay’s solution uses dedicated &lt;strong&gt;AWS PrivateLinks&lt;/strong&gt; with a two-hop architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hop 1: PCI zone connects to Platform Engineering’s network via a dedicated PrivateLink.&lt;/li&gt;
&lt;li&gt;Hop 2: Platform Engineering’s network connects to the Kafka cluster via a second PrivateLink.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Non-PCI teams use AWS Transit Gateway with a single PrivateLink hop. The Payments Platform uses two hops, ensuring the PCI environment has no direct visibility into the shared transit network.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DNS resolution&lt;/strong&gt; for Kafka brokers across the PrivateLink connections uses AWS Route53 private hosted zones. &lt;strong&gt;Schema Registry access&lt;/strong&gt; from the PCI zone is gated through a Squid Proxy that explicitly whitelists Schema Registry DNS names as an egress control.&lt;/p&gt;
&lt;h3 id=&quot;custom-kafka-client-libraries&quot;&gt;Custom Kafka client libraries&lt;/h3&gt;
&lt;p&gt;A standard Kafka client provides no protection against cardholder data leaving the PCI zone in message payloads, no mechanism for asynchronous publication, and no message-level integrity verification. Afterpay’s Payments Platform team built custom producer and consumer libraries to address all three (Jing Li, September 2022):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Card detection and obfuscation:&lt;/strong&gt; The producer library applies RegEx-based cardholder data detection during Avro serialisation. Any field matching a card number pattern is obfuscated before the bytes are published to the Kafka topic. Luhn algorithm validation was planned as a second pass to reduce false positives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Non-blocking publication:&lt;/strong&gt; A dedicated thread pool dispatches Kafka publishing asynchronously. The payment processing thread is released immediately after serialisation; Kafka I/O proceeds independently. A Kafka broker outage or elevated publish latency cannot propagate into payment response times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Message integrity verification:&lt;/strong&gt; Producers sign each message using HMAC with an AWS KMS Customer Managed Key. Consumers within the PCI zone verify the signature with the same KMS key before processing. This provides a tamper-evidence guarantee independent of TLS, which is relevant for audit and compliance scenarios where message-level provenance needs to be demonstrable.&lt;/p&gt;
&lt;h3 id=&quot;kafka-data-archival-confluent-sink-connectors&quot;&gt;Kafka data archival (Confluent Sink Connectors)&lt;/h3&gt;
&lt;p&gt;Afterpay archives Kafka topics to S3 using &lt;strong&gt;Confluent Sink Connectors&lt;/strong&gt;, which land hourly records as Avro files in the Sydney APSE2 region. These archived files are the input to the Project Teleport cross-region pipeline.&lt;/p&gt;
&lt;h3 id=&quot;project-teleport-cross-region-data-processing&quot;&gt;Project Teleport: cross-region data processing&lt;/h3&gt;
&lt;p&gt;Before Project Teleport, Afterpay’s data team ran Delta merge operations with compute in APSE2 writing to Delta tables in USW2. S3 cross-region egress costs exceeded USD 1,500 per day. The core problem was that every merge operation transferred data between regions at the point of processing.&lt;/p&gt;
&lt;p&gt;Project Teleport restructures the pipeline into two stages, each co-located with its data:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 1 (APSE2):&lt;/strong&gt; DeltaSync Spark jobs use Databricks Autoloader for incremental Avro file discovery. Files are converted from Avro to compressed Parquet and loaded into streaming interface Delta tables. Compression reduces the volume transferred to USW2 by approximately 50%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 2 (USW2):&lt;/strong&gt; Delta Live Tables (DLT) jobs apply transformations and run merge operations locally using the &lt;code&gt;apply_changes&lt;/code&gt; API. The &lt;code&gt;apply_changes&lt;/code&gt; API provides built-in deduplication, handling the duplicate events that arise from Kafka’s at-least-once delivery semantics without requiring downstream logic.&lt;/p&gt;
&lt;p&gt;Apache Airflow orchestrates both stages. A checkpoint transfer mechanism ensures historical data is not reprocessed during migration. All approximately 120 topics were migrated between November 2024 and March 2025 with no reported downtime.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Producers in the PCI pipeline use Afterpay’s custom producer library. The card detection, thread pool, and KMS signing layers run within the library; the underlying Kafka client handles batching and delivery. Avro is the serialisation format throughout.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Custom consumer libraries verify the KMS-based HMAC signature on each message before processing. The Schema Registry, accessed via Squid Proxy, provides schema resolution for Avro deserialisation.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;two-hop-privatelink-for-pci-network-isolation&quot;&gt;Two-hop PrivateLink for PCI network isolation&lt;/h3&gt;
&lt;p&gt;The two-hop PrivateLink design is notable for its explicit trade-off: it adds a network hop and DNS configuration overhead in exchange for a provable compliance boundary. The PCI zone has no visibility into the shared transit network; it sees only the Platform Engineering PrivateLink endpoint. This makes the network isolation auditable, which matters for PCI DSS assessments.&lt;/p&gt;
&lt;h3 id=&quot;kms-signed-kafka-messages-for-message-level-integrity&quot;&gt;KMS-signed Kafka messages for message-level integrity&lt;/h3&gt;
&lt;p&gt;Transport-layer security (TLS) protects messages in transit but does not provide message-level provenance. If a message is intercepted and modified between the producer and broker, or replayed by an adversary with network access, TLS does not prevent this. HMAC signing with a KMS CMK addresses both scenarios: a consumer can verify that a message was produced by an entity that held the signing key at the time of production, and that the message has not been modified since. This is a meaningful addition in regulated environments where audit evidence at the message level is valuable.&lt;/p&gt;
&lt;h3 id=&quot;deduplication-via-delta-live-tables-apply_changes&quot;&gt;Deduplication via Delta Live Tables &lt;code&gt;apply_changes&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Rather than writing custom deduplication logic for Kafka’s at-least-once delivery guarantees, Project Teleport delegates this to Delta Live Tables’ &lt;code&gt;apply_changes&lt;/code&gt; API. The API applies upsert semantics as records land in the target table, treating retried events as updates rather than inserts. This keeps the pipeline logic simple and the deduplication behaviour well-defined.&lt;/p&gt;
&lt;h3 id=&quot;co-locating-compute-with-data-to-eliminate-cross-region-egress&quot;&gt;Co-locating compute with data to eliminate cross-region egress&lt;/h3&gt;
&lt;p&gt;The USD 1,500/day egress problem arose because compute was separated from the data it was processing. Project Teleport’s solution is straightforward in principle: run each stage’s compute in the same region as its input data. The Avro-to-Parquet conversion in Stage 1 compounds the saving by reducing the volume transferred between regions before Stage 2 begins.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Afterpay’s Kafka cluster runs on AWS, operated by Block’s Platform Engineering team. Confluent Sink Connectors handle the archival layer from Kafka to S3. Block does not publicly describe using a fully managed Kafka service such as Amazon MSK or Confluent Cloud for the primary cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema governance:&lt;/strong&gt; Avro is used throughout the PCI and archival pipelines. Schema Registry access from the PCI zone is controlled via a Squid Proxy, which enforces explicit DNS-level egress whitelisting. Schema updates must be compatible with existing consumers before being deployed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orchestration:&lt;/strong&gt; Apache Airflow manages cross-stage orchestration in Project Teleport, coordinating the DeltaSync jobs in APSE2 and the DLT jobs in USW2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration strategy:&lt;/strong&gt; Project Teleport’s checkpoint transfer mechanism allows Kafka topic migration without reprocessing historical data. Bulk migration of approximately 120 topics ran over four months (November 2024 to March 2025) with zero reported downtime. Transient streaming interface Delta tables in Stage 1 use sliding window logic to avoid race conditions during migration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data lake governance:&lt;/strong&gt; The Delta Lake medallion architecture (bronze, silver, gold) on S3 provides tiered data quality guarantees. Unity Catalog and AWS Glue handle metadata and governance across the platform.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;pci-network-isolation-for-kafka-connectivity&quot;&gt;PCI network isolation for Kafka connectivity&lt;/h3&gt;
&lt;p&gt;Standard Kafka client connectivity over shared transit infrastructure is not viable under PCI DSS without additional isolation controls. AWS Transit Gateway was evaluated and rejected because it lacked support for overlapping VPC CIDR ranges across Block’s network topology, and because it would have exposed the PCI zone to other Block networks. Afterpay designed a two-hop PrivateLink architecture: PCI zone to Platform Engineering, then Platform Engineering to the Kafka cluster. DNS is resolved via Route53 private hosted zones; Schema Registry egress is explicitly controlled via Squid Proxy. Since rollout, millions of payment events have been published through this architecture.&lt;/p&gt;
&lt;h3 id=&quot;cardholder-data-leaking-into-kafka-message-payloads&quot;&gt;Cardholder data leaking into Kafka message payloads&lt;/h3&gt;
&lt;p&gt;A standard Kafka producer serialises all fields without inspecting their content. In a payments context, this creates a risk that card numbers embedded in event payloads would be published to Kafka topics accessible outside the PCI zone. Afterpay’s custom producer library applies RegEx-based card number detection during Avro serialisation and obfuscates matching fields before publishing. The check runs inline in the serialisation path, with no external calls or added dependencies. Luhn algorithm validation was planned as a follow-up to reduce false positives.&lt;/p&gt;
&lt;h3 id=&quot;kafka-io-blocking-payment-processing&quot;&gt;Kafka I/O blocking payment processing&lt;/h3&gt;
&lt;p&gt;Synchronous Kafka publication on the main payment processing thread means that any Kafka broker latency — whether from a broker restart, network fluctuation, or backpressure — propagates into payment response times. Afterpay’s custom producer library introduces a dedicated thread pool that takes Kafka publishing off the main thread as soon as serialisation completes. The payment flow’s latency is entirely independent of Kafka’s availability. This is a straightforward pattern, but the explicit documentation of it as a PCI-adjacent concern is useful for other teams operating in regulated environments.&lt;/p&gt;
&lt;h3 id=&quot;usd-1500day-in-cross-region-s3-egress-from-delta-merge-operations&quot;&gt;USD 1,500/day in cross-region S3 egress from Delta merge operations&lt;/h3&gt;
&lt;p&gt;After Block acquired Afterpay, data processing consolidated in US West. Afterpay’s Kafka archive was in Sydney (APSE2), and the Delta merge jobs ran compute in APSE2 against Delta tables in USW2. Every merge operation transferred data cross-region, generating more than USD 1,500/day in S3 egress costs. Project Teleport split the pipeline at the regional boundary: Stage 1 runs in APSE2 and converts Avro to Parquet locally, reducing transfer volume by ~50%; Stage 2 runs merge operations locally in USW2. The annual saving is approximately USD 540,000.&lt;/p&gt;
&lt;h3 id=&quot;duplicate-events-from-kafka-at-least-once-delivery&quot;&gt;Duplicate events from Kafka at-least-once delivery&lt;/h3&gt;
&lt;p&gt;Kafka’s at-least-once delivery semantics mean that events may be published more than once, particularly during producer retries or consumer rebalances. Without explicit handling, these duplicates propagate into downstream Delta Lake tables as duplicate rows. Project Teleport uses Delta Live Tables’ &lt;code&gt;apply_changes&lt;/code&gt; API to apply upsert semantics as records land in USW2, treating retried events as updates. The deduplication logic is built into the DLT framework rather than maintained as custom code.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;AWS-hosted; operated by Block’s Platform Engineering team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation format&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Used throughout PCI and archival pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Accessed via Squid Proxy from PCI zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Confluent Sink Connectors&lt;/td&gt;
&lt;td&gt;Archive Kafka topics to S3 as hourly Avro files (APSE2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom client libraries&lt;/td&gt;
&lt;td&gt;Proprietary (Afterpay Payments Platform)&lt;/td&gt;
&lt;td&gt;Card obfuscation, async thread pool publishing, KMS HMAC signing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key management&lt;/td&gt;
&lt;td&gt;AWS KMS (Customer Managed Keys)&lt;/td&gt;
&lt;td&gt;HMAC signing for message integrity verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Networking&lt;/td&gt;
&lt;td&gt;AWS PrivateLink (two-hop), AWS Route53 private hosted zones, Squid Proxy&lt;/td&gt;
&lt;td&gt;PCI zone Kafka connectivity and Schema Registry egress control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Databricks Spark Declarative Pipelines, Delta Live Tables&lt;/td&gt;
&lt;td&gt;Kafka-to-Delta Lake ingestion; Project Teleport pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File discovery&lt;/td&gt;
&lt;td&gt;Databricks Autoloader&lt;/td&gt;
&lt;td&gt;Incremental Avro file ingestion in Project Teleport Stage 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;Apache Airflow&lt;/td&gt;
&lt;td&gt;Cross-stage pipeline orchestration in Project Teleport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake storage&lt;/td&gt;
&lt;td&gt;Delta Lake on S3&lt;/td&gt;
&lt;td&gt;Medallion architecture; Avro-to-Parquet via Project Teleport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute platform&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;DeltaSync jobs and DLT workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata / catalogue&lt;/td&gt;
&lt;td&gt;Unity Catalog, AWS Glue&lt;/td&gt;
&lt;td&gt;Data lake governance post-acquisition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics sink&lt;/td&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Platform-agnostic BI access on Delta Lake&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jing Li&lt;/strong&gt; — authored “Implementing Kafka in the Payments PCI World” (September 2022), detailing the PCI Kafka architecture including the two-hop PrivateLink design and custom client libraries. &lt;a href=&quot;https://code.cash.app/implementing-kafka-in-the-payments-pci-dss-world&quot;&gt;Implementing Kafka in the Payments PCI World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unni Krishnan&lt;/strong&gt; — authored “Project Teleport: Cost-Effective and Scalable Kafka Data Processing at Block” (March 2025), detailing the cross-region Kafka data processing pipeline and its cost outcomes. &lt;a href=&quot;https://code.cash.app/project-teleport&quot;&gt;Project Teleport&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unnee Udayakumar&lt;/strong&gt; — Senior Manager of Data Engineering, Cash App ML and Data Science organisation; primary contact named in the Databricks case study on Delta Lake and Kafka streaming integration. &lt;a href=&quot;https://www.databricks.com/customers/cashapp/delta-lake&quot;&gt;Databricks case study&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PCI DSS compliance does not require a separate Kafka cluster inside the PCI zone.&lt;/strong&gt; Afterpay uses standard Kafka infrastructure outside the PCI zone and enforces the boundary at the network layer (two-hop PrivateLink) and the client layer (card detection in the serialiser, KMS signing, async publication). The compliance work is in the access controls and client libraries, not in the broker topology.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message-level signing with KMS is a viable pattern for tamper-evidence in regulated pipelines.&lt;/strong&gt; HMAC verification at the consumer provides provenance guarantees that TLS alone does not. The operational requirement is shared KMS key access between producers and authorised consumers, which fits naturally into IAM-based access control on AWS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keeping compute co-located with data at each pipeline stage eliminates cross-region egress costs.&lt;/strong&gt; The pattern in Project Teleport is reusable: if you are running a multi-stage pipeline across regions, identify the point where data crosses the regional boundary and ensure compression or format conversion happens before that crossing, not after.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delta Live Tables’ &lt;code&gt;apply_changes&lt;/code&gt; API handles Kafka at-least-once deduplication without custom logic.&lt;/strong&gt; If you are landing Kafka events into Delta Lake and your producer may retry, &lt;code&gt;apply_changes&lt;/code&gt; provides the deduplication semantics you need at the framework level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-region pipeline migration is achievable without downtime using checkpoint transfer.&lt;/strong&gt; Afterpay migrated approximately 120 Kafka topics across a four-month window with no reported downtime by using checkpoint transfer to avoid historical data reprocessing. If you are rearchitecting a Kafka-to-lakehouse pipeline, the migration approach matters as much as the target architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Jing Li, “&lt;a href=&quot;https://code.cash.app/implementing-kafka-in-the-payments-pci-dss-world&quot;&gt;Implementing Kafka in the Payments PCI World&lt;/a&gt;” (September 2022)&lt;/li&gt;
&lt;li&gt;Unni Krishnan, “&lt;a href=&quot;https://code.cash.app/project-teleport&quot;&gt;Project Teleport: Cost-Effective and Scalable Kafka Data Processing at Block&lt;/a&gt;” (March 2025)&lt;/li&gt;
&lt;li&gt;Unnee Udayakumar (attributed), “&lt;a href=&quot;https://www.databricks.com/customers/cashapp/delta-lake&quot;&gt;Consistently providing accurate financial insights&lt;/a&gt;” (Databricks case study)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are monitoring a Kafka cluster at any scale, you can try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>Apache Kafka 4.3.0: A guide for platform engineers</title><link>https://factorhouse.io/articles/apache-kafka-4-3-0/</link><guid isPermaLink="true">https://factorhouse.io/articles/apache-kafka-4-3-0/</guid><description>Kafka 4.3.0 covers broker cordoning, partition size metrics, share group tuning, and tiered storage fixes. Here&apos;s what platform engineers need to act on.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kafka 4.3.0 was released on 22 May 2026 with 25 KIPs and 600+ commits since 4.2.0. If you manage &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; in production, the release notes cover what changed in full. This article focuses on which of those changes are likely to affect your upgrade window, your monitoring setup, and the operational issues that have been quietly causing friction.&lt;/p&gt;
&lt;p&gt;This is primarily an operator’s release. The headline features are broker cordoning, partition size metrics, share group tuning, classic protocol deprecation, and a set of tiered storage improvements. These changes address the operational experience of running Kafka at scale rather than developer-facing functionality — the kind of work that accumulates in Jira tickets like “decommission broker without manual coordination” and “surface retention headroom per partition in monitoring.”&lt;/p&gt;
&lt;p&gt;This article walks through the operationally significant changes, calls out the deprecations that deserve a calendar entry, and closes with a checklist for teams preparing an upgrade.&lt;/p&gt;
&lt;h2 id=&quot;broker-and-log-directory-cordoning--kip-1066&quot;&gt;Broker and log directory cordoning — KIP-1066&lt;/h2&gt;
&lt;p&gt;The new &lt;code&gt;cordoned.log.dirs&lt;/code&gt; configuration marks a log directory as off-limits for new partition replica placement. The broker stays up; existing replicas continue serving reads and writes. New partition assignments route around the cordoned directory. Kafka’s 4.3 operations documentation covers the &lt;a href=&quot;https://kafka.apache.org/43/operations/basic-kafka-operations/#decommissioning-brokers-and-log-directories&quot;&gt;full decommissioning workflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before 4.3, draining a broker or a specific disk required careful manual coordination: partition reassignment scripts, watching ISR (in-sync replica) lag, and accepting more operational risk than most teams wanted because the tooling made it genuinely awkward. Cordoning does not solve every decommissioning problem, but it addresses the most friction-heavy part: stopping the cluster from making the situation worse while you work.&lt;/p&gt;
&lt;p&gt;The workflow now looks like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Cordon the log directory (or broker, via &lt;code&gt;cordoned.log.dirs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Let the cluster route new placements elsewhere; no new replicas land on that target&lt;/li&gt;
&lt;li&gt;Drain existing replicas at your own pace using partition reassignment&lt;/li&gt;
&lt;li&gt;Decommission once drained&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is particularly useful in cloud-native environments where disk replacement or node cycling is a routine event rather than an incident. Teams running Kubernetes-based Kafka deployments will find this materially reduces the operational surface area of a node drain.&lt;/p&gt;
&lt;p&gt;Test this in staging first. Understand the interaction with your replica placement strategy and rack-awareness configuration before rolling it to production.&lt;/p&gt;
&lt;h2 id=&quot;partition-size-percentage-metrics--kip-1257&quot;&gt;Partition size percentage metrics — KIP-1257&lt;/h2&gt;
&lt;p&gt;Retention headroom has always been one of the more tedious Kafka metrics to derive. The raw data exists (log size, retention bytes), but assembling per-partition headroom from JMX required either custom tooling or cobbled-together dashboards that most teams quietly abandoned.&lt;/p&gt;
&lt;p&gt;KIP-1257 fixes this directly. New JMX metrics expose what percentage of maximum retention each topic-partition currently occupies. This gives you a first-class signal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identifying partitions approaching retention thresholds before they cause data loss or unexpected compaction behaviour&lt;/li&gt;
&lt;li&gt;Setting meaningful alerts on retention pressure without writing custom collectors&lt;/li&gt;
&lt;li&gt;Spotting topics where retention config is badly misaligned with actual throughput&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This metric belongs in every Kafka operations dashboard and is a straightforward addition once you upgrade.&lt;/p&gt;
&lt;p&gt;For teams who want these metrics surfaced alongside consumer lag, broker health, and throughput in a single view, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow by Factor House&lt;/a&gt; ingests JMX and Kafka-native metrics and will pick up these new partition size indicators without additional configuration.&lt;/p&gt;
&lt;h2 id=&quot;share-groups-real-operational-handles--kip-1240--kip-1263&quot;&gt;Share groups: real operational handles — KIP-1240 + KIP-1263&lt;/h2&gt;
&lt;p&gt;Share groups (Kafka’s queue-semantics consumer model, introduced in 4.0) arrive in 4.3 with the operational tuning options that early adopters have been waiting for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1240&lt;/strong&gt; adds new broker-level and group-level configurations to control share group behaviour. The specifics depend on your use case, but the significance is structural: share groups now have the configuration surface area you would expect from a production-grade feature. Teams that adopted early on 4.0 or 4.1 and found themselves with insufficient levers should review the new configs in the 4.3 documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1263&lt;/strong&gt; is quieter but arguably more impactful for clusters with high consumer group churn. The group coordinator’s assignment logic is improved to avoid recomputing assignments when not necessary. If you have environments with frequent consumer restarts, scaling events, or high group cardinality, this directly reduces coordinator overhead. It is not the kind of change that appears in a benchmark, but it smooths out the coordinator’s CPU profile in a busy cluster.&lt;/p&gt;
&lt;p&gt;Where share groups stand: 4.3 is a meaningful maturation milestone. If your team is on 4.2 and has not started with share groups yet, this release is a good prompt to re-evaluate the roadmap. If you are on 4.0 or 4.1 and already running share groups, upgrade and take the new configs seriously. If you were waiting for the project to stabilise before committing, the pace of development across recent releases gives a reasonable basis for planning.&lt;/p&gt;
&lt;h2 id=&quot;classic-rebalance-protocol-deprecation--kip-1251--kip-1274&quot;&gt;Classic rebalance protocol deprecation — KIP-1251 + KIP-1274&lt;/h2&gt;
&lt;p&gt;If your consumers are still using the classic rebalance protocol, 4.3 introduces Phase 1 of the deprecation process: warning only, with no removal yet. The direction is unambiguous.&lt;/p&gt;
&lt;p&gt;When a consumer starts with the &lt;code&gt;classic&lt;/code&gt; rebalance protocol, 4.3 logs a recommendation to migrate. This is the precursor to a formal deprecation signalled for a future release. The &lt;code&gt;group.coordinator.rebalance.protocols&lt;/code&gt; broker configuration is also deprecated in this release, marked for removal in Kafka 5.0.&lt;/p&gt;
&lt;p&gt;KIP-1251 is the complementary improvement: member epoch validation logic is tightened to reduce unnecessary fencing of group members. Concretely, this means fewer spurious rebalances triggered by epoch mismatches during consumer group stabilisation. If you have been seeing rebalance noise in stable groups, this is worth tracking after upgrading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action for operators:&lt;/strong&gt; Audit your consumer configurations now. Identify any consumer groups still using the classic protocol and build a migration timeline. The cooperative protocol has been the correct choice for new consumers since 2.4. Teams that defer this work until Kafka 5.0 will have less time to address it comfortably.&lt;/p&gt;
&lt;h2 id=&quot;tiered-storage-safer-and-smarter--kip-1023--kip-1208--kip-1235&quot;&gt;Tiered storage: safer and smarter — KIP-1023 + KIP-1208 + KIP-1235&lt;/h2&gt;
&lt;p&gt;Three tiered storage changes in 4.3: two operational and one correctness fix that deserves immediate attention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1235 — check this first if you run tiered storage.&lt;/strong&gt; The default &lt;code&gt;min.insync.replicas&lt;/code&gt; (ISR) for the internal &lt;code&gt;__remote_log_metadata&lt;/code&gt; topic was incorrect and did not align with what you would expect for a durable internal topic. 4.3 introduces a dedicated &lt;code&gt;remote.log.metadata.topic.min.isr&lt;/code&gt; configuration to set this explicitly. If you are running tiered storage today, check your current &lt;code&gt;__remote_log_metadata&lt;/code&gt; topic ISR configuration and align it with &lt;code&gt;remote.log.metadata.topic.min.isr&lt;/code&gt;. This is a correctness issue rather than a tuning recommendation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1023&lt;/strong&gt; introduces &lt;code&gt;follower.fetch.last.tiered.offset.enable&lt;/code&gt; (default: &lt;code&gt;false&lt;/code&gt;). When enabled, bootstrapping a new follower starts from the last tiered offset rather than the beginning of the local log. The practical effect: new brokers joining a cluster with tiered storage sync faster, and leader amplification during follower bootstrap is reduced. This is directly relevant to teams scaling clusters with large tiered storage configurations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1208&lt;/strong&gt; adds the &lt;code&gt;remote.log.metadata.admin.&lt;/code&gt; prefix for configuring the admin client used by tiered storage’s &lt;code&gt;RemoteLogMetadataManager&lt;/code&gt;. This is a configuration hygiene improvement that makes the namespace cleaner and the scoping more explicit.&lt;/p&gt;
&lt;p&gt;Taken together, tiered storage in 4.3 is safer to run (KIP-1235), faster to scale (KIP-1023), and better configured (KIP-1208).&lt;/p&gt;
&lt;h2 id=&quot;kraft-fetch-size-controls--kip-1219&quot;&gt;KRaft fetch size controls — KIP-1219&lt;/h2&gt;
&lt;p&gt;New configurations: &lt;code&gt;controller.quorum.fetch.max.bytes&lt;/code&gt; and &lt;code&gt;controller.quorum.fetch.snapshot.max.bytes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These cap the amount of data retrieved by KRaft Fetch and FetchSnapshot requests respectively. For most teams, these will never need to be changed from default. If you run large clusters where the KRaft controller shares resources with broker workloads, or where snapshot fetch operations have caused controller memory spikes, these controls give you a lever that did not previously exist.&lt;/p&gt;
&lt;p&gt;This is a niche but real pain point for teams running high-metadata-throughput clusters. If you have seen controller memory pressure during snapshot fetches, the relevance will be apparent.&lt;/p&gt;
&lt;h2 id=&quot;oauth-client-assertions--kip-1258&quot;&gt;OAuth client assertions — KIP-1258&lt;/h2&gt;
&lt;p&gt;Support for client assertion authentication in the &lt;code&gt;client_credentials&lt;/code&gt; OAuth grant type.&lt;/p&gt;
&lt;p&gt;This is a security posture improvement for teams using OAuth with enterprise identity providers (Okta, Azure Active Directory, and similar) that prefer or require signed client assertions over shared secrets. If your Kafka security implementation already uses &lt;code&gt;client_credentials&lt;/code&gt; and you have been working around the absence of assertion support, 4.3 resolves it natively. If this does not describe your environment, it is unlikely to affect your upgrade decision.&lt;/p&gt;
&lt;h2 id=&quot;kafka-streams-what-changed-for-stream-processors&quot;&gt;Kafka Streams: what changed for stream processors&lt;/h2&gt;
&lt;p&gt;Not a Streams-heavy release, but several targeted improvements are worth cataloguing for teams running Streams in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1259: Automatic local state cleanup on startup.&lt;/strong&gt; The new &lt;code&gt;state.cleanup.dir.max.age.ms&lt;/code&gt; configuration tells Streams to delete state directories that have not been modified within the specified duration on startup. This addresses a genuine hygiene problem in containerised Streams deployments where stale state accumulates across restarts and pod cycling. Previously, cleaning this required external intervention or custom initialisation logic. If you run Streams in Kubernetes or any environment with ephemeral containers, add this to your config review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1271 + KIP-1285: Headers in state stores.&lt;/strong&gt; Record headers can now be stored in and retrieved from state stores, exposed through both the Processor API (KIP-1271) and the DSL (KIP-1285). Teams using headers to carry routing metadata, tracing context, or correlation IDs will find this closes a meaningful gap — previously those headers had to be stripped or handled separately before writing to state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1270: ProcessingExceptionHandler for GlobalKTable threads.&lt;/strong&gt; Exception handling now extends to GlobalKTable threads via the &lt;code&gt;processing.exception.handler.global.enabled&lt;/code&gt; configuration. Before 4.3, exceptions in the global thread could cause opaque failures that were difficult to surface cleanly. This is a reliability improvement for any topology that uses global state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1035: StateStore changelog offset management.&lt;/strong&gt; Adds methods to the &lt;code&gt;StateStore&lt;/code&gt; API for changelog offset management. This is an internal change primarily relevant to custom &lt;code&gt;StateStore&lt;/code&gt; implementations. If you maintain a custom state store, review the updated API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1247: &lt;code&gt;Bytes&lt;/code&gt; utility class promoted to public API.&lt;/strong&gt; The &lt;code&gt;Bytes&lt;/code&gt; class is now part of the public API and will appear in the Javadoc. Minor, but useful for teams that have been importing it from internal packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1250: In-memory state store size metrics.&lt;/strong&gt; New metrics tracking the number of keys in in-memory state stores. If your Streams topologies use in-memory stores and you have limited visibility into store growth, this is a monitoring addition worth wiring up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1244: &lt;code&gt;streams-scala&lt;/code&gt; module deprecated.&lt;/strong&gt; If your team uses the Scala DSL for Kafka Streams, 4.3 is the start of the deprecation clock. Marked for removal in Kafka 5.0. Plan migration now rather than at the 5.0 deadline.&lt;/p&gt;
&lt;h2 id=&quot;kafka-connect&quot;&gt;Kafka Connect&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;KIP-1239:&lt;/strong&gt; &lt;code&gt;RemoteClusterUtils.translateOffsets()&lt;/code&gt; now accepts multiple consumer groups in a single call. For teams running MirrorMaker 2 across clusters with many groups, this is a meaningful quality-of-life improvement that reduces the number of round trips required for offset translation workflows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1273:&lt;/strong&gt; A new &lt;code&gt;ConnectPlugin&lt;/code&gt; interface standardises methods across all Kafka Connect plugin types. Teams building or maintaining custom connectors should review this — it establishes a common contract that all plugin types now implement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-1280:&lt;/strong&gt; MirrorMaker metric names are being updated. The new &lt;code&gt;metric.names.formats&lt;/code&gt; configuration on &lt;code&gt;MirrorSourceConnector&lt;/code&gt; and &lt;code&gt;MirrorCheckpointConnector&lt;/code&gt; lets you opt into the new metric names. Existing metric names are deprecated and will be removed in Kafka 5.0. If you have dashboards or alerts built on MirrorMaker metrics, schedule the migration before 5.0.&lt;/p&gt;
&lt;h2 id=&quot;deprecation-roundup&quot;&gt;Deprecation roundup&lt;/h2&gt;
&lt;p&gt;Everything deprecated in 4.3.0 in one place. Set calendar reminders. Kafka 5.0 removals tend to arrive faster than teams expect.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;KIP&lt;/th&gt;
&lt;th&gt;Removal target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;streams-scala module&lt;/td&gt;
&lt;td&gt;KIP-1244&lt;/td&gt;
&lt;td&gt;Kafka 5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;group.coordinator.rebalance.protocols broker config&lt;/td&gt;
&lt;td&gt;KIP-1237&lt;/td&gt;
&lt;td&gt;Kafka 5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing MirrorMaker metric names&lt;/td&gt;
&lt;td&gt;KIP-1280&lt;/td&gt;
&lt;td&gt;Kafka 5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classic consumer rebalance protocol&lt;/td&gt;
&lt;td&gt;KIP-1274 (Phase 1 — warning only)&lt;/td&gt;
&lt;td&gt;Future release&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The classic rebalance protocol deprecation is Phase 1 only in 4.3: no removal yet, just logging. The 5.0 items are the ones that need concrete action.&lt;/p&gt;
&lt;h2 id=&quot;upgrade-readiness-checklist&quot;&gt;Upgrade readiness checklist&lt;/h2&gt;
&lt;p&gt;A practical checklist for teams preparing a 4.3.0 upgrade. Work through this before scheduling your maintenance window.&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Read the &lt;a href=&quot;https://kafka.apache.org/documentation.html#upgrade_4_3_0&quot;&gt;official 4.3.0 upgrade notes&lt;/a&gt; — specifically any version-specific upgrade requirements&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Audit consumer configurations — identify any groups still configured with the classic rebalance protocol and build a migration timeline&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; If running tiered storage: check your current &lt;code&gt;__remote_log_metadata&lt;/code&gt; topic ISR configuration and align with the new &lt;code&gt;remote.log.metadata.topic.min.isr&lt;/code&gt; config&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; If tiered storage with large follower bootstrap latency is a concern: evaluate &lt;code&gt;follower.fetch.last.tiered.offset.enable&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Update MirrorMaker dashboards and alerts to account for new metric names via &lt;code&gt;metric.names.formats&lt;/code&gt; — plan migration before 5.0&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Identify any &lt;code&gt;streams-scala&lt;/code&gt; usage and scope a migration plan if applicable&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Wire up new partition size percentage metrics in your monitoring stack&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Test the broker and log directory cordoning workflow in staging before relying on it in production decommissioning&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Review share group configurations and new tuning options in 4.3 if you are running or evaluating share groups&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Remove any references to the deprecated &lt;code&gt;group.coordinator.rebalance.protocols&lt;/code&gt; broker config from your configuration management&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-operator-signal&quot;&gt;The operator signal&lt;/h2&gt;
&lt;p&gt;Kafka 4.3.0 continues a pattern visible across the 4.x series. The feature set is not about new consumer semantics or Streams DSL additions — it is about the operational experience of running Kafka at scale. Broker cordoning, retention headroom metrics, coordinator performance, tiered storage correctness, KRaft fetch controls: these are the kinds of improvements that accumulate into a platform that operations engineers can rely on.&lt;/p&gt;
&lt;p&gt;Teams evaluating Kafka adoption or upgrade timing should note that 4.x has been consistently improving the operational story with each release. 4.3.0 continues that pattern.&lt;/p&gt;
&lt;p&gt;Keeping up with a release cadence like this is easier with good cluster visibility. If you are upgrading to 4.3 and want to make the most of the new metrics and operational improvements, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow by Factor House&lt;/a&gt; gives you an observability layer that covers partition size headroom, consumer group health, tiered storage state, and broker-level metrics in a single interface. You can try it for yourself with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/blog/2026/05/22/apache-kafka-4.3.0-release-announcement/&quot;&gt;Official Kafka 4.3.0 release announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://downloads.apache.org/kafka/4.3.0/RELEASE_NOTES.html&quot;&gt;Release notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation.html#upgrade_4_3_0&quot;&gt;Upgrade notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/43/operations/basic-kafka-operations/#decommissioning-brokers-and-log-directories&quot;&gt;Decommissioning brokers and log dirs — 4.3 operations docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/downloads&quot;&gt;Download Kafka 4.3.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow by Factor House&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Industry</category><author>Factor House</author></item><item><title>How Apple uses Apache Kafka in production</title><link>https://factorhouse.io/articles/apple-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/apple-kafka-architecture/</guid><description>A deep-dive into Apple&apos;s Kafka architecture — covering their managed internal platform, Strimzi on EKS, tiered storage, zero-data-movement balancing, and mTLS migration.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apple runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; as a shared internal platform serving multiple engineering teams across the company. Since at least 2018, Apple’s engineers have presented at Kafka Summit on the operational realities of running Kafka as a managed, multi-tenant service at the scale you would expect from one of the world’s largest technology companies. Their publicly documented work spans partition management at scale, a custom zero-data-movement balancing algorithm, Kubernetes-native tiered storage, and an ongoing migration from password-based authentication to mTLS.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Apple designs hardware, software, and services including the iPhone, iPad, Mac, Apple Watch, iCloud, Apple Music, Apple TV+, and the App Store. Those services operate at a scale that requires low-latency, fault-tolerant data infrastructure: iCloud alone serves hundreds of millions of users, while the App Store processes purchases across more than 175 countries.&lt;/p&gt;
&lt;p&gt;Kafka sits within the data infrastructure layer that keeps these services fed with real-time event data. The iCloud Data organisation, for example, uses stream-processing technologies that include Kafka for building scalable data pipelines. Apple began operating Kafka as a multi-tenant internal service no later than 2018, when they first presented on the architecture at Kafka Summit London.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2018:&lt;/strong&gt; Apple presents “Kafka as a Service: A Tale of Security and Multi Tenancy” at Kafka Summit London, confirming that Kafka is operated as a managed internal platform with multi-tenancy and authentication controls.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2019:&lt;/strong&gt; Noa Resare presents operational lessons from running Kafka at large scale at Kafka Summit New York, covering partition reassignment challenges at scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2023:&lt;/strong&gt; Haochen Li and Yaodong Yang present a zero-data-movement partition placement algorithm at Kafka Summit London and deploy it to production as a Topic Operator.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2024:&lt;/strong&gt; Tiered storage on Kafka 3.6 reaches production, following a year-long integration effort using Strimzi on AWS EKS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2024:&lt;/strong&gt; Apple’s tiered storage contribution is merged into Strimzi 0.40.0, making native tiered storage support available to the wider community.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2025:&lt;/strong&gt; Gaurav Narula presents a hybrid mTLS migration strategy at Confluent Current, describing how Apple is moving from SASL password authentication to short-lived X.509 certificates.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;apples-kafka-use-cases&quot;&gt;Apple’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Apple’s Kafka deployment centres on internal data infrastructure rather than a single named product use case. Their engineering talks describe Kafka as a platform shared across teams, which is consistent with how large technology companies typically use Kafka at enterprise scale.&lt;/p&gt;
&lt;p&gt;The iCloud Data organisation cites Kafka as part of the technology set used to build scalable, timely data pipelines for internal teams that ship product features. Apple’s job postings for iCloud data engineering roles reference Kafka alongside Apache Flink, Kafka Streams, and Apache Spark Streaming, suggesting that Kafka is used for real-time data movement between services as well as stream processing.&lt;/p&gt;
&lt;p&gt;The Kafka as a Service model Apple described in 2018 implies a broad adoption pattern: rather than individual teams standing up their own clusters, a central platform team manages shared clusters, and product teams onboard as tenants.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;Apple does not publish aggregate cluster statistics publicly. The figures available come directly from conference presentations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clusters:&lt;/strong&gt; Multiple Kafka clusters operated as a shared internal service; exact count not publicly disclosed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brokers per cluster:&lt;/strong&gt; 6 brokers confirmed in the 2024 tiered storage production environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-broker throughput:&lt;/strong&gt; Measured in the Strimzi/tiered storage test environment: 270 MB/s with a single consumer; 250 MB/s with three consumers; 90 MB/s with five consumers across 400 topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partitions:&lt;/strong&gt; Described as reaching a volume where large-scale reassignment operations are a regular operational concern; exact count not publicly disclosed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;/strong&gt; EBS-backed local storage per broker, supplemented by S3 tiered storage for older data following the 2024 production rollout.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;apples-kafka-architecture&quot;&gt;Apple’s Kafka architecture&lt;/h2&gt;
&lt;p&gt;Apple’s Kafka clusters run on Kubernetes using the Strimzi operator on AWS EKS. Broker nodes use Amazon EBS volumes for local storage. Helm is used to manage Strimzi deployments across environments. This Kubernetes-native approach means cluster lifecycle operations, scaling, and upgrades are managed through Kubernetes custom resources rather than direct broker administration.&lt;/p&gt;
&lt;p&gt;Since February 2024, Apple has operated Kafka with tiered storage (KIP-405) in production. Older log segments are offloaded to Amazon S3, separating data retention from local broker capacity. The Remote Log Metadata Manager (RLMM) uses an internal Kafka client secured with SSL/TLS using a PKCS12 keystore and truststore, configured via the &lt;code&gt;rsm.config&lt;/code&gt; prefix.&lt;/p&gt;
&lt;p&gt;Security is layered. Apple has operated Kafka with SASL authentication and ACL-based authorisation since at least 2018. As of 2025, the platform team is actively migrating toward mTLS using short-lived X.509 certificates, driven by the security advantages of certificate-based identity over long-lived passwords in a multi-tenant environment.&lt;/p&gt;
&lt;h3 id=&quot;producer-and-consumer-architecture&quot;&gt;Producer and consumer architecture&lt;/h3&gt;
&lt;p&gt;Apple’s partition placement algorithm (described below) is built on the assumption that data ingestion workloads assign events randomly to partitions with no ordering requirements, and that all partitions from a topic are consumed evenly. This is consistent with a high-throughput event ingestion pattern where producers distribute load across partitions and consumers process each partition independently.&lt;/p&gt;
&lt;p&gt;Consumer lag monitoring is handled by Burrow, which is deployed alongside CMAK for cluster administration.&lt;/p&gt;
&lt;h3 id=&quot;operating-model&quot;&gt;Operating model&lt;/h3&gt;
&lt;p&gt;The platform is provided to internal teams as a managed service. This means the central Kafka team is responsible for cluster provisioning, upgrades, monitoring, and operational tooling, while product teams interact with the platform as tenants. Multi-tenancy brings authentication, authorisation, quota management, and topic namespace concerns, all of which Apple has addressed in successive talks.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;zero-data-movement-cluster-balancing&quot;&gt;Zero-data-movement cluster balancing&lt;/h3&gt;
&lt;p&gt;Apple engineers Haochen Li and Yaodong Yang developed a partition placement strategy that achieves optimal cluster load balancing without moving any data between brokers. Traditional rebalancing tools, including Cruise Control, rebalance clusters by reassigning partitions, which involves copying data from one broker to another. Apple’s approach avoids that cost entirely by placing partitions correctly at the point of creation.&lt;/p&gt;
&lt;p&gt;The algorithm enforces two properties across every topic: the number of replicas per broker is equal, and the number of leader replicas per broker is equal. The formula applied is &lt;code&gt;partition_count = scale_number * broker_count&lt;/code&gt;, where &lt;code&gt;scale_number&lt;/code&gt; is the number of leader replicas per broker for a topic. This is applied at topic creation, when partition count changes, and when brokers are added to a cluster.&lt;/p&gt;
&lt;p&gt;The strategy is implemented as a Topic Operator and has been deployed to production. The result, as described by Haochen Li and Yaodong Yang, was well-balanced clusters with measurable storage cost savings and no performance degradation. The team submitted a Kafka Improvement Proposal (KIP) to upstream the approach to the Apache Kafka project.&lt;/p&gt;
&lt;h3 id=&quot;tiered-storage-on-strimzi&quot;&gt;Tiered storage on Strimzi&lt;/h3&gt;
&lt;p&gt;Apple ran a structured integration effort to bring Kafka tiered storage (KIP-405) into production on their Strimzi-managed clusters. The work began in February 2023 with an evaluation using the KIP-405 author’s Kafka 2.8.x development branch and progressed through a series of phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;April 2023:&lt;/strong&gt; Prototype integration of Strimzi and S3 plugin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;September 2023:&lt;/strong&gt; Kafka 2.8 development branch fully tested.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;November 2023:&lt;/strong&gt; Kafka 3.6.0 dogfooding phase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2024:&lt;/strong&gt; Production rollout on Kafka 3.6.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The integration required upgrading Strimzi from 0.27.x to 0.38.x and Java from 11 to 17. Apple contributed native tiered storage support back to the Strimzi project, which was merged in Strimzi 0.40.0. This means you can now configure a custom remote storage manager in Strimzi via standard Kafka custom resource definitions, a capability that originated from Apple’s production work.&lt;/p&gt;
&lt;p&gt;Performance tuning for tiered storage required several configuration adjustments: &lt;code&gt;max.fetch.wait.time&lt;/code&gt; was increased to tolerate the additional latency of remote object fetches; &lt;code&gt;remote.log.reader.threads&lt;/code&gt; and thread pool sizes were tuned for throughput; log segment sizes were optimised to keep RLMM metadata overhead manageable; and AWS multipart uploads were configured to run concurrently alongside range fetch APIs for parallel I/O.&lt;/p&gt;
&lt;h3 id=&quot;mtls-migration&quot;&gt;mTLS migration&lt;/h3&gt;
&lt;p&gt;Gaurav Narula from Apple described a hybrid migration strategy for moving from SASL password authentication to mTLS. The challenge with any mTLS migration in a large shared platform is that you cannot require all clients to switch simultaneously without breaking production workloads.&lt;/p&gt;
&lt;p&gt;Apple’s approach adds mTLS support directly to the SASL listener, so both authentication methods are accepted on the same port. Existing clients continue to authenticate with passwords while new or migrated clients present X.509 certificates. KafkaPrincipal identities are preserved across both methods, which means ACLs and quota configurations do not need to change when a client migrates. For inter-broker communication, each broker is configured with distinct server and client certificates.&lt;/p&gt;
&lt;p&gt;The use of short-lived X.509 certificates addresses two specific weaknesses of SASL passwords: credential leak impact is bounded by certificate lifetime, and certificate-based authentication is not vulnerable to brute-force attacks.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes-native operations:&lt;/strong&gt; Strimzi handles cluster provisioning, rolling upgrades, and configuration changes through Kubernetes custom resources. This removes the need for manual broker-level administration and integrates Kafka lifecycle management with existing container platform tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and observability:&lt;/strong&gt; Burrow provides consumer lag monitoring. CMAK provides a cluster administration interface. Both run alongside the Strimzi operator in the Kafka platform stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster rebalancing:&lt;/strong&gt; Cruise Control is deployed for automated partition load balancing, though Apple’s zero-data-movement algorithm addresses a significant portion of imbalance at topic-creation time rather than reactively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition reassignment at scale:&lt;/strong&gt; Noa Resare’s 2019 talk described the operational challenge of managing large-scale partition reassignments: tracking progress, understanding the impact on producers and consumers during broker restarts, and applying debugging and mitigation strategies when reassignments do not proceed as expected. At large partition counts, these operations become significant engineering events rather than routine maintenance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upgrade strategy:&lt;/strong&gt; The tiered storage work illustrates Apple’s approach to major upgrades: start with an evaluation phase on a development branch, build a prototype integration, run dogfooding before committing to production, and execute the production rollout as a distinct step. The Kafka 2.8 to 3.6 upgrade and the Strimzi 0.27 to 0.38 jump were managed as part of the tiered storage project rather than independently.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;cluster-imbalance-without-data-movement&quot;&gt;Cluster imbalance without data movement&lt;/h3&gt;
&lt;p&gt;As Kafka clusters grow or traffic patterns shift, broker resource utilisation becomes uneven. CPU, disk, and leader distribution diverge, leading to over-provisioned capacity on some brokers and bottlenecks on others. The conventional remedy is partition reassignment, but at scale this involves copying large volumes of data between brokers, creating I/O pressure and operational risk.&lt;/p&gt;
&lt;p&gt;Apple developed the zero-data-movement placement algorithm to solve this at the source. By enforcing balanced placement at topic-creation time, the problem of runtime imbalance is largely avoided. When it does occur, during cluster expansion or partition scaling, the same algorithm is applied to achieve balance without data movement.&lt;/p&gt;
&lt;h3 id=&quot;cost-of-ebs-backed-data-retention&quot;&gt;Cost of EBS-backed data retention&lt;/h3&gt;
&lt;p&gt;Storing all log data on EBS volumes attached to broker nodes means storage costs scale directly with retention windows. For teams that need to retain large event histories for backfill, reprocessing, or compliance, this becomes a significant expense. Tiered storage decouples retention from broker capacity: older segments are offloaded to S3, which is substantially cheaper than EBS, while brokers remain sized for hot data and throughput.&lt;/p&gt;
&lt;p&gt;The challenge Apple faced was that Strimzi did not natively support tiered storage configuration at the time. Solving this required building the integration, testing it across multiple Kafka versions, and contributing the result back to Strimzi before production deployment.&lt;/p&gt;
&lt;h3 id=&quot;password-based-authentication-in-a-multi-tenant-service&quot;&gt;Password-based authentication in a multi-tenant service&lt;/h3&gt;
&lt;p&gt;In a shared Kafka platform with many producer and consumer clients, SASL password credentials create a persistent risk surface. Long-lived credentials can be leaked and remain valid indefinitely. They are also vulnerable to brute-force attacks in a way that certificate-based authentication is not.&lt;/p&gt;
&lt;p&gt;The transition to mTLS eliminates these risks, but migrating a large multi-tenant platform is not straightforward: you cannot break existing clients while switching authentication mechanisms. Apple’s hybrid SASL-plus-mTLS approach on a shared listener resolves this by making the migration incremental and non-disruptive.&lt;/p&gt;
&lt;h3 id=&quot;large-scale-partition-reassignment&quot;&gt;Large-scale partition reassignment&lt;/h3&gt;
&lt;p&gt;At the partition counts Apple operates, reassigning partitions, whether to recover from broker imbalance, decommission a broker, or respond to capacity changes, becomes a complex operational exercise. Tracking progress across many concurrent reassignments, managing the impact on producers and consumers, and debugging failures when they occur requires both tooling and operational discipline. Noa Resare’s 2019 talk shared the lessons Apple accumulated from running these operations repeatedly at scale.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka 3.6+&lt;/td&gt;
&lt;td&gt;Core streaming platform; tiered storage available from 3.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka operator&lt;/td&gt;
&lt;td&gt;Strimzi&lt;/td&gt;
&lt;td&gt;Kubernetes-native cluster lifecycle management on EKS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container platform&lt;/td&gt;
&lt;td&gt;AWS EKS&lt;/td&gt;
&lt;td&gt;Kubernetes runtime for broker and operator workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local broker storage&lt;/td&gt;
&lt;td&gt;Amazon EBS&lt;/td&gt;
&lt;td&gt;Block storage attached to broker nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiered (remote) storage&lt;/td&gt;
&lt;td&gt;Amazon S3 (via KIP-405 plugin)&lt;/td&gt;
&lt;td&gt;Cost-effective long-term event retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaging&lt;/td&gt;
&lt;td&gt;Helm&lt;/td&gt;
&lt;td&gt;Strimzi deployment management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster rebalancing&lt;/td&gt;
&lt;td&gt;Cruise Control&lt;/td&gt;
&lt;td&gt;Automated partition load balancing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer lag monitoring&lt;/td&gt;
&lt;td&gt;Burrow&lt;/td&gt;
&lt;td&gt;Consumer group lag visibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster administration&lt;/td&gt;
&lt;td&gt;CMAK&lt;/td&gt;
&lt;td&gt;Kafka cluster management UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;mTLS (X.509 / PKCS12), SASL&lt;/td&gt;
&lt;td&gt;Client and inter-broker authentication; migrating toward mTLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Kafka broker and tooling runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Noa Resare&lt;/strong&gt; — Apple engineer; spoke on operating Kafka at large scale, Kafka Summit NY 2019. &lt;a href=&quot;https://videos.confluent.io/watch/dBdby8aKRJ3Dxqh6MZ9qZC&quot;&gt;Talk recording&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Haochen Li&lt;/strong&gt; — Apple software engineer; co-developed the zero-data-movement partition placement algorithm, Kafka Summit London 2023. &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-london-2023/balance-kafka-cluster-with-zero-data-movement/&quot;&gt;Session&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Yaodong Yang&lt;/strong&gt; — Apple software engineer; co-developed the zero-data-movement partition placement algorithm, Kafka Summit London 2023.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bo Gao&lt;/strong&gt; — Apple engineer; led the Strimzi tiered storage integration project, Kafka Summit London 2024. &lt;a href=&quot;https://www.confluent.io/es-es/events/kafka-summit-london-2024/leveraging-tiered-storage-in-strimzi-operated-kafka-for-cost-effective/&quot;&gt;Session&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lixin Yao&lt;/strong&gt; — Apple engineer; co-led the Strimzi tiered storage integration project, Kafka Summit London 2024.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gaurav Narula&lt;/strong&gt; — Apple engineer; presented the hybrid mTLS migration approach, Confluent Current 2025. &lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/leave-your-passwords-behind-embracing-mtls-in-kafka-lnd25&quot;&gt;Session&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Address cluster imbalance at creation time, not at rebalance time.&lt;/strong&gt; Apple’s zero-data-movement algorithm enforces balanced partition and leader placement when topics are created or scaled. This reduces or eliminates the need for reactive rebalancing operations, which are expensive in large clusters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tiered storage is a practical path to decoupling retention from broker capacity.&lt;/strong&gt; Apple’s production journey from prototype (February 2023) to production (February 2024) demonstrates that tiered storage on Strimzi is achievable with deliberate testing phases, though it requires careful performance tuning, particularly around fetch latency and thread pool sizing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrating authentication mechanisms in a shared platform requires a hybrid transition period.&lt;/strong&gt; Adding mTLS to the existing SASL listener rather than replacing it allows clients to migrate at their own pace. Preserving KafkaPrincipal identities means existing ACLs remain valid throughout the transition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you operate Kafka as a managed service for internal teams, multi-tenancy concerns appear early.&lt;/strong&gt; Security enforcement (authentication, authorisation, quotas), partition reassignment at scale, and cluster observability all become harder in a shared environment than in a single-team deployment. Apple’s public talks trace a consistent investment in each of these areas over several years.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contributing upstream reduces long-term maintenance burden.&lt;/strong&gt; Apple’s Strimzi tiered storage integration was contributed back to the project and merged in Strimzi 0.40.0. If you are building integrations on top of open source Kafka tooling, upstream contributions mean you are no longer carrying a fork.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Noa Resare, Apple — “&lt;a href=&quot;https://www.confluent.io/kafka-summit-ny19/experiences-operating-apache-kafka-at-scale/&quot;&gt;Experiences Operating Apache Kafka® at Scale&lt;/a&gt;”, Kafka Summit NY 2019&lt;/li&gt;
&lt;li&gt;Apple — “&lt;a href=&quot;https://www.confluent.io/kafka-summit-london18/kafka-as-a-service-a-tale-of-security-and-multi-tenancy/&quot;&gt;Kafka as a Service: A Tale of Security and Multi Tenancy&lt;/a&gt;”, Kafka Summit London 2018, &lt;a href=&quot;https://www.youtube.com/watch?v=EcH06CY93Uo&quot;&gt;video&lt;/a&gt;‍&lt;/li&gt;
&lt;li&gt;Haochen Li and Yaodong Yang, Apple — “&lt;a href=&quot;https://www.confluent.io/events/kafka-summit-london-2023/balance-kafka-cluster-with-zero-data-movement/&quot;&gt;Balance Kafka Cluster with Zero Data Movement&lt;/a&gt;”, Kafka Summit London 2023&lt;/li&gt;
&lt;li&gt;Bo Gao and Lixin Yao, Apple — “&lt;a href=&quot;https://www.confluent.io/es-es/events/kafka-summit-london-2024/leveraging-tiered-storage-in-strimzi-operated-kafka-for-cost-effective/&quot;&gt;Leveraging Tiered Storage in Strimzi-Operated Kafka for Cost-Effective Streaming Applications&lt;/a&gt;”, Kafka Summit London 2024&lt;/li&gt;
&lt;li&gt;Gaurav Narula, Apple — “&lt;a href=&quot;https://current.confluent.io/post-conference-videos-2025/leave-your-passwords-behind-embracing-mtls-in-kafka-lnd25&quot;&gt;Leave Your Passwords Behind: Embracing mTLS in Kafka&lt;/a&gt;”, Confluent Current 2025&lt;/li&gt;
&lt;li&gt;dttung2905/&lt;a href=&quot;https://github.com/dttung2905/kafka-in-production&quot;&gt;kafka-in-production&lt;/a&gt; — Apple entries&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you work with a Kafka cluster and want visibility into consumer lag, broker health, and topic throughput without standing up additional infrastructure, try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. You can connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Barclays uses Apache Kafka in production</title><link>https://factorhouse.io/articles/barclays-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/barclays-kafka-architecture/</guid><description>A deep-dive into Barclays&apos; Kafka architecture — covering dual-environment deployment on AWS and IBM Z-Linux, operating practices, and the broader streaming stack.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most banks that adopt &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; keep it in the cloud or on commodity Linux hardware. Barclays does something less common: the bank runs separate Confluent Kafka deployments on Amazon EKS and on IBM Z-Linux, bringing the streaming layer into direct contact with mainframe workloads rather than bridging them through a connector from outside. That architectural choice sits alongside a Solace PubSub+ messaging layer that has been in place since 2009, creating a layered messaging estate that reflects the complexity of a global bank operating across front, middle, and back office.&lt;/p&gt;
&lt;p&gt;Barclays is listed on the official Apache Kafka Powered By page with the description “Barclays utilizes Kafka for streaming and analytical information.” That is the bank’s only public characterisation of its Kafka usage. The rest of what is known comes from official job postings, vendor case studies, and industry award citations, all of which point to Confluent Kafka as the chosen distribution and to an engineering model built around SRE principles and Infrastructure as Code.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Barclays is a British multinational bank headquartered in London, operating across retail banking, corporate banking, and investment banking. The bank serves tens of millions of customers and processes millions of financial transactions daily. Its investment banking arm, Barclays Investment Bank, operates across equities, fixed income, foreign exchange, and commodities.&lt;/p&gt;
&lt;p&gt;In 2025, Barclays won the Databricks Data Intelligence Financial Services Industry Award for adopting “a scalable and unified data platform that supports global trade analytics, complex data warehousing use cases and real-time insights,” with the platform delivering “real-time streaming capabilities, native machine learning, generative AI capabilities and comprehensive governance.”&lt;/p&gt;
&lt;p&gt;Key milestones in the bank’s messaging and streaming history:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2009:&lt;/strong&gt; Barclays deploys Solace PubSub+ as its enterprise-wide high-speed messaging platform, integrating applications across front, middle, and back office.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2025:&lt;/strong&gt; Barclays wins the Databricks Data Intelligence Financial Services Industry Award for its unified data platform with real-time streaming capabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;April 2026:&lt;/strong&gt; Barclays posts simultaneous open roles for Confluent Kafka engineers across three distinct specialisms: cluster management, AWS deployment, and IBM Z-Linux deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;barclays-kafka-use-cases&quot;&gt;Barclays’ Kafka use cases&lt;/h2&gt;
&lt;p&gt;Barclays’ public description of its Kafka usage is deliberately broad: streaming and analytical information. The bank has not published engineering blog posts that map specific products or teams to Kafka pipelines, so the breakdown here is limited to what can be inferred from official sources.&lt;/p&gt;
&lt;p&gt;The job postings describe engineering teams responsible for “event-driven architectures (EDA) using industry-standard messaging and streaming platforms,” with ownership of “Kafka-centric systems” framed as a platform engineering concern rather than a product-specific one. The phrasing suggests Kafka is positioned as shared infrastructure serving multiple consuming teams, with a central platform team responsible for cluster operations, capacity planning, and governance.&lt;/p&gt;
&lt;p&gt;In Barclays’ post-trade technology estate, a microservices-based settlement platform handles cash settlement processing. A Camunda case study attributed to Shakir Ahmed (Director of Operations Technology and Strategy) and Larisa Kvetnoy (MD Post Trade Technology) describes this system as using a message bus for distributed microservices communication, targeting 500,000 or more daily settlement processes and handling T+1 regulatory settlement requirements. The case study does not explicitly name Kafka as the message bus; it is included here as context for the broader event-driven architecture at the bank.&lt;/p&gt;
&lt;h2 id=&quot;barclays-kafka-architecture&quot;&gt;Barclays’ Kafka architecture&lt;/h2&gt;
&lt;p&gt;The most distinctive aspect of Barclays’ Kafka setup is the split between two separate deployment environments, each with its own engineering specialism.&lt;/p&gt;
&lt;h3 id=&quot;aws-cluster&quot;&gt;AWS cluster&lt;/h3&gt;
&lt;p&gt;Barclays runs Confluent Kafka on Amazon EKS. The cluster topology supports multi-region AWS deployments in both active-active and active-passive configurations. Engineers are required to design “multi-region AWS architectures, including active-active and active-passive deployments, with clear failover, disaster recovery, and data consistency strategies.” Kafka workloads run as containerised applications on Kubernetes, using StatefulSets, persistent volumes, services, and ingress controllers.&lt;/p&gt;
&lt;p&gt;The active-active configuration is the more demanding of the two patterns: both regional clusters serve traffic simultaneously, which requires consistent offset management across regions, careful handling of replication lag, and a defined approach to data consistency for consumers that may be reading from either cluster. Barclays’ job requirements call for explicit expertise in this area. The specific replication mechanism (MirrorMaker 2, Confluent Replicator, or an alternative) is not named in available sources.&lt;/p&gt;
&lt;h3 id=&quot;ibm-z-linux-cluster&quot;&gt;IBM Z-Linux cluster&lt;/h3&gt;
&lt;p&gt;A separate Confluent Kafka deployment runs on IBM Z-Linux. This is a less common pattern. The typical approach for mainframe-to-Kafka integration is to run Kafka on separate infrastructure and bridge the gap using Kafka Connect with an IBM MQ connector, IBM Data Gate for Confluent, or IBM’s Open Enterprise SDK for Apache Kafka. Barclays instead co-locates Confluent Kafka brokers on the IBM Z platform itself, running on the Linux partition of the mainframe.&lt;/p&gt;
&lt;p&gt;The rationale for this approach is not documented in public sources, but the engineering case is straightforward: applications running natively on IBM Z can produce and consume Kafka events without traversing a network boundary to reach a separate Kafka cluster. For latency-sensitive workloads on mainframe systems, removing that hop is meaningful. The Z-Linux role requires understanding of “hybrid platform interoperability within regulated enterprise environments” and compliance with Barclays’ UK standards.&lt;/p&gt;
&lt;h3 id=&quot;coexisting-messaging-layer&quot;&gt;Coexisting messaging layer&lt;/h3&gt;
&lt;p&gt;Solace PubSub+ has been Barclays’ enterprise-wide high-speed messaging platform since 2009. The bank consolidated its messaging needs onto the Solace platform to integrate applications across front, middle, and back office, simplifying infrastructure and reducing licensing, datacenter, development, and support costs. The relationship between the Solace layer and the Confluent Kafka deployments is not described in any available source. Both appear to be active parts of Barclays’ messaging estate.&lt;/p&gt;
&lt;h3 id=&quot;producer-and-consumer-architecture&quot;&gt;Producer and consumer architecture&lt;/h3&gt;
&lt;p&gt;Barclays’ Kafka engineering standards cover both sides of the producer-consumer boundary. On the producer side, engineers are expected to understand event ordering, partitioning strategy, replay capability, and exactly-once vs at-least-once semantics. On the consumer side, offset management and schema evolution are listed as required competencies. Confluent Schema Registry is part of the deployment, with schema evolution identified as an area requiring hands-on expertise. The schema encoding format (Avro, Protobuf, or JSON Schema) is not specified in available sources.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Kafka Connect is in use across the Confluent Kafka deployments. The specific source and sink connectors in production are not named in any public source.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Confluent Kafka on IBM Z-Linux.&lt;/strong&gt; Running Kafka brokers natively on IBM Z-Linux is not a common deployment pattern. It requires a build of the JVM and Confluent runtime that targets the s390x architecture, and it places the streaming layer inside the mainframe’s security and compliance boundary rather than outside it. For a regulated institution like Barclays, keeping data within a known, auditable infrastructure footprint can simplify governance. The approach also avoids the latency introduced by external connectors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KRaft mode.&lt;/strong&gt; The IBM Z-Linux job posting references both ZooKeeper and KRaft mode, indicating Barclays is running or actively evaluating Confluent Kafka in KRaft (ZooKeeper-free) mode. KRaft became generally available in Kafka 3.3 and removes the external ZooKeeper dependency, simplifying cluster operations and reducing the number of processes to monitor and manage. Whether both the AWS and Z-Linux clusters are in KRaft mode is not stated in available sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-region active-active on AWS.&lt;/strong&gt; An active-active multi-region Kafka topology is more operationally complex than an active-passive warm-standby. Barclays’ requirements for this pattern specify explicit expertise in “failover, disaster recovery, and data consistency strategies,” suggesting the architecture is designed to stay up and serving traffic in the event of a regional AWS outage, not merely to recover to a secondary region after a failover event.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model.&lt;/strong&gt; The AWS cluster is deployed on Amazon EKS (self-managed Confluent Kafka on Kubernetes). The IBM Z-Linux cluster runs on IBM Z hardware, managed separately. Both deployments use Confluent Kafka rather than open-source Apache Kafka.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure as Code.&lt;/strong&gt; Cluster provisioning uses Terraform and AWS CloudFormation. The job posting describes this as enabling “repeatable, auditable, and scalable cloud provisioning.” Ansible handles configuration management and automation across the streaming platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CI/CD and DevSecOps.&lt;/strong&gt; GitLab is the source control and pipeline platform. Engineers apply DevSecOps practices, integrating security controls into the CI/CD pipeline rather than treating them as a separate gate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SRE practices.&lt;/strong&gt; Barclays applies “BUK Service First and SRE principles” to its Kafka platform. Engineers are accountable for platform stability, performance tuning, capacity planning, and incident response. DORA metrics, covering Deployment Frequency, Lead Time for Change, Change Failure Rate, and Mean Time to Recovery, are the stated mechanism for tracking delivery and operational health.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing.&lt;/strong&gt; The engineering standard for Kafka services includes contract testing with PACT, unit testing with JUnit, integration and performance testing with JMeter, and mutation testing. Contract testing between producers and consumers is worth noting specifically: it catches schema or payload incompatibilities before deployment rather than at runtime, which reduces the risk of consumer failures caused by upstream producer changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and alerting.&lt;/strong&gt; The specific monitoring stack (metrics exporters, dashboards, alerting tools) is not named in any available source. The SRE accountability model implies alert-based on-call rotation, but the tooling is not described publicly.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Confluent Kafka&lt;/td&gt;
&lt;td&gt;Two separate deployments: AWS/EKS and IBM Z-Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise messaging&lt;/td&gt;
&lt;td&gt;Solace PubSub+&lt;/td&gt;
&lt;td&gt;Enterprise-wide high-speed messaging for front, middle, and back office; in place since 2009&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Schema evolution required; encoding format not specified in public sources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;Specific source and sink connectors not named publicly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Amazon EKS (Kubernetes)&lt;/td&gt;
&lt;td&gt;StatefulSets, persistent volumes, ingress controllers; used for AWS Kafka deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure as Code&lt;/td&gt;
&lt;td&gt;Terraform, AWS CloudFormation&lt;/td&gt;
&lt;td&gt;Repeatable, auditable cloud provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration management&lt;/td&gt;
&lt;td&gt;Ansible&lt;/td&gt;
&lt;td&gt;Automation across cloud and streaming platforms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;Source control and DevSecOps pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Containerisation&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;Local development environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflow orchestration&lt;/td&gt;
&lt;td&gt;Camunda Platform, Camunda 8&lt;/td&gt;
&lt;td&gt;Post-trade settlement processing and T+1 regulatory compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data platform&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Unified platform for trade analytics, warehousing, ML, and generative AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;PACT, JUnit, JMeter&lt;/td&gt;
&lt;td&gt;Contract, unit, and performance testing for Kafka services; mutation testing also applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mainframe platform&lt;/td&gt;
&lt;td&gt;IBM Z-Linux (zLinux)&lt;/td&gt;
&lt;td&gt;Host environment for IBM Z-resident Confluent Kafka deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shakir Ahmed&lt;/td&gt;
&lt;td&gt;Director of Operations Technology and Strategy&lt;/td&gt;
&lt;td&gt;Named in the Camunda case study discussing Barclays’ microservices-based post-trade settlement platform and T+1 compliance programme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Larisa Kvetnoy&lt;/td&gt;
&lt;td&gt;MD Post Trade Technology&lt;/td&gt;
&lt;td&gt;Named alongside Shakir Ahmed in the Camunda case study; responsible for Barclays’ post-trade technology estate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Running Kafka where your data lives reduces integration complexity.&lt;/strong&gt; Barclays’ decision to deploy Confluent Kafka on IBM Z-Linux rather than bridging the mainframe through an external connector keeps the streaming layer inside the same infrastructure boundary as the workloads it serves. If you have latency-sensitive applications on a mainframe or another non-commodity platform, consider whether bringing the broker closer is viable before adding a connector hop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-region active-active is a design commitment, not just a configuration choice.&lt;/strong&gt; Barclays’ AWS deployment explicitly covers active-active and active-passive patterns as separate engineering concerns. An active-active topology requires upfront decisions about consumer group management, replication, and data consistency that cannot be retrofitted easily. If you need this level of resilience, design for it from the start rather than treating it as a later scaling step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treat Kafka platform operations as an SRE discipline.&lt;/strong&gt; Barclays applies formal SRE practices to its Kafka clusters, including DORA metrics, capacity planning accountabilities, and incident management runbooks. Kafka is not a fire-and-forget system at scale; defining the same operational standards you apply to application services is a practical way to maintain reliability as the platform grows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contract testing between producers and consumers is worth the overhead.&lt;/strong&gt; Including PACT-based contract testing in the Kafka engineering standard catches schema incompatibilities before deployment, which matters more as the number of independent producer and consumer teams grows. The cost of running contract tests in CI is lower than the cost of debugging a consumer failure caused by an upstream schema change in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multiple messaging layers can coexist if their roles are distinct.&lt;/strong&gt; Barclays runs both Solace PubSub+ and Confluent Kafka as active parts of its messaging estate. That is not a transitional state; PubSub+ has been in place since 2009. If you are evaluating whether to consolidate onto a single messaging technology, Barclays’ architecture suggests that retaining a low-latency broker for specific front-office use cases alongside a general-purpose streaming platform may be a deliberate long-term choice rather than a migration that never completed.&lt;/p&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Apache Kafka Powered By — Barclays entry: &lt;a href=&quot;https://kafka.apache.org/powered-by&quot;&gt;https://kafka.apache.org/powered-by&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Barclays Careers — Software Engineer, Confluent Kafka Management (JR-0000103144): &lt;a href=&quot;https://search.jobs.barclays/job/bengaluru/software-engineer-confluent-kafka-management/13015/93793235776&quot;&gt;https://search.jobs.barclays/job/bengaluru/software-engineer-confluent-kafka-management/13015/93793235776&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Barclays Careers — Software Engineer, Confluent Kafka on IBM Z-Linux (JR-0000103136): &lt;a href=&quot;https://barclays.wd3.myworkdayjobs.com/en-US/External_Career_Site_Barclays/job/Software-Engineer---Confluent-Kafka-on-IBM-Z-Linux_JR-0000103136&quot;&gt;https://barclays.wd3.myworkdayjobs.com/en-US/External_Career_Site_Barclays/job/Software-Engineer---Confluent-Kafka-on-IBM-Z-Linux_JR-0000103136&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Solace customer case study — Barclays: &lt;a href=&quot;https://solace.com/company/customers/barclays/&quot;&gt;https://solace.com/company/customers/barclays/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Camunda case study — Barclays post-trade settlement: &lt;a href=&quot;https://camunda.com/case-study/barclays/&quot;&gt;https://camunda.com/case-study/barclays/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Databricks 2025 Data Intelligence Industry Awards: &lt;a href=&quot;https://www.databricks.com/blog/announcing-winners-2025-data-intelligence-industry-awards&quot;&gt;https://www.databricks.com/blog/announcing-winners-2025-data-intelligence-industry-awards&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Try Kpow for your Kafka clusters:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you manage Confluent Kafka clusters and want deeper visibility into consumer lag, topic throughput, and schema registry state, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; connects to any Kafka cluster in minutes. You can trial it free for 30 days and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Cash App uses Apache Kafka in production</title><link>https://factorhouse.io/articles/cash-app-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/cash-app-kafka-architecture/</guid><description>A deep-dive into Cash App&apos;s Kafka architecture — covering the evently-cloud bridge service, per-topic application-layer encryption, and the internal PubSub platform at Block.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Cash App runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; as the primary eventing backbone for its distributed services, operated by an internal PubSub platform team that also provides SQS for job queues and NATS for real-time client updates. Two aspects of Cash App’s Kafka story are worth studying in detail: the evently-cloud bridge service, which absorbs Kafka client initialisation overhead at a proxy layer and reduced its pod count from 100 to 15 without SLA regression; and a data-centric encryption model that assigns distinct AWS KMS-backed keys per Kafka topic rather than per service, operating at more than 8 TB of encrypted data per day across Kafka and gRPC transport.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Cash App is a consumer financial services product developed by Block Inc., offering peer-to-peer payments, banking, investing, and Bitcoin services to tens of millions of users primarily in the United States and the United Kingdom. Engineering at Cash App operates independently from Block’s other brands, with shared infrastructure provided by dedicated platform teams.&lt;/p&gt;
&lt;p&gt;Kafka’s role at Cash App is as the central asynchronous message bus. The PubSub platform team owns the Kafka cluster and provides it alongside complementary messaging primitives as a managed internal offering: Kafka for durable event streaming, SQS for job queues, and NATS for low-latency real-time client updates. The team also operates Kpow as the Kafka management UI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ongoing (pre-2022):&lt;/strong&gt; Cash App runs Kafka for asynchronous event pub/sub; the evently-cloud Kotlin bridge service serves legacy services that cannot manage Kafka client connections directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;December 2024:&lt;/strong&gt; Yoav Amit, Gelareh Taban, and Matthew Miller publish “Encryption using data-specific keys,” documenting the shift from service-centric to data-centric encryption across Kafka and gRPC transport. Cash App is encrypting more than 8 TB/day at this point.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-2022 (Block acquisition of Afterpay):&lt;/strong&gt; Cash App’s Delta Lake on Databricks adoption enables Spark Declarative Pipelines that read streaming data directly from Kafka for machine learning and data science workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cash-apps-kafka-use-cases&quot;&gt;Cash App’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;asynchronous-event-pubsub&quot;&gt;Asynchronous event pub/sub&lt;/h3&gt;
&lt;p&gt;The primary use of Kafka at Cash App is as the durable pub/sub backbone for distributed services. Engineering teams across Cash App produce and consume events through a managed platform offering rather than owning Kafka client connections themselves. This keeps operational overhead concentrated in the PubSub platform team while providing consistent eventing semantics to product teams.&lt;/p&gt;
&lt;h3 id=&quot;legacy-service-integration-via-evently-cloud&quot;&gt;Legacy service integration via evently-cloud&lt;/h3&gt;
&lt;p&gt;Not all Cash App services were built to manage long-lived Kafka client connections. The evently-cloud service fills this gap: it is a Kotlin service that exposes a REST API for fetching events from Kafka topics at specified offsets. Legacy services interact with Kafka through HTTP calls to evently-cloud, which handles client lifecycle, caching, and offset management internally. Alec Holmes documented this architecture on the Cash App Code Blog.&lt;/p&gt;
&lt;h3 id=&quot;machine-learning-and-data-science-pipelines&quot;&gt;Machine learning and data science pipelines&lt;/h3&gt;
&lt;p&gt;Cash App uses Databricks Spark Declarative Pipelines to read streaming data from Kafka, feeding machine learning and data science workloads. The data lake uses a medallion architecture — bronze, silver, and gold tiers — on Delta Lake, governed by Unity Catalog and AWS Glue. This allows ML teams to work with Kafka-sourced event data without managing stream processing infrastructure directly.&lt;/p&gt;
&lt;h3 id=&quot;encrypted-data-transport&quot;&gt;Encrypted data transport&lt;/h3&gt;
&lt;p&gt;Cash App treats Kafka as part of its data transport infrastructure alongside gRPC. Both are subject to the same application-layer encryption model: encryption keys are scoped per Kafka topic rather than per service, and any producer or consumer must hold the relevant topic key via AWS IAM. This model was documented by Yoav Amit, Gelareh Taban, and Matthew Miller in December 2024.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encryption volume:&lt;/strong&gt; More than 8 TB of data per day encrypted at the application layer across Kafka and gRPC transport (Yoav Amit, Gelareh Taban, Matthew Miller — December 2024).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;evently-cloud pod count:&lt;/strong&gt; Reduced from 100 pods to 15 pods after request-affinity optimisation, with no SLA regression (Alec Holmes).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total fleet size:&lt;/strong&gt; Topic count, partition count, and cluster count are not publicly disclosed by Cash App.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cash-apps-kafka-architecture&quot;&gt;Cash App’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Cash App’s Kafka cluster runs on AWS. The evently-cloud service and its surrounding infrastructure run on Kubernetes using Istio as the service mesh. Before Istio, each service used a dedicated Elastic Load Balancer; after migration, load balancing is handled client-side via Envoy sidecars.&lt;/p&gt;
&lt;h3 id=&quot;evently-cloud-kafka-bridge-service&quot;&gt;evently-cloud: Kafka bridge service&lt;/h3&gt;
&lt;p&gt;Kafka clients take several seconds to initialise and seek to specific topic offsets. In a service that creates a new client per request, this latency is incurred on every API call. evently-cloud avoids this by maintaining one long-lived cached Kafka client per topic consumer. When a request arrives, the service checks its cache; if a client already exists for that topic, the request is handled immediately without initialisation overhead.&lt;/p&gt;
&lt;p&gt;The caching strategy only works if requests for the same topic consistently reach the same pod. Cash App solved this with &lt;strong&gt;Istio-based request affinity&lt;/strong&gt;: Kafka topic identifiers are passed as HTTP headers, and Istio’s consistent-hashing load balancing routes requests for the same topic to the same pod. The affinity rule is topic-level, not session-level, which means the benefit applies across all consumers of a given topic regardless of which service made the request.&lt;/p&gt;
&lt;p&gt;After deploying request affinity, the evently-cloud deployment was reduced from 100 pods to 15 pods. Cache hit rates improved, and the SLA was unaffected.&lt;/p&gt;
&lt;h3 id=&quot;per-topic-encryption-at-application-layer&quot;&gt;Per-topic encryption at application layer&lt;/h3&gt;
&lt;p&gt;Cash App’s encryption model shifted from a service-centric design — where services held keys tied to their own identity — to a data-centric design, where encryption keys are associated with the data itself. For Kafka, this means a distinct Tink keyset per topic. Any workload that produces or consumes from a topic must have explicit access to that topic’s key via AWS IAM policies and role chaining.&lt;/p&gt;
&lt;p&gt;Keys are stored as Tink keysets in S3 buckets, backed by AWS KMS Customer Managed Keys. The practical effect is that a compromised service cannot decrypt data from topics outside its own access grants, even if it can reach the Kafka broker. The same model applies to gRPC transport, making it a consistent encryption boundary across Cash App’s data infrastructure. Block was encrypting more than 8 TB/day across both channels as of December 2024.&lt;/p&gt;
&lt;h3 id=&quot;producer-and-consumer-architecture&quot;&gt;Producer and consumer architecture&lt;/h3&gt;
&lt;p&gt;Producers and consumers in Cash App’s primary Kafka workloads use Kafka clients managed either directly by services with the capability to do so, or indirectly through evently-cloud for services that cannot. In the encrypted pipeline, Tink keysets are loaded by producers before serialisation and by consumers before deserialisation, with the key lookup resolved via AWS KMS at access time.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;topic-level-client-caching-with-istio-affinity&quot;&gt;Topic-level client caching with Istio affinity&lt;/h3&gt;
&lt;p&gt;The combination of Kafka client caching in evently-cloud and Istio consistent-hashing affinity at the load balancer layer is an effective pattern for bridging Kafka’s stateful client model with a stateless HTTP API. The Kafka client’s initialisation cost is paid once per topic per pod lifecycle, not once per request. The Istio affinity rule ensures the cached client is reused rather than abandoned when new requests arrive. The 85% pod reduction (100 to 15) is a concrete outcome of applying this pattern.&lt;/p&gt;
&lt;h3 id=&quot;data-centric-encryption-keys-scoped-to-kafka-topics&quot;&gt;Data-centric encryption keys scoped to Kafka topics&lt;/h3&gt;
&lt;p&gt;Most application-layer encryption in distributed systems is service-centric: a service holds a key and uses it for all its interactions. Cash App’s model is data-centric: the key belongs to the topic (or the gRPC endpoint), not the service. The implication for Kafka is that topic access control is enforced at the key level as well as at the broker ACL level. Rotating a key revokes access to historical data for any service that cannot access the new key. This is a meaningful security boundary in a financial services context where data sensitivity varies significantly by topic.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Cash App’s Kafka cluster runs self-managed on AWS. The PubSub platform team operates the cluster alongside SQS and NATS as a unified internal messaging platform. There is no public statement from Cash App about using a managed Kafka service such as Amazon MSK or Confluent Cloud for its primary cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka management UI:&lt;/strong&gt; The PubSub platform team operates &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; as the &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;Kafka management&lt;/a&gt; interface. This is the same team responsible for provisioning and maintaining the Kafka cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Encryption key management:&lt;/strong&gt; Tink keysets are stored in S3 and backed by AWS KMS Customer Managed Keys. AWS IAM policies govern which workloads can access which topic keys. The data safety levels framework, documented separately by Block’s security team, governs classification and retention policies for data that flows through Kafka.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; The evently-cloud service reduces the barrier to Kafka adoption for engineering teams that are not equipped to manage persistent Kafka client connections. Teams that can manage clients directly do so; those that cannot use the REST API. The PubSub platform team maintains both paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Service mesh:&lt;/strong&gt; Istio (Envoy sidecars) handles service-to-service communication for evently-cloud and the surrounding Kubernetes workloads. The migration from per-service ELBs to Istio client-side load balancing was what enabled the request-affinity optimisation that reduced evently-cloud’s pod footprint.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;kafka-client-initialisation-latency-in-evently-cloud&quot;&gt;Kafka client initialisation latency in evently-cloud&lt;/h3&gt;
&lt;p&gt;Kafka clients take seconds to initialise and seek to specific topic offsets. For a service that creates a new client per request, this overhead dominates response time. The first approach — running more pods so each one handled fewer requests — did not solve the root cause. The actual fix was to cache clients at the pod level and use Istio consistent-hashing to ensure requests for the same topic always route to the same pod. Once each pod’s client cache is warm, initialisation overhead disappears from the hot path. Pod count fell from 100 to 15 without SLA regression.&lt;/p&gt;
&lt;h3 id=&quot;service-centric-encryption-creating-implicit-cross-topic-access&quot;&gt;Service-centric encryption creating implicit cross-topic access&lt;/h3&gt;
&lt;p&gt;In a service-centric encryption model, a service that holds a key can decrypt any data encrypted with that key, regardless of which Kafka topic it came from. If a service’s key is compromised, or if the service is misconfigured to consume from a topic it was not intended to read, it can access data it should not. Cash App’s response was to move to per-topic keys under a data-centric model. Each Kafka topic has its own Tink keyset; access to that keyset is granted explicitly via AWS IAM. A service can only decrypt data from the topics it has been explicitly granted access to, regardless of what other topics it can reach at the network level.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;AWS-hosted; self-managed by the PubSub platform team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka bridge service&lt;/td&gt;
&lt;td&gt;evently-cloud (Kotlin)&lt;/td&gt;
&lt;td&gt;REST API bridge for legacy services; topic-level client caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asynchronous processing&lt;/td&gt;
&lt;td&gt;Amazon SQS, NATS&lt;/td&gt;
&lt;td&gt;Alongside Kafka; SQS for job queues, NATS for real-time client updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption framework&lt;/td&gt;
&lt;td&gt;Tink keysets&lt;/td&gt;
&lt;td&gt;Application-layer, per-topic encryption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key management&lt;/td&gt;
&lt;td&gt;AWS KMS (Customer Managed Keys)&lt;/td&gt;
&lt;td&gt;Topic-level CMKs; IAM role chaining for access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key storage&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;Encrypted Tink keysets stored per topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service mesh&lt;/td&gt;
&lt;td&gt;Istio (Envoy sidecars)&lt;/td&gt;
&lt;td&gt;Request affinity for evently-cloud on Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;AWS (Kubernetes, ELB)&lt;/td&gt;
&lt;td&gt;Kafka cluster and evently-cloud deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Databricks Spark Declarative Pipelines&lt;/td&gt;
&lt;td&gt;Read streaming data from Kafka for ML workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake storage&lt;/td&gt;
&lt;td&gt;Delta Lake on S3&lt;/td&gt;
&lt;td&gt;Medallion architecture (bronze, silver, gold)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute platform&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;ETL and ML workloads on Delta Lake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata / catalogue&lt;/td&gt;
&lt;td&gt;Unity Catalog, AWS Glue&lt;/td&gt;
&lt;td&gt;Data lake governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka management UI&lt;/td&gt;
&lt;td&gt;Kpow&lt;/td&gt;
&lt;td&gt;Operated by the Cash App PubSub platform team&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Alec Holmes&lt;/strong&gt; (with Ryan Hall and Jan Zantinge) — authored “&lt;a href=&quot;https://code.cash.app/request-affinity-with-istio&quot;&gt;Request Affinity with Istio&lt;/a&gt;,” covering the evently-cloud architecture and the optimisation that reduced pod count from 100 to 15.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Yoav Amit, Gelareh Taban, Matthew Miller&lt;/strong&gt; — authored “&lt;a href=&quot;https://code.cash.app/encryption-using-data-keys&quot;&gt;Encryption using data-specific keys&lt;/a&gt;” (December 2024), documenting the per-topic encryption model across Kafka and gRPC transport.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka client initialisation overhead can be absorbed at a bridge layer.&lt;/strong&gt; If you have services that need Kafka access but cannot manage persistent client connections, a proxy service with topic-level client caching and request affinity is worth considering. The operational win at Cash App was an 85% reduction in pod count for the same workload.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Istio consistent-hashing is a practical way to implement topic affinity without a dedicated load balancer per service.&lt;/strong&gt; Passing the Kafka topic name as an HTTP header and configuring a consistent-hash affinity rule gives you stateful routing in a stateless HTTP layer with minimal infrastructure overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-topic encryption keys are more granular than per-service keys, and the operational overhead is manageable at scale.&lt;/strong&gt; Cash App encrypts more than 8 TB/day under this model. The main requirement is an IAM structure that grants topic key access explicitly rather than implicitly through service identity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separating Kafka, job queues, and real-time push into distinct primitives simplifies service design.&lt;/strong&gt; Cash App’s PubSub platform offers Kafka for durable streaming, SQS for queue-based job processing, and NATS for sub-second client updates as separate tools with distinct semantics. This avoids the anti-pattern of using Kafka for workloads where its durability guarantees are unnecessary overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Alec Holmes, “Request Affinity with Istio” (Cash App Code Blog): &lt;a href=&quot;https://code.cash.app/request-affinity-with-istio&quot;&gt;https://code.cash.app/request-affinity-with-istio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yoav Amit, Gelareh Taban, Matthew Miller, “Encryption using data-specific keys” (December 2024): &lt;a href=&quot;https://code.cash.app/encryption-using-data-keys&quot;&gt;https://code.cash.app/encryption-using-data-keys&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are monitoring a Kafka cluster at any scale, you can try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Datadog uses Apache Kafka in production</title><link>https://factorhouse.io/articles/datadog-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/datadog-kafka-architecture/</guid><description>A deep-dive into Datadog&apos;s Kafka architecture — covering use cases, scale, engineering decisions, and key contributors across hundreds of clusters.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Datadog processes hundreds of trillions of observability events per day across logs, metrics, and traces, and &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; sits at the centre of that pipeline. The scale of the problem meant off-the-shelf Kafka tooling and clients eventually hit limits, and Datadog’s engineering teams have built a significant amount of infrastructure around Kafka to keep up: a custom Rust client, a control plane that routes traffic across hundreds of clusters without redeployment, and open-source SRE tooling that has been in production since 2018.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Datadog is a cloud-based observability and monitoring platform used by engineering and operations teams to monitor infrastructure, applications, and logs. Its core product ingests telemetry data from customer environments, runs it through real-time processing pipelines, and makes it queryable and alertable at low latency.&lt;/p&gt;
&lt;p&gt;The scale of that data ingestion is what drives most of Datadog’s Kafka decisions. As of February 2025, Datadog runs hundreds of Kafka clusters, thousands of topics, millions of partitions, and hundreds of consumer groups, handling terabytes of data per second.&lt;/p&gt;
&lt;p&gt;Kafka has been documented in Datadog’s engineering blog since at least 2018, when the SRE team open-sourced kafka-kit to handle the operational overhead of 40+ clusters processing trillions of datapoints per day. The architecture has evolved substantially since then, with the most recent major development being the Streaming Platform: a custom abstraction layer that decouples Kafka clients from physical cluster topology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;August 2018:&lt;/strong&gt; kafka-kit open-sourced, automating partition rebalancing, broker replacements, and replication throttle management across 40+ clusters&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;June 2019:&lt;/strong&gt; First detailed public documentation of Datadog’s Kafka operations, covering multi-region deployments and configuration practices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2019 (Kafka Summit London):&lt;/strong&gt; Balthazar Rouberol presents on running Kafka clusters in Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2022:&lt;/strong&gt; Husky event store introduced, with Kafka as its primary ingestion front-end&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2023:&lt;/strong&gt; Exactly-once ingestion semantics and shard routing in Husky documented publicly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2025:&lt;/strong&gt; Streaming Platform architecture published, revealing the full scope of Datadog’s Kafka infrastructure and the custom Rust client&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2025 (multiple posts):&lt;/strong&gt; CDC replication platform, configuration distribution, and live process metrics pipeline all documented, showing the breadth of Kafka’s role across teams&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;datadogs-kafka-use-cases&quot;&gt;Datadog’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;observability-event-ingestion&quot;&gt;Observability event ingestion&lt;/h3&gt;
&lt;p&gt;The primary role of Kafka at Datadog is as the durable buffer for the Data Platform. All incoming observability events – logs, spans, and metrics – pass through Kafka before reaching downstream storage and processing systems. Guillaume Bort, a Staff Software Engineer at Datadog, described this as the foundation of the entire observability pipeline in a &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/streaming-platform-kafka-custom-abstractions/&quot;&gt;February 2025 engineering blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;metrics-storage-ingestion-husky&quot;&gt;Metrics storage ingestion (Husky)&lt;/h3&gt;
&lt;p&gt;Datadog’s third-generation event store, Husky, uses Kafka as its ingestion layer. Writers in Husky read from Kafka, buffer events briefly in memory, then upload them to blob storage (AWS S3). A Shard Router service reads from one Kafka cluster and writes to a separate output cluster with data organised into logical shards – groups of partitions that map to physical storage units. This design allows Kafka scaling and storage scaling to proceed independently.&lt;/p&gt;
&lt;h3 id=&quot;exactly-once-ingestion-routing&quot;&gt;Exactly-once ingestion routing&lt;/h3&gt;
&lt;p&gt;Husky’s second generation introduced a locality concept embedded in the Kafka pipeline: a given event’s tenant ID and timestamp deterministically route it to the same Kafka shard on every ingestion attempt. Combined with FoundationDB for metadata transactions, this eliminates duplicates without requiring stateful writers. Daniel Intskirveli and Cecilia Watt documented this in a &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/husky-deep-dive/&quot;&gt;February 2023 post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cdc-based-data-replication&quot;&gt;CDC-based data replication&lt;/h3&gt;
&lt;p&gt;Kafka is also the backbone of Datadog’s internal change data capture (CDC) replication platform, which handles Postgres-to-Postgres replication, Postgres-to-Apache Iceberg pipelines for analytics, Cassandra replication, and cross-region Kafka replication. Sanketh Balakrishna and Andrew Zhang &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/cdc-replication-search/&quot;&gt;published the architecture&lt;/a&gt; in November 2025, documenting approximately 500 ms end-to-end replication lag for the initial use case.&lt;/p&gt;
&lt;h3 id=&quot;live-process-metrics-pipeline&quot;&gt;Live process metrics pipeline&lt;/h3&gt;
&lt;p&gt;The live process metrics feature uses Kafka for host subscription signalling. The live data server publishes to a host subscriptions topic at 1-second intervals; the intake service consumes from that topic and maintains an in-memory cache of which hosts are currently being viewed, activating high-frequency data collection only for those hosts. Kai Zong Khor and William Yu described this architecture in an &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-process-pipeline-efficiency/&quot;&gt;August 2025 post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;configuration-distribution&quot;&gt;Configuration distribution&lt;/h3&gt;
&lt;p&gt;Kafka carries cache-invalidation notifications when tenant configuration changes are written to the database, propagating those changes to downstream services. Gabriel Reid documented this use case in a &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-config-delivery-containers/&quot;&gt;June 2025 post&lt;/a&gt;, noting that a newer v2 design complements Kafka with cloud object storage for this role.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;As documented by Guillaume Bort in February 2025:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clusters:&lt;/strong&gt; Hundreds of Kafka clusters across Kubernetes and cloud environments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topics:&lt;/strong&gt; Thousands of topics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partitions:&lt;/strong&gt; Millions of partitions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer groups:&lt;/strong&gt; Hundreds of consumer groups&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daily event volume:&lt;/strong&gt; Hundreds of trillions of observability events per day&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput:&lt;/strong&gt; Terabytes of data per second&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For context, the open-source kafka-kit page describes “double-digit gigabytes per second” bandwidth requiring petabytes of high-performance storage even with short retention windows.&lt;/p&gt;
&lt;p&gt;In 2019, Emily Chang documented 40+ Kafka and ZooKeeper clusters processing trillions of datapoints daily across multiple data centres and regions, which gives a sense of how much the fleet has grown since then.&lt;/p&gt;
&lt;p&gt;On a smaller scale, one indicative example is the live process metrics pipeline: before an architecture change in 2025, the host subscriptions topic was handling 500,000 messages per second. After redesigning when and what data was collected, that fell to 5,000 messages per second – a 100x reduction – while freeing over 600 CPU cores and 1 TB of memory.&lt;/p&gt;
&lt;h2 id=&quot;datadogs-kafka-architecture&quot;&gt;Datadog’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;the-streaming-platform&quot;&gt;The Streaming Platform&lt;/h3&gt;
&lt;p&gt;The most significant architectural layer above raw Kafka is the Streaming Platform, introduced publicly in February 2025. Rather than having clients connect to physical Kafka clusters, the Streaming Platform provides three logical abstractions:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Streams&lt;/strong&gt; are multi-cluster logical topics. A Stream has a stable identifier that is independent of the physical cluster topology. Producers and consumers reference a Stream name; the control plane resolves that to one or more physical topics on one or more clusters. This decoupling means the underlying cluster can change without application redeployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stream Lanes&lt;/strong&gt; provide quality-of-service tiers within a Stream. A single Stream can have multiple lanes: a real-time lane for live traffic, a batch lane for lower-priority or late-arriving data, and a dead-letter queue lane for poison pills. When a malformed message blocks a consumer, the DLQ lane copies it out of the main partition, allowing the partition to make progress.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Assigner&lt;/strong&gt; is a custom multi-cluster coordinator that replaces Kafka’s default consumer group coordinator. It monitors cluster health and workload distribution across the entire fleet and can redirect traffic to a different cluster, decommission a cluster, or reallocate partitions – all without consumer redeployment.&lt;/p&gt;
&lt;h3 id=&quot;libstreaming-the-custom-kafka-client&quot;&gt;libstreaming: the custom Kafka client&lt;/h3&gt;
&lt;p&gt;All client interaction with the Streaming Platform goes through libstreaming, a unified Kafka client library written in Rust with bindings for Java, Go, and Python. It initially wrapped rdkafka, but rdkafka’s handling of compressed batches (see Challenges below) led Datadog to replace it with a fully custom Kafka client in Rust. libstreaming manages cluster discovery, failover, and the advanced commit log (described below).&lt;/p&gt;
&lt;h3 id=&quot;topic-organisation-kafka-kit-model&quot;&gt;Topic organisation (kafka-kit model)&lt;/h3&gt;
&lt;p&gt;Rather than running one or a few large clusters, Datadog treats topics as first-class capacity units. Topics are grouped into shared pools for smaller workloads or promoted to dedicated pools when they outgrow shared resources. The kafka-kit tooling automates the placement logic, using either even partition distribution (“count” strategy) or storage-based bin-packing (“storage” strategy).&lt;/p&gt;
&lt;h3 id=&quot;multi-region-redundancy&quot;&gt;Multi-region redundancy&lt;/h3&gt;
&lt;p&gt;For critical pipelines, Datadog uses duplicate writes to primary and secondary Kafka clusters. If an unclean leader election occurs on one cluster, the data is recoverable from the other. Emily Chang documented this pattern in 2019 as a response to data loss events caused by out-of-sync replicas being elected leader before Kafka 0.11.0 made exactly-once semantics more broadly available.&lt;/p&gt;
&lt;h3 id=&quot;advanced-commit-log&quot;&gt;Advanced commit log&lt;/h3&gt;
&lt;p&gt;The standard Kafka consumer offset model tracks a single pointer per partition, which means a consumer must choose between processing live traffic and reprocessing a backlog. Datadog’s Streaming Platform extends the commit metadata to track multiple offsets or offset ranges within a single partition simultaneously. This allows consumers to handle both live traffic and concurrent backlog reprocessing without head-of-line blocking.&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-deployment&quot;&gt;Kubernetes deployment&lt;/h3&gt;
&lt;p&gt;All Kafka clusters run self-managed on Kubernetes, using StatefulSets and persistent volumes. Martin Dickson, a Senior Software Engineer on the Datadog Kafka team, covered this in a March 2024 talk, noting that Datadog operates dozens of self-managed Kubernetes clusters across a multi-cloud environment.&lt;/p&gt;
&lt;h3 id=&quot;cdc-pipeline-architecture&quot;&gt;CDC pipeline architecture&lt;/h3&gt;
&lt;p&gt;The CDC replication platform uses Debezium as the change capture connector for Postgres and Cassandra sources. Debezium serialises captured changes into Avro format and publishes them to Kafka topics, along with schema updates, to a multi-tenant Kafka Schema Registry. Kafka Connect (with single message transforms for field manipulation, topic renaming, and column filtering) handles the fault-tolerant data movement between systems.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Producers interact with the Streaming Platform through libstreaming, which manages batching and routing transparently. The system uses Zstandard (zstd) compression across pipelines. At the scale Datadog operates, even compression can introduce problems – an incoming compressed batch may expand to gigabytes on decompression, which the custom client addresses by enforcing limits on decompressed batch size rather than compressed size.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer group management is handled by The Assigner rather than Kafka’s native group coordinator. Lag monitoring uses wall-clock time rather than pure message-count offsets: the advanced commit log enriches metadata with ingestion timestamps, and the Streaming Platform computes time lag by consuming &lt;code&gt;__consumer_offsets&lt;/code&gt; topics across all clusters. This allows automated rebalancing to trigger based on actual data age.&lt;/p&gt;
&lt;p&gt;For the live process metrics pipeline, each entry in the Kafka-backed cache includes a TTL, so hosts expire automatically when collection stops without requiring an explicit deletion event.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;live-traffic-failover-without-redeployment&quot;&gt;Live traffic failover without redeployment&lt;/h3&gt;
&lt;p&gt;The Streaming Platform’s Assigner can redirect all traffic for a Stream from one cluster to another in seconds. The control plane creates a new topic on the target cluster, redirects producers and consumers, then drains the old topic. The same mechanism handles proactive load redistribution and cluster decommissioning without any application-side changes.&lt;/p&gt;
&lt;h3 id=&quot;relaxed-ordering-for-parallelism&quot;&gt;Relaxed ordering for parallelism&lt;/h3&gt;
&lt;p&gt;Datadog moved away from strict per-partition ordering for the main observability pipeline. Rather than relying on Kafka partition order for correctness, events are processed with at-least-once delivery and ordering is deferred to the downstream Husky storage layer. This unlocks substantially greater parallelism at petabyte scale.&lt;/p&gt;
&lt;h3 id=&quot;deterministic-shard-routing-for-deduplication&quot;&gt;Deterministic shard routing for deduplication&lt;/h3&gt;
&lt;p&gt;Husky’s shard routing assigns events to Kafka shards based on tenant ID and timestamp. Because the mapping is deterministic, the same event will always land in the same shard regardless of how many times it is re-ingested. Combined with FoundationDB for transactional metadata, this provides exactly-once semantics without stateful writers or distributed locking.&lt;/p&gt;
&lt;h3 id=&quot;storage-based-partition-rebalancing&quot;&gt;Storage-based partition rebalancing&lt;/h3&gt;
&lt;p&gt;kafka-kit’s topicmappr tool supports two placement strategies: count-based (even partition distribution across brokers) and storage-based (bin-packing partitions according to their current storage metrics). The storage strategy integrates with Datadog’s own metrics API to pull live partition size data, and includes rack-aware replica placement. This reduces the manual effort of broker replacements and rebalances at a fleet of hundreds of clusters.&lt;/p&gt;
&lt;h3 id=&quot;ttl-based-subscription-expiry&quot;&gt;TTL-based subscription expiry&lt;/h3&gt;
&lt;p&gt;In the live process metrics pipeline, host subscription state in the Kafka-backed cache includes a TTL. This handles ungraceful terminations: if a service crashes without sending a deletion event, its subscription entries expire naturally, preventing the intake service from continuing to collect data for a host no one is viewing.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Entirely self-managed on Kubernetes, across dozens of Kubernetes clusters in a multi-cloud environment. Datadog does not use Confluent Cloud, MSK, or other managed Kafka services for its production clusters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; Datadog monitors its Kafka infrastructure with its own platform, tracking MBean metrics including &lt;code&gt;MessagesInPerSec&lt;/code&gt;, &lt;code&gt;BytesInPerSec&lt;/code&gt;, and ISR shrinks/expands. The kafka-kit autothrottle tool integrates directly with the Datadog metrics API to manage replication throttle rates dynamically during broker replacements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Segment configuration tuning:&lt;/strong&gt; For low-throughput topics, default Kafka segment settings can cause log retention to exceed the configured topic-level retention. Emily Chang documented reducing &lt;code&gt;segment.ms&lt;/code&gt; to 43,200,000 ms (12 hours) and &lt;code&gt;segment.bytes&lt;/code&gt; to approximately 100 MB to align segment lifecycle with topic-level retention. Open file handles are monitored to ensure frequent segment rollouts do not exhaust OS limits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Offset retention:&lt;/strong&gt; Consumer offset retention was extended from the default 1 day to 7 days, preventing data reprocessing on topics where consumer groups are temporarily offline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuration change testing:&lt;/strong&gt; Configuration changes are validated on replicated or mirrored clusters before being applied to production. ISR shrinks/expands and segment sizes are monitored continuously during rollouts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer tooling:&lt;/strong&gt; The kafka-kit suite (open-sourced August 2018, maintained through at least v4.2.1 in July 2023) handles topic mapping, partition rebalancing, broker replacements, and replication throttle management. It is available at &lt;a href=&quot;https://github.com/DataDog/kafka-kit&quot;&gt;github.com/DataDog/kafka-kit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema governance:&lt;/strong&gt; The CDC replication platform uses a multi-tenant Kafka Schema Registry with backward compatibility enforced. Schema changes are validated against the registry before application, preventing pipeline breakage from schema drift.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;static-cluster-bindings-preventing-rapid-failover&quot;&gt;Static cluster bindings preventing rapid failover&lt;/h3&gt;
&lt;p&gt;Applications were bound to physical Kafka cluster addresses at deploy time. Redirecting traffic to a different cluster required a full redeployment cycle. Datadog built the Streaming Platform abstraction so that producers and consumers reference logical Stream names, not cluster addresses. The Assigner resolves those names to physical clusters and can redirect traffic in seconds. The result is zero-downtime cluster failover and live traffic migration without touching application configuration.&lt;/p&gt;
&lt;h3 id=&quot;single-pointer-offset-model-blocking-concurrent-backlog-processing&quot;&gt;Single-pointer offset model blocking concurrent backlog processing&lt;/h3&gt;
&lt;p&gt;Kafka’s standard offset model gives each consumer group a single pointer per partition. Reprocessing a backlog means abandoning live traffic on that partition, or running a separate consumer group with separate infrastructure. Datadog built a custom commit log that stores multiple offsets or offset ranges in a partition’s commit metadata simultaneously. Consumers can process live traffic and catch up on a backlog at the same time, on the same partition.&lt;/p&gt;
&lt;h3 id=&quot;zstd-compression-bombs-crashing-consumers&quot;&gt;zstd compression bombs crashing consumers&lt;/h3&gt;
&lt;p&gt;The rdkafka client – which Datadog initially used as the foundation of libstreaming – limits batch sizes by compressed size. A message batch that is small when compressed can expand to gigabytes on decompression if the payload is highly repetitive. At Datadog’s ingestion rate, compressed batches large enough to crash consumer processes were not a theoretical problem. Datadog replaced rdkafka with a custom Rust Kafka client that enforces decompressed size limits. Consumer crashes from oversized decompressed batches were eliminated.&lt;/p&gt;
&lt;h3 id=&quot;offset-based-lag-insufficient-for-operational-decisions&quot;&gt;Offset-based lag insufficient for operational decisions&lt;/h3&gt;
&lt;p&gt;Standard Kafka consumer lag is expressed in message count: how many messages behind the latest offset the consumer group sits. That figure provides no information about how old the unprocessed data is. A consumer group that is 10 million messages behind on a high-throughput topic may be seconds behind in wall-clock time; on a low-throughput topic, those same 10 million messages might represent days of data. Datadog enriched commit metadata with ingestion timestamps and built wall-clock time lag computation by consuming &lt;code&gt;__consumer_offsets&lt;/code&gt; topics across all clusters. Automated rebalancing now triggers on actual data age rather than message count.&lt;/p&gt;
&lt;h3 id=&quot;unclean-leader-elections-causing-data-loss&quot;&gt;Unclean leader elections causing data loss&lt;/h3&gt;
&lt;p&gt;In multi-region deployments running pre-0.11.0 Kafka (which enabled unclean leader elections by default), an out-of-sync replica could be elected leader when the in-sync leader became unavailable, discarding the unreplicated messages. Datadog’s response was duplicate writes: data written to both a primary and a secondary Kafka cluster. A data loss event on one cluster left the data intact on the other. The combination of Kafka 0.11.0’s changes to leader election defaults and duplicate-write redundancy eliminated permanent data loss from this failure mode.&lt;/p&gt;
&lt;h3 id=&quot;log-segment-retention-exceeding-topic-level-retention-on-low-throughput-topics&quot;&gt;Log segment retention exceeding topic-level retention on low-throughput topics&lt;/h3&gt;
&lt;p&gt;Kafka’s default &lt;code&gt;segment.ms&lt;/code&gt; value is 7 days. On topics with a 36-hour retention setting and low message volume, segments stayed open for the full 7 days before rolling, meaning data was retained for longer than the configured policy. Reducing &lt;code&gt;segment.ms&lt;/code&gt; to 12 hours and &lt;code&gt;segment.bytes&lt;/code&gt; to approximately 100 MB brought segment lifecycle in line with topic-level retention.&lt;/p&gt;
&lt;h3 id=&quot;head-of-line-blocking-from-poison-pills-and-traffic-spikes&quot;&gt;Head-of-line blocking from poison pills and traffic spikes&lt;/h3&gt;
&lt;p&gt;A malformed message in a partition blocks all consumers on that partition until it is handled, and a traffic spike from one high-volume workload can starve other workloads sharing the same partitions. Stream Lanes within the Streaming Platform address both problems. Real-time, batch, and dead-letter workloads run on separate lanes within the same logical Stream. When the system detects a poison pill, it copies the message to the DLQ lane, allowing the main lane to advance. Different workload types can no longer block each other at the partition level.&lt;/p&gt;
&lt;h3 id=&quot;live-process-pipeline-consuming-500000-messagessecond-for-inactive-data&quot;&gt;Live process pipeline consuming 500,000 messages/second for inactive data&lt;/h3&gt;
&lt;p&gt;The intake service was receiving all process metrics for all hosts, even those that no user was actively viewing. The Kafka-consuming service maintained in-memory state for the entire host fleet. Kai Zong Khor and William Yu redesigned the pipeline so that the live data server only activates 2-second collection intervals for hosts currently being viewed, and signals this state to the intake service via Kafka at 1-second intervals. Peak throughput on the host subscriptions topic dropped from 500,000 to 5,000 messages per second, and the supporting infrastructure was scaled down by 98%.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Self-managed; specific version not disclosed publicly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client&lt;/td&gt;
&lt;td&gt;libstreaming (custom, Rust)&lt;/td&gt;
&lt;td&gt;Replaces rdkafka; bindings for Java, Go, Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client (legacy/external)&lt;/td&gt;
&lt;td&gt;rdkafka / librdkafka&lt;/td&gt;
&lt;td&gt;Still used in some services and Observability Pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client (Go)&lt;/td&gt;
&lt;td&gt;DataDog/confluent-kafka-go&lt;/td&gt;
&lt;td&gt;Datadog fork of the Confluent Go client, wrapping librdkafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing control plane&lt;/td&gt;
&lt;td&gt;The Assigner (custom)&lt;/td&gt;
&lt;td&gt;Replaces Kafka’s native group coordinator for the Streaming Platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Kafka Schema Registry (multi-tenant)&lt;/td&gt;
&lt;td&gt;Configured for backward compatibility; used in CDC pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation format&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Used in CDC event pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change data capture&lt;/td&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;Source connector for Postgres and Cassandra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data movement&lt;/td&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;With single message transforms for CDC pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata store&lt;/td&gt;
&lt;td&gt;FoundationDB&lt;/td&gt;
&lt;td&gt;Transactional metadata for exactly-once ingestion in Husky&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blob storage&lt;/td&gt;
&lt;td&gt;AWS S3&lt;/td&gt;
&lt;td&gt;Target for Husky event store Writers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics target&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;Target for Postgres-to-analytics CDC pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;Zstandard (zstd)&lt;/td&gt;
&lt;td&gt;Compression algorithm for Kafka messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka operations tooling&lt;/td&gt;
&lt;td&gt;kafka-kit (open source, Go)&lt;/td&gt;
&lt;td&gt;Covers topicmappr, autothrottle, registry, metricsfetcher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes (StatefulSets)&lt;/td&gt;
&lt;td&gt;Self-managed, multi-cloud, dozens of clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;Kafka clusters monitored with Datadog’s own platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Guillaume Bort&lt;/strong&gt; (Staff Software Engineer, Datadog): Led the Streaming Platform, libstreaming, and the custom Rust Kafka client. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/streaming-platform-kafka-custom-abstractions/&quot;&gt;February 2025 engineering post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jamie Alquiza&lt;/strong&gt; (Staff Software Engineer, Datadog): Creator of kafka-kit; co-lead of early Kafka infrastructure scaling. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/introducing-kafka-kit-tools-for-scaling-kafka/&quot;&gt;kafka-kit introduction post&lt;/a&gt;, &lt;a href=&quot;https://github.com/DataDog/kafka-kit&quot;&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balthazar Rouberol&lt;/strong&gt; (Data Reliability Engineer Team Lead, Datadog): Co-lead of Kafka and Cassandra infrastructure; presented “Running Production Kafka Clusters in Kubernetes” at &lt;a href=&quot;https://videos.confluent.io/watch/xKrnWUeo3tLBWZRbAL2aBY&quot;&gt;Kafka Summit London 2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emily Chang&lt;/strong&gt;: Authored the 2019 “Lessons learned from running Kafka at Datadog” post. &lt;a href=&quot;https://www.datadoghq.com/blog/kafka-at-datadog/&quot;&gt;Engineering blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Richard Artoul and Cecilia Watt&lt;/strong&gt;: Introduced the Husky event store and Kafka’s role as its ingestion layer. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/introducing-husky/&quot;&gt;May 2022 post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daniel Intskirveli and Cecilia Watt&lt;/strong&gt;: Documented Husky’s exactly-once ingestion and shard routing. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/husky-deep-dive/&quot;&gt;February 2023 post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sanketh Balakrishna and Andrew Zhang&lt;/strong&gt;: Designed and published the CDC replication platform. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/cdc-replication-search/&quot;&gt;November 2025 post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kai Zong Khor and William Yu&lt;/strong&gt;: Redesigned the live process metrics pipeline, achieving a 100x throughput reduction. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-process-pipeline-efficiency/&quot;&gt;August 2025 post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gabriel Reid&lt;/strong&gt;: Documented configuration distribution via Kafka. &lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-config-delivery-containers/&quot;&gt;June 2025 post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Martin Dickson&lt;/strong&gt; (Senior Software Engineer, Kafka team): Covered self-managed Kafka on Kubernetes in a 2024 summit talk. &lt;a href=&quot;https://datadogon.datadoghq.com/episodes/datadog-on-stateful-workloads-on-kubernetes/&quot;&gt;Episode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decouple clients from physical topology early.&lt;/strong&gt; Binding producers and consumers directly to cluster addresses makes failover expensive. Datadog built an abstraction layer so applications reference logical stream names. If you are running multiple clusters, even a lightweight routing layer pays off as the fleet grows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message-count lag is an incomplete signal.&lt;/strong&gt; Whether 10 million lagging messages represents a problem depends entirely on how old they are. Enriching commit metadata with ingestion timestamps and computing wall-clock lag gives operations teams a signal they can actually act on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-side compressed batch size limits are by compressed size, not decompressed size.&lt;/strong&gt; At high throughput with zstd compression, this can cause decompressed batches to overwhelm consumer memory. If you are running high-compression pipelines at scale, verify how your client handles this and whether you need a custom or patched client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Segment configuration affects retention on low-throughput topics.&lt;/strong&gt; The default &lt;code&gt;segment.ms&lt;/code&gt; of 7 days can cause data to be retained well beyond the topic-level retention setting on topics that receive infrequent writes. Tuning &lt;code&gt;segment.ms&lt;/code&gt; and &lt;code&gt;segment.bytes&lt;/code&gt; for low-throughput topics brings actual retention in line with policy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;QoS tiers within a topic reduce blast radius.&lt;/strong&gt; Rather than isolating workloads entirely into separate topics or clusters, Datadog’s Stream Lanes provide lightweight isolation within a single logical stream. Separating real-time from batch traffic and routing poison pills to a dead-letter lane reduces the operational surface area without requiring full topic duplication.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Guillaume Bort, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/streaming-platform-kafka-custom-abstractions/&quot;&gt;Achieving relentless Kafka reliability at scale with the Streaming Platform&lt;/a&gt;” (February 2025)&lt;/li&gt;
&lt;li&gt;Emily Chang, “&lt;a href=&quot;https://www.datadoghq.com/blog/kafka-at-datadog/&quot;&gt;Lessons learned from running Kafka at Datadog&lt;/a&gt;” (June 2019)&lt;/li&gt;
&lt;li&gt;Jamie Alquiza, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/introducing-kafka-kit-tools-for-scaling-kafka/&quot;&gt;Introducing Kafka-Kit: Tools for scaling Kafka&lt;/a&gt;” (August 2018)&lt;/li&gt;
&lt;li&gt;Richard Artoul and Cecilia Watt, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/introducing-husky/&quot;&gt;Introducing Husky, Datadog’s third-generation event store&lt;/a&gt;” (May 2022)&lt;/li&gt;
&lt;li&gt;Daniel Intskirveli and Cecilia Watt, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/husky-deep-dive/&quot;&gt;Husky: Exactly-once ingestion and multi-tenancy at scale&lt;/a&gt;” (February 2023)&lt;/li&gt;
&lt;li&gt;Sanketh Balakrishna and Andrew Zhang, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/cdc-replication-search/&quot;&gt;Replication redefined: How we built a low-latency, multi-tenant data replication platform&lt;/a&gt;” (November 2025)&lt;/li&gt;
&lt;li&gt;Kai Zong Khor and William Yu, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-process-pipeline-efficiency/&quot;&gt;Scaling down to speed up: How we improved efficiency of live process metrics by 100x&lt;/a&gt;” (August 2025)&lt;/li&gt;
&lt;li&gt;Gabriel Reid, “&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/scaling-config-delivery-containers/&quot;&gt;How we scaled fast, reliable configuration distribution to thousands of workload containers&lt;/a&gt;” (June 2025)&lt;/li&gt;
&lt;li&gt;Balthazar Rouberol, “&lt;a href=&quot;https://videos.confluent.io/watch/xKrnWUeo3tLBWZRbAL2aBY&quot;&gt;Running Production Kafka Clusters in Kubernetes&lt;/a&gt;” (Kafka Summit London 2019)&lt;/li&gt;
&lt;li&gt;Jamie Alquiza and Balthazar Rouberol, “&lt;a href=&quot;https://datadogon.datadoghq.com/episodes/datadog-on-kafka/&quot;&gt;Datadog on Kafka&lt;/a&gt;” podcast (May 2020)&lt;/li&gt;
&lt;li&gt;Martin Dickson, “&lt;a href=&quot;https://datadogon.datadoghq.com/episodes/datadog-on-stateful-workloads-on-kubernetes/&quot;&gt;Datadog on Stateful Workloads on Kubernetes&lt;/a&gt;” (March 2024)&lt;/li&gt;
&lt;li&gt;kafka-kit &lt;a href=&quot;https://github.com/DataDog/kafka-kit&quot;&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are monitoring a Kafka cluster at any scale, you can try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>Apache Kafka architecture: a complete guide to internals, components, and deployment</title><link>https://factorhouse.io/articles/kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-architecture/</guid><description>A complete guide to Apache Kafka architecture: internals, components, KRaft, replication, consumers, Connect, Streams, and deployment options.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;how-kafka-works-a-mental-model&quot;&gt;How Kafka works: a mental model&lt;/h2&gt;
&lt;p&gt;Kafka is a distributed commit log. That framing matters: it is not a message queue, not a database, and not a pub-sub system, though it borrows ideas from all three. Understanding how it works starts with understanding what the log is and why that design choice dictates everything else.&lt;/p&gt;
&lt;p&gt;At its core, Kafka stores records in topics. Each topic is divided into one or more partitions, and each partition is an ordered, immutable sequence of records written to disk. Producers append records to the end of a partition; consumers read from any position in the log by specifying an offset. Nothing is deleted when a consumer reads a record - the record stays in the log until a retention policy removes it. Multiple consumers reading the same partition at the same time each read independently, at their own position.&lt;/p&gt;
&lt;p&gt;This design gives Kafka two properties that are difficult to achieve together in traditional message brokers: high throughput and replay. Because writes are sequential disk appends, they are fast. Because data persists, any consumer can re-read historical data, replay a stream from a specific point, or bootstrap a new consumer group without disturbing others.&lt;/p&gt;
&lt;p&gt;Kafka separates producers from consumers entirely. A producer writes records to Kafka; it has no knowledge of which consumers exist or where they are reading. A consumer reads from Kafka; it has no interaction with producers. The log in between is the only coupling point. This decoupling is why Kafka scales: adding a new consumer does not affect producers or existing consumers, and a producer can continue writing whether consumers are online or not.&lt;/p&gt;
&lt;p&gt;The cluster is a set of brokers. Each broker stores a subset of partitions. Some partitions are replicated across multiple brokers for durability. The cluster is managed by a controller, which tracks partition leadership and handles broker failures. As of Kafka 4.0, that controller runs on an internal consensus protocol called KRaft, with no external coordination dependency.&lt;/p&gt;
&lt;h2 id=&quot;the-kafka-log&quot;&gt;The Kafka log&lt;/h2&gt;
&lt;p&gt;A Kafka partition is a log. Physically, it is a directory on the broker’s filesystem. Inside that directory are one or more log segments, each of which is a pair of files: a &lt;code&gt;.log&lt;/code&gt; file containing the raw record data, and an &lt;code&gt;.index&lt;/code&gt; file that maps record offsets to byte positions in the &lt;code&gt;.log&lt;/code&gt; file. A third file, the &lt;code&gt;.timeindex&lt;/code&gt;, maps timestamps to offsets, enabling time-based lookups.&lt;/p&gt;
&lt;p&gt;Segments are rolled when they exceed a size threshold (controlled by &lt;code&gt;log.segment.bytes&lt;/code&gt;, defaulting to 1 GB) or when they age past a configured interval (&lt;code&gt;log.roll.ms&lt;/code&gt;). Only the most recent segment in a partition is active and writable; older segments are sealed and can be cleaned or deleted by background threads.&lt;/p&gt;
&lt;p&gt;Kafka uses two separate retention mechanisms:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Delete retention:&lt;/strong&gt; Records older than a configured age (&lt;code&gt;log.retention.ms&lt;/code&gt;) or beyond a size cap (&lt;code&gt;log.retention.bytes&lt;/code&gt;) are deleted. The broker removes entire segments - it does not delete individual records from within a segment. This is the default for most topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compacted retention:&lt;/strong&gt; For topics with &lt;code&gt;cleanup.policy=compact&lt;/code&gt;, Kafka retains only the most recent record for each key. A background thread called the log cleaner compares dirty segments (containing duplicate keys) against clean segments and writes a compacted output. A null-valued record (a tombstone) marks a key for deletion. The cleaner thread is governed by &lt;code&gt;log.cleaner.min.dirty.ratio&lt;/code&gt;, which sets the minimum fraction of dirty log that must exist before compaction runs. Compacted topics are appropriate when you care about the latest state of a key rather than the full history: a user profile topic, for example, or a Kafka Streams changelog topic.&lt;/p&gt;
&lt;p&gt;You can apply both policies to the same topic using &lt;code&gt;cleanup.policy=compact,delete&lt;/code&gt;. Compaction removes redundant keys; deletion caps total retention by time or size.&lt;/p&gt;
&lt;p&gt;The log is also the unit of replication. Followers replicate the partition leader’s log by issuing fetch requests. The high watermark marks the point up to which all in-sync replicas have confirmed they have written. Consumers only read up to the high watermark, so they never see records that might be lost if a leader failure occurred before full replication.&lt;/p&gt;
&lt;h2 id=&quot;core-components-of-kafka&quot;&gt;Core components of Kafka&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;Stores and serves partitions; handles producer and consumer connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic&lt;/td&gt;
&lt;td&gt;Logical category for records; divided into partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;Ordered, append-only log; unit of parallelism and replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Writes records to topics; controls partitioning and batching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;td&gt;Reads records from partitions; tracks position via offsets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer group&lt;/td&gt;
&lt;td&gt;Set of consumers that share the work of reading a topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KRaft controller&lt;/td&gt;
&lt;td&gt;Manages cluster metadata and leader election (replaces ZooKeeper)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema Registry&lt;/td&gt;
&lt;td&gt;Enforces schema contracts for record serialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;Integrates external systems via source and sink connectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;Client library for stateful stream processing inside the cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ksqlDB&lt;/td&gt;
&lt;td&gt;SQL interface for stream processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka UI&lt;/td&gt;
&lt;td&gt;Operational visibility layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These components divide into three layers: the core data plane (brokers, topics, partitions, producers, consumers), the control plane (KRaft controller), and the ecosystem (Schema Registry, Connect, Streams, ksqlDB, and operational tooling). The diagram below shows how they relate.&lt;/p&gt;
&lt;h2 id=&quot;topics-and-partitions&quot;&gt;Topics and partitions&lt;/h2&gt;
&lt;p&gt;A topic is a named stream of records. It is the unit of logical organization in Kafka: producers target a topic, consumers subscribe to one or more topics. Physically, a topic is implemented as one or more partitions spread across brokers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition count and parallelism:&lt;/strong&gt; The number of partitions in a topic determines the maximum read parallelism within a consumer group. A partition can only be assigned to one consumer in a group at a time, so a consumer group with more consumers than partitions will have idle consumers. Partition count is a durable decision: you can increase it after creation, but you cannot decrease it, and increasing it can break ordering guarantees for keyed records (because keys are hashed to partitions, and adding partitions changes the mapping).&lt;/p&gt;
&lt;p&gt;A reasonable starting point is one partition per expected peak parallel consumer, with headroom for growth. For high-throughput topics, also account for per-partition memory overhead on brokers and per-partition file descriptor usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition assignment:&lt;/strong&gt; When a producer sends a record without a key, Kafka assigns it to partitions using a sticky batching strategy: the producer fills a batch for one partition before moving to the next, which improves compression and throughput. When a record has a key, Kafka hashes the key to a partition using the &lt;code&gt;DefaultPartitioner&lt;/code&gt; (murmur2 hash). Records with the same key always go to the same partition, preserving per-key ordering. You can supply a custom &lt;code&gt;Partitioner&lt;/code&gt; implementation if you need different routing logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compacted versus delete topics:&lt;/strong&gt; Topics use either delete or compact cleanup policy, or both. Delete topics are appropriate for event streams where age determines relevance: logs, metrics, activity events. Compacted topics are appropriate for changelog semantics where the latest value per key is what matters: materialized state, lookup tables, Kafka Streams state stores.&lt;/p&gt;
&lt;h2 id=&quot;producers&quot;&gt;Producers&lt;/h2&gt;
&lt;p&gt;The producer lifecycle for a single record moves through four stages: serialization, partitioning, batching, and network send.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serialization and partitioning:&lt;/strong&gt; The producer serializes the key and value using configured serializers (byte array, string, Avro, Protobuf, and so on) before anything else. After serialization, the partitioner assigns the record to a partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Batching:&lt;/strong&gt; Records destined for the same partition and broker accumulate in a &lt;code&gt;RecordBatch&lt;/code&gt; in the producer’s send buffer. Two configurations govern batch behavior:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;batch.size&lt;/code&gt;: the maximum size in bytes of a single batch. The producer sends when the batch is full or when &lt;code&gt;linger.ms&lt;/code&gt; elapses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;linger.ms&lt;/code&gt;: the maximum time the producer waits to fill a batch before sending. At &lt;code&gt;linger.ms=0&lt;/code&gt;, the producer sends immediately. Increasing this trades latency for throughput by allowing larger batches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Compression:&lt;/strong&gt; Setting &lt;code&gt;compression.type&lt;/code&gt; (gzip, snappy, lz4, zstd) compresses batches before sending. Compression reduces network I/O and broker storage at the cost of CPU. LZ4 and Zstandard offer the best throughput-to-compression-ratio tradeoff for most workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Delivery guarantees and acks:&lt;/strong&gt; The &lt;code&gt;acks&lt;/code&gt; configuration controls when the leader considers a write complete:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;acks=0&lt;/code&gt;: no acknowledgment; fire and forget.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acks=1&lt;/code&gt;: the leader acknowledges after writing to its local log, before followers replicate. A leader failure before replication means data loss.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acks=all&lt;/code&gt; (or &lt;code&gt;-1&lt;/code&gt;): the leader waits for all in-sync replicas to acknowledge. Since Kafka 3.0 this is the default. Combined with &lt;code&gt;min.insync.replicas=2&lt;/code&gt; and &lt;code&gt;replication.factor=3&lt;/code&gt;, this provides strong durability guarantees.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Idempotent producers and exactly-once semantics:&lt;/strong&gt; Setting &lt;code&gt;enable.idempotence=true&lt;/code&gt; assigns the producer a persistent producer ID and sequences each record batch. The broker deduplicates retried batches using the producer ID and sequence number, preventing duplicate records during transient network failures. Idempotent producers require &lt;code&gt;acks=all&lt;/code&gt; and &lt;code&gt;retries &amp;gt; 0&lt;/code&gt;; Kafka enforces these automatically when idempotence is enabled.&lt;/p&gt;
&lt;p&gt;For end-to-end exactly-once semantics across multiple topics - including writes paired with consumer offset commits - use the transactional API: call &lt;code&gt;initTransactions()&lt;/code&gt;, begin a transaction, produce and commit consumer offsets within the transaction, then commit or abort.&lt;/p&gt;
&lt;p&gt;A common misconception is that exactly-once requires &lt;code&gt;max.in.flight.requests.per.connection=1&lt;/code&gt;. This was true for early versions of idempotent producers. Since Kafka 0.11, idempotent producers safely handle up to five in-flight requests per partition while maintaining ordering and deduplication.&lt;/p&gt;
&lt;h2 id=&quot;brokers-and-the-cluster&quot;&gt;Brokers and the cluster&lt;/h2&gt;
&lt;p&gt;A Kafka cluster is a set of broker processes, each running on a separate machine or VM. Each broker is responsible for the partitions it hosts: writing records to local disk as producers send them, serving fetch requests from followers and consumers, and enforcing producer quotas, consumer quotas, and topic-level access controls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition leadership:&lt;/strong&gt; Every partition has a leader and zero or more followers. The leader handles all produce and fetch requests for that partition. Followers issue FetchRequests to the leader, replicating the log independently. When a broker fails, the controller elects a new leader from the in-sync replicas of each partition that broker led.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Controller responsibilities:&lt;/strong&gt; In a KRaft cluster, a subset of nodes forms the controller quorum. The active controller is the single node responsible for metadata decisions: partition leader elections, ISR updates, broker registrations, topic creation, and ACL changes. The other nodes in the quorum maintain up-to-date replicas of the metadata log and can assume the active role in milliseconds if the active controller fails. The controller does not handle data reads or writes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rack awareness:&lt;/strong&gt; Setting &lt;code&gt;broker.rack&lt;/code&gt; on each broker causes the controller to spread partition replicas across distinct failure domains. In a three-AZ deployment with replication factor 3, each AZ gets one replica. A single AZ failure does not take the partition offline.&lt;/p&gt;
&lt;h2 id=&quot;kraft-kafka-without-zookeeper&quot;&gt;KRaft: Kafka without ZooKeeper&lt;/h2&gt;
&lt;p&gt;KRaft (Kafka Raft) is the internal consensus protocol that manages Kafka cluster metadata. It replaced Apache ZooKeeper as the coordination layer, removing a major operational dependency and resolving fundamental scaling limits in the original architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why ZooKeeper was removed:&lt;/strong&gt; The ZooKeeper-based architecture stored cluster metadata externally. Whenever partition state changed, the active controller wrote to ZooKeeper, waited for ZooKeeper consensus, received a watch notification, updated its local memory, and then pushed update RPCs to every affected broker. This push-based propagation created two compounding problems. First, it imposed a hard ceiling on cluster scale: as partition counts grew past roughly 200,000, ZooKeeper’s metadata handling degraded, with coordination overhead growing substantially at scale. Second, controller failover was expensive: a newly elected controller had to pull and reconstruct its entire metadata cache from ZooKeeper before it could make any decisions, causing prolonged unavailability during failover on large clusters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What KRaft does instead:&lt;/strong&gt; KRaft treats cluster metadata as an append-only log stored in an internal topic called &lt;code&gt;__cluster_metadata&lt;/code&gt;. The active KRaft controller is the leader of this log. Other controllers in the quorum act as followers, maintaining hot in-memory copies of the metadata. When a controller fails, a standby takes over in milliseconds because it already has the full metadata state in memory - no rebuild from an external store.&lt;/p&gt;
&lt;p&gt;Rather than pushing metadata updates to brokers, KRaft uses a pull model. Brokers issue incremental FetchRequests to the active controller, pulling only the changes they have not yet seen. Because metadata changes are consumed from a single ordered log, every broker processes events in the same sequence, ensuring eventual consistency and preventing the metadata divergence that was possible with the legacy push model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The controller quorum:&lt;/strong&gt; The KRaft quorum uses a modified Raft consensus protocol (defined in KIP-595). To tolerate &lt;code&gt;f&lt;/code&gt; controller failures, you need &lt;code&gt;2f+1&lt;/code&gt; voters: a 3-node quorum tolerates 1 failure; a 5-node quorum tolerates 2. For production, run an odd number of dedicated controller nodes - not combined broker-controller nodes - to insulate the consensus layer from broker-side I/O pressure and garbage collection pauses. Co-locating controllers with data brokers exposes the control plane to the same resource contention it is trying to isolate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker state machine:&lt;/strong&gt; Each broker in a KRaft cluster transitions through four states:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Offline&lt;/td&gt;
&lt;td&gt;The broker process is stopped or is completing initial startup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced&lt;/td&gt;
&lt;td&gt;The broker is running but excluded from client RPCs - entered during startup or when it loses contact with the controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online&lt;/td&gt;
&lt;td&gt;The broker is fully operational, registered with the active controller, and authorized to serve traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stopping&lt;/td&gt;
&lt;td&gt;The broker is in graceful shutdown; the controller migrates its partition leadership before taking it offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If a broker loses contact with the controller and cannot send heartbeats, the controller fences it in the metadata log. Other brokers, consuming this event, redirect client traffic away from the fenced broker immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Snapshotting (KIP-630):&lt;/strong&gt; Because &lt;code&gt;__cluster_metadata&lt;/code&gt; is an append-only log, it would grow indefinitely without compaction. KRaft uses a snapshotting mechanism: when enough records have accumulated since the last snapshot, the active controller serializes its in-memory metadata state to a checkpoint file. Followers download snapshots chunk-by-chunk via FetchSnapshot RPCs, validate the CRC, and load the new state. A newly joined or lagging broker can bootstrap quickly by fetching the latest snapshot and replaying only the log entries after the snapshot’s end offset.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dynamic quorum membership (KIP-853):&lt;/strong&gt; Previously, the controller quorum was statically configured via &lt;code&gt;controller.quorum.voters&lt;/code&gt;. KIP-853 added support for adding or removing controller voters without a cluster restart, using &lt;code&gt;AddVoter&lt;/code&gt; and &lt;code&gt;RemoveVoter&lt;/code&gt; RPCs. Only one membership change is permitted at a time to prevent split-brain scenarios. This makes controller hardware replacement (after a disk failure, for example) an online operation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version timeline:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kafka version&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.8&lt;/td&gt;
&lt;td&gt;Early 2021&lt;/td&gt;
&lt;td&gt;KRaft introduced as experimental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.3&lt;/td&gt;
&lt;td&gt;August 2022&lt;/td&gt;
&lt;td&gt;KRaft marked production-ready (KIP-833); ZooKeeper deprecated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.6&lt;/td&gt;
&lt;td&gt;September 2023&lt;/td&gt;
&lt;td&gt;ZK-to-KRaft live migration promoted to GA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.9&lt;/td&gt;
&lt;td&gt;May 2025&lt;/td&gt;
&lt;td&gt;Final 3.x release; dynamic KRaft quorums (KIP-853) added&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;March 18, 2025&lt;/td&gt;
&lt;td&gt;ZooKeeper support completely removed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;ZooKeeper mode is not available in Kafka 4.0 and later. If you are running a ZooKeeper-based cluster on 3.x, migration to KRaft is required before upgrading to 4.x. The live migration process (KIP-866) runs in four phases - metadata copy, dual-write hybrid runtime, rolling broker reconfiguration, and finalization - and supports rollback through Phase 3.&lt;/p&gt;
&lt;h2 id=&quot;replication-and-fault-tolerance&quot;&gt;Replication and fault tolerance&lt;/h2&gt;
&lt;p&gt;Kafka replication distributes partition data across multiple brokers to survive broker failures without data loss or service interruption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In-sync replicas (ISR):&lt;/strong&gt; Each partition maintains an ISR set: the replicas that are considered current with the leader. A follower stays in the ISR as long as it has fetched from the leader within &lt;code&gt;replica.lag.time.max.ms&lt;/code&gt; (default: 30 seconds). If a follower stops fetching or falls behind, the leader removes it from the ISR by sending an &lt;code&gt;AlterPartitionRequest&lt;/code&gt; to the controller. The controller commits the shrunk ISR to the metadata log. Crucially, only the persisted ISR matters for leader election: if a leader fails before its latest ISR shrink request reaches the controller, the controller elects from the last committed ISR state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What happens when a broker fails:&lt;/strong&gt; In KRaft mode, if a broker misses its heartbeat window with the active controller, the controller fences the broker by appending a &lt;code&gt;FenceBrokerRecord&lt;/code&gt; to &lt;code&gt;__cluster_metadata&lt;/code&gt;. Brokers consuming the metadata log discover the fence and redirect clients away. The controller then initiates leader elections for all partitions the failed broker led.&lt;/p&gt;
&lt;p&gt;The controller selects a new leader from a priority hierarchy:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Candidate group&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;In-sync replicas (ISR)&lt;/td&gt;
&lt;td&gt;Selects the first active, unfenced ISR member. No data loss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Eligible leader replicas (ELR)&lt;/td&gt;
&lt;td&gt;If ISR is empty, selects from the ELR set (KIP-966).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Last known leader&lt;/td&gt;
&lt;td&gt;If ELR is also empty, attempts the last known unfenced leader.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Out-of-sync replicas&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;unclean.leader.election.enable=true&lt;/code&gt;, elects any surviving replica. Data loss occurs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;If unclean elections are disabled and no clean replica exists, the partition stays offline.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Unclean leader election:&lt;/strong&gt; With &lt;code&gt;unclean.leader.election.enable=false&lt;/code&gt; (the default), a partition stays offline rather than elect an out-of-sync replica. This protects data integrity at the cost of availability. With the setting enabled, the out-of-sync replica becomes leader, the ISR is reset to contain only that new leader, and any records acknowledged by the old leader but not replicated to the new leader are permanently lost. The controller marks the partition recovery state as RECOVERING. This setting should remain false for any topic where losing acknowledged writes is unacceptable. If you need to override it, do so at the topic level, not cluster-wide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The default configuration durability gap:&lt;/strong&gt; There is a trap worth naming directly. Since Kafka 3.0, &lt;code&gt;acks&lt;/code&gt; defaults to &lt;code&gt;all&lt;/code&gt;. However, &lt;code&gt;min.insync.replicas&lt;/code&gt; still defaults to 1, and &lt;code&gt;default.replication.factor&lt;/code&gt; defaults to 1. With &lt;code&gt;min.insync.replicas=1&lt;/code&gt;, &lt;code&gt;acks=all&lt;/code&gt; behaves identically to &lt;code&gt;acks=1&lt;/code&gt;: the leader acknowledges a write as soon as its own log is written, because the ISR contains only itself. If two follower replicas are temporarily removed from the ISR (due to network issues), the leader accepts writes with &lt;code&gt;acks=all&lt;/code&gt; and immediately acknowledges them. If that leader then crashes before the followers rejoin, those acknowledged records are gone.&lt;/p&gt;
&lt;p&gt;For any production topic where data loss is unacceptable, set:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;replication.factor=3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;min.insync.replicas=2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this configuration and &lt;code&gt;acks=all&lt;/code&gt;, a write is only acknowledged after two brokers have it on disk. If two brokers fail simultaneously and the ISR drops to 1, producers receive a &lt;code&gt;NotEnoughReplicasException&lt;/code&gt; rather than a silent acknowledgment that could be lost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leader epoch and truncation (KIP-101):&lt;/strong&gt; Kafka uses a leader epoch - a monotonically increasing counter incremented on each leadership change - to prevent log divergence during follower recovery. When a follower restarts, it sends an &lt;code&gt;OffsetForLeaderEpochRequest&lt;/code&gt; to the current leader, which responds with the log end offset for the follower’s epoch. The follower truncates only to that offset. Before KIP-101, followers truncated to their last local high watermark, which could cause data loss under a double failover scenario where a follower restarted and truncated before a second broker failure.&lt;/p&gt;
&lt;h2 id=&quot;consumers-and-consumer-groups&quot;&gt;Consumers and consumer groups&lt;/h2&gt;
&lt;p&gt;Kafka consumers read records from partitions by subscribing to topics and issuing FetchRequests to the broker that leads each assigned partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Offsets and commits:&lt;/strong&gt; A consumer’s position in a partition is its offset. Kafka stores committed offsets in the internal &lt;code&gt;__consumer_offsets&lt;/code&gt; topic, keyed by &lt;code&gt;(group.id, topic, partition)&lt;/code&gt;. When a consumer restarts, it resumes from its last committed offset. Two configurations govern offset behavior:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;auto.offset.reset&lt;/code&gt;: what to do when no committed offset exists. &lt;code&gt;earliest&lt;/code&gt; reads from the beginning; &lt;code&gt;latest&lt;/code&gt; reads from the current end; &lt;code&gt;none&lt;/code&gt; throws an exception.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable.auto.commit&lt;/code&gt;: whether the consumer automatically commits offsets on a schedule. Auto-commit is convenient but can commit before processing is complete, causing duplicate processing or data loss on restart. For reliable at-least-once guarantees, set &lt;code&gt;enable.auto.commit=false&lt;/code&gt; and commit offsets manually after successful processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Consumer groups:&lt;/strong&gt; A consumer group is a set of consumers identified by a shared &lt;code&gt;group.id&lt;/code&gt;. Each partition in a subscribed topic is assigned to exactly one consumer in the group at a time. A group with 10 consumers and a topic with 10 partitions gets one partition per consumer. A group with 10 consumers and 4 partitions has 6 idle consumers: partition count is the ceiling on consumer-level parallelism within a group.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rebalancing:&lt;/strong&gt; When a consumer joins or leaves a group, or when partition count changes, Kafka reassigns partitions across group members. The protocol used for rebalancing determines how disruptive this is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Eager rebalancing&lt;/strong&gt; (legacy): all consumers stop consuming and release all their partitions. The group coordinator assigns partitions from scratch. This is a stop-the-world pause for the entire group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cooperative-sticky rebalancing&lt;/strong&gt; (recommended): consumers retain as many existing partition assignments as possible. Only partitions that need to move change hands, and they do so incrementally. Consumers that keep their partitions continue processing throughout the rebalance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To enable cooperative-sticky rebalancing, set &lt;code&gt;partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor&lt;/code&gt;. This is not the default as of Kafka 3.x. Switching strategies in a running group requires a one-time migration: first add &lt;code&gt;CooperativeStickyAssignor&lt;/code&gt; alongside the existing strategy, perform a rolling application restart, then remove the old strategy and restart again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag:&lt;/strong&gt; &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;Consumer lag&lt;/a&gt; is the difference between the log end offset of a partition and the consumer group’s last committed offset for that partition. Lag represents the amount of unprocessed data. A persistently growing lag means the consumer cannot keep up with the producer and is a leading indicator of downstream problems. Monitoring lag per consumer group and per partition is one of the most operationally important signals in any Kafka-backed system.&lt;/p&gt;
&lt;h2 id=&quot;cluster-architecture-and-management&quot;&gt;Cluster architecture and management&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Adding brokers:&lt;/strong&gt; Adding a broker to a running cluster is non-disruptive, but new brokers are not assigned existing partitions automatically. Run a partition reassignment using &lt;code&gt;kafka-reassign-partitions.sh&lt;/code&gt; to generate and submit a reassignment plan. The plan is executed by the controller while the cluster continues serving traffic. Throttle the reassignment using &lt;code&gt;kafka-configs.sh&lt;/code&gt; to limit the impact on produce and consume latency during the move.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rolling upgrades:&lt;/strong&gt; Kafka supports rolling upgrades without downtime. Upgrade one broker at a time, allowing it to re-join and reach full replication before moving to the next. Keep &lt;code&gt;inter.broker.protocol.version&lt;/code&gt; (IBP) at the version of the oldest broker until all brokers are upgraded, then advance the IBP. In KRaft clusters, upgrade controllers before brokers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log retention and disk management:&lt;/strong&gt; Retention is managed per-topic via &lt;code&gt;log.retention.ms&lt;/code&gt;, &lt;code&gt;log.retention.bytes&lt;/code&gt;, and &lt;code&gt;cleanup.policy&lt;/code&gt;. Monitor per-broker disk usage; uneven partition distribution (common when some partitions are significantly larger than others) can cause a single broker to reach capacity before others. Consider tiered storage (see “What’s changing in Kafka architecture”) to offload older segments to object storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key JMX metrics:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderReplicatedPartitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-zero means at least one follower is lagging or offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OfflinePartitionsCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-zero means partitions with no available leader; immediate action required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ActiveControllerCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Should be exactly 1 cluster-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UncleanLeaderElectionsPerSec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Should be 0; non-zero means data loss is occurring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnderMinIsrPartitionCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-zero means writes with &lt;code&gt;acks=all&lt;/code&gt; are being rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer lag&lt;/td&gt;
&lt;td&gt;Per consumer group, per partition; growing lag indicates a consumer falling behind&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Throughput versus latency tuning:&lt;/strong&gt; The primary levers are on the producer side (&lt;code&gt;linger.ms&lt;/code&gt; and &lt;code&gt;batch.size&lt;/code&gt;) and the consumer side (&lt;code&gt;fetch.min.bytes&lt;/code&gt; and &lt;code&gt;fetch.max.wait.ms&lt;/code&gt;). Increasing &lt;code&gt;linger.ms&lt;/code&gt; and &lt;code&gt;batch.size&lt;/code&gt; improves throughput by sending larger batches at the cost of higher produce latency. Increasing &lt;code&gt;fetch.min.bytes&lt;/code&gt; and &lt;code&gt;fetch.max.wait.ms&lt;/code&gt; reduces the number of fetch requests at the cost of higher read latency. Neither set of parameters should be tuned in isolation from the other side of the pipeline.&lt;/p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;/h2&gt;
&lt;p&gt;Kafka’s security model covers authentication, authorization, and encryption. All three require explicit configuration; Kafka ships with no authentication enabled by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication (SASL):&lt;/strong&gt; Kafka supports four SASL mechanisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PLAIN:&lt;/strong&gt; Username and password transmitted in plaintext. Acceptable only when TLS is also enabled to encrypt the connection. Simple to configure; not suitable for environments where credentials might be intercepted at the network layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SCRAM-SHA-256 / SCRAM-SHA-512:&lt;/strong&gt; Salted challenge-response. Credentials are stored as salted hashes and managed via the Kafka admin API (in KRaft clusters). Well-suited for most deployments without existing Kerberos infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GSSAPI (Kerberos):&lt;/strong&gt; Integrates with Active Directory or MIT Kerberos. Required in environments with existing Kerberos infrastructure. Operationally complex to configure correctly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OAUTHBEARER:&lt;/strong&gt; Delegates authentication to an OAuth 2.0 identity provider. Well-suited for cloud-native environments where OIDC is already in use. Requires a custom or third-party token validator implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For new deployments without existing Kerberos infrastructure, SCRAM-SHA-512 over TLS is the most practical starting point. For cloud-native environments with identity provider integration, OAUTHBEARER is increasingly common.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authorization (ACLs):&lt;/strong&gt; Kafka ACLs grant or deny operations (Read, Write, Create, Delete, Describe, and others) on resources (topics, consumer groups, clusters) to principals (users, service accounts). ACLs are managed via &lt;code&gt;kafka-acls.sh&lt;/code&gt; or the admin API. In KRaft clusters, use &lt;code&gt;StandardAuthorizer&lt;/code&gt; rather than the legacy &lt;code&gt;AclAuthorizer&lt;/code&gt;, which required ZooKeeper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Encryption in transit:&lt;/strong&gt; Configure TLS on broker listeners by setting a keystore and truststore. All inter-broker communication should also use TLS (&lt;code&gt;security.inter.broker.protocol=SSL&lt;/code&gt;). mTLS (mutual TLS) is used in environments that want the broker to authenticate clients via client certificates, rather than relying on SASL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network isolation:&lt;/strong&gt; Kafka brokers should not be exposed to the public internet. Place them in private subnets, restrict inbound access to known application IP ranges via security groups or firewall rules, and use separate listener configurations if you need to expose Kafka to both internal and external clients with different security settings (&lt;code&gt;listeners&lt;/code&gt; and &lt;code&gt;advertised.listeners&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;schemas-and-schema-registry&quot;&gt;Schemas and schema registry&lt;/h2&gt;
&lt;p&gt;Without schema management, a Kafka topic is a sequence of untyped bytes. Any producer can write any format; any consumer must be written to match. This implicit contract fails silently: a producer change that modifies a field type or removes a required field can break consumers without warning, and the break is discovered at deserialize time, not at produce time.&lt;/p&gt;
&lt;p&gt;A schema registry solves this by storing schemas centrally and embedding a schema ID in each record. Before a producer serializes a record, it registers the schema (if not already registered) and encodes the schema ID as a prefix in the record value, using the Confluent wire format: a magic byte, a 4-byte schema ID, then the serialized payload. Consumers look up the schema by ID before deserializing. Incompatible schema changes are rejected at produce time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Supported formats:&lt;/strong&gt; All major schema registries support Avro, Protobuf, and JSON Schema. Avro is the most widely deployed in Kafka ecosystems; Protobuf is common in organizations already using gRPC; JSON Schema is convenient but produces larger payloads and is less strict about types.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compatibility modes:&lt;/strong&gt; The registry enforces schema evolution rules per-subject:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BACKWARD:&lt;/strong&gt; New schema can read data written by the previous schema. Safe for consumers to upgrade before producers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FORWARD:&lt;/strong&gt; Previous schema can read data written by the new schema. Safe for producers to upgrade before consumers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FULL:&lt;/strong&gt; Both backward and forward compatible. The safest setting for most production topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NONE:&lt;/strong&gt; No compatibility checking.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Registry implementations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Confluent Schema Registry:&lt;/strong&gt; The most widely deployed implementation. Defines the wire format that most Kafka serializers expect. Available as open source under a mix of Apache 2.0 and Confluent Community License terms, or as a managed service in Confluent Cloud.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apicurio Registry:&lt;/strong&gt; Fully Apache 2.0 licensed. Supports Avro, Protobuf, and JSON Schema. Compatible with the Confluent wire format, making it a drop-in alternative for clients using standard Confluent serializers. Maintained by Red Hat.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Glue Schema Registry:&lt;/strong&gt; Managed service integrated with MSK and other AWS services. Supports Avro and JSON Schema; Protobuf support was added more recently. Uses a different wire format from Confluent, requiring Glue-specific serializers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For self-managed clusters or environments where license terms matter, Apicurio is the best-maintained open-source alternative to Confluent Schema Registry.&lt;/p&gt;
&lt;h2 id=&quot;kafka-connect&quot;&gt;Kafka Connect&lt;/h2&gt;
&lt;p&gt;Kafka Connect is a framework for building and running connectors that move data between Kafka and external systems. Rather than writing custom producer or consumer applications for each system integration, you configure a connector with a set of properties, and Connect handles serialization, offset tracking, error handling, and parallelism.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source connectors&lt;/strong&gt; read from an external system and write to a Kafka topic. A PostgreSQL CDC source connector using Debezium, for example, tails the write-ahead log and produces records to Kafka as each committed row change occurs. &lt;strong&gt;Sink connectors&lt;/strong&gt; read from a Kafka topic and write to an external system: an S3 sink connector writes records to S3, partitioned by date or hour.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment modes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standalone mode:&lt;/strong&gt; A single Connect worker process. No fault tolerance. Suitable for development or simple single-node pipelines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distributed mode:&lt;/strong&gt; Multiple Connect workers form a group. Tasks are distributed across the group; if a worker fails, tasks are reassigned to surviving workers. This is the production deployment model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Exactly-once semantics in Connect:&lt;/strong&gt; Connect supports exactly-once delivery for source connectors in distributed mode (since Kafka 2.6), using the transactional producer API combined with offset storage in Kafka. The connector’s source offsets and the produced records are written in the same transaction, so a crash mid-write is recovered cleanly on restart. Not all connectors support this: the connector must manage offsets through the Connect framework rather than externally. Exactly-once for sink connectors depends on whether the target system can participate in transactions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connector ecosystem:&lt;/strong&gt; Confluent Hub hosts several hundred connectors. Debezium provides production-grade CDC connectors for PostgreSQL, MySQL, SQL Server, MongoDB, and others, and is widely used for event sourcing and data replication patterns. Most major cloud data stores (S3, GCS, BigQuery, Redshift, Snowflake, Elasticsearch) have maintained sink connectors available.&lt;/p&gt;
&lt;p&gt;A concrete example of a common Connect pipeline: a Debezium PostgreSQL source connector captures row changes from a transactional database and writes them to Kafka topics (one topic per table), from where an S3 sink connector archives them to object storage. Both ends are configured and managed in Connect; no custom producer or consumer code is written.&lt;/p&gt;
&lt;h2 id=&quot;kafka-streams&quot;&gt;Kafka Streams&lt;/h2&gt;
&lt;p&gt;Kafka Streams is a client library for building stateful stream processing applications that read from and write to Kafka. Unlike Apache Flink or Spark Streaming, it runs inside your application process: there is no separate cluster to deploy, no cluster manager to operate, and no external state store required unless you choose to add one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core abstractions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KStream:&lt;/strong&gt; An unbounded stream of records. Each record is an independent event.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KTable:&lt;/strong&gt; A changelog stream interpreted as a materialized table. Each record represents the latest value for a key. Supports joins and aggregations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GlobalKTable:&lt;/strong&gt; A KTable replicated in full to every application instance. Used for broadcast joins where one side is small relative to the other.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;State stores:&lt;/strong&gt; Aggregations and joins require state. Kafka Streams maintains state in local RocksDB stores, backed by changelog topics in Kafka. On restart, an application instance restores its state by replaying the changelog topic. State stores can also be queried externally via the interactive queries API, allowing the application to expose its materialized state as a queryable service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use Kafka Streams versus alternatives:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka Streams&lt;/strong&gt; is the right choice when your processing logic is sophisticated and tied to Kafka topics, and when running a separate processing cluster adds more cost and complexity than it is worth. It handles stateful operations (joins, aggregations, windowing) well, and deploying it as a jar alongside your existing application is a significant operational simplicity advantage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Flink&lt;/strong&gt; is preferable when you need to join Kafka streams with non-Kafka sources, when batch and streaming workloads need to share processing logic, or when the scale of state exceeds what RocksDB on a single JVM can comfortably handle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ksqlDB&lt;/strong&gt; provides a SQL interface over Kafka Streams. See the next section.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ksqldb&quot;&gt;ksqlDB&lt;/h2&gt;
&lt;p&gt;ksqlDB is a SQL-based stream processing system built on top of Kafka Streams. It lets you define streaming transformations, aggregations, and joins using SQL syntax, without writing application code. It supports two query types: push queries, which return a continuous stream of results and are suited to streaming dashboards and alerting, and pull queries, which return point-in-time lookups against materialized state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Relationship to Kafka Streams:&lt;/strong&gt; ksqlDB compiles SQL statements into Kafka Streams topologies and runs them on ksqlDB server nodes. The state store model, changelog topics, and processing guarantees are the same as Kafka Streams; ksqlDB adds a query language and a server process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Development activity:&lt;/strong&gt; Contributor activity on the ksqlDB GitHub repository (github.com/confluentinc/ksql) declined materially from 2022 onward. Confluent acquired Immerok in early 2023 to accelerate a cloud-native Apache Flink offering, now marketed as Confluent Cloud for Apache Flink. Confluent’s product positioning has shifted decisively toward Flink; ksqlDB receives minimal prominence in current Confluent documentation and engineering content. If you are evaluating ksqlDB for a new deployment, the reduced development momentum is a concrete factor in the decision, not an editorial judgement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Licensing:&lt;/strong&gt; ksqlDB is distributed under the Confluent Community License, not Apache 2.0. The Community License prohibits using the software to build a competing SaaS product. For internal deployments this is rarely a practical concern, but it is worth understanding before committing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choosing a stream processing layer:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;When to choose it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;Java/Scala client library embedded in your application&lt;/td&gt;
&lt;td&gt;Sophisticated stateful processing on Kafka data; no separate cluster needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ksqlDB&lt;/td&gt;
&lt;td&gt;SQL interface over Kafka Streams; runs as a separate server&lt;/td&gt;
&lt;td&gt;Lower barrier for SQL-fluent teams; simpler, lower-throughput use cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;General-purpose stream and batch processor; separate cluster&lt;/td&gt;
&lt;td&gt;Mixed Kafka and non-Kafka sources; large-scale state; batch and streaming in one system&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;kafka-architecture-in-practice&quot;&gt;Kafka architecture in practice&lt;/h2&gt;
&lt;p&gt;The following companies use Kafka as a core part of their production infrastructure.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;How they use Kafka&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/apple-kafka-architecture&quot;&gt;Apple&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Runs Kafka across dozens of data centres to move billions of events per day through internal analytics and ML pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/afterpay-kafka-architecture&quot;&gt;Afterpay&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka as the central event bus for payment transaction processing and real-time fraud detection across its buy-now-pay-later platform.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/cash-app-kafka-architecture&quot;&gt;Cash App&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams financial transaction events through Kafka to power real-time payment processing, ledger updates, and fraud signals.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/adidas-kafka-architecture&quot;&gt;Adidas&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to unify e-commerce order events, inventory updates, and customer activity data across its global digital platform.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/notion-kafka-architecture&quot;&gt;Notion&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Propagates document change events through Kafka to keep real-time collaboration, search indexing, and audit logs in sync.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/bytedance-kafka-architecture&quot;&gt;Bytedance&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Processes trillions of user interaction events per day through Kafka to feed the recommendation engines behind TikTok and Douyin.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/datadog-kafka-architecture&quot;&gt;Datadog&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ingests metrics, logs, and distributed traces at massive scale through Kafka before routing them to purpose-built storage backends.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/walmart-kafka-architecture&quot;&gt;Walmart&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams real-time inventory, order, and supply chain events through Kafka to coordinate fulfilment across thousands of stores and warehouses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/paypal-kafka-architecture&quot;&gt;PayPal&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to process billions of financial events per day, connecting payment processing, fraud detection, and risk scoring pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/tencent-kafka-architecture&quot;&gt;Tencent&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Runs one of the largest Kafka deployments in the world to handle messaging, activity data, and analytics across WeChat and its gaming platforms.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/the-new-york-times-kafka-architecture&quot;&gt;The New York Times&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to decouple its content publishing pipeline, streaming article events from the CMS through to personalisation, search, and reader analytics.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/grab-kafka-architecture&quot;&gt;Grab&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Moves real-time ride-hailing and food delivery events through Kafka to support driver-passenger matching, dynamic pricing, and operational dashboards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/wix-kafka-architecture&quot;&gt;Wix&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka as the event backbone for its website-building platform, streaming user and site activity data into analytics and product features.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/doordash-kafka-architecture&quot;&gt;DoorDash&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams order lifecycle events through Kafka to coordinate real-time delivery tracking, driver dispatch, and merchant notifications.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/goldman-sachs-kafka-architecture&quot;&gt;Goldman Sachs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to distribute market data and trade events across trading systems, risk engines, and regulatory reporting pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/jpmorgan-kafka-architecture&quot;&gt;JPMorgan&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Processes financial transaction and market data events through Kafka to support real-time risk management and internal data platform services.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/new-relic-kafka-architecture&quot;&gt;New Relic&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka as the ingestion backbone for its observability platform, buffering and routing telemetry data before it reaches downstream storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/pagerduty-kafka-architecture&quot;&gt;PagerDuty&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Routes alert and incident events through Kafka to decouple ingestion from routing logic and ensure reliable delivery at high ingest volumes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/robinhood-kafka-architecture&quot;&gt;Robinhood&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams trading, market data, and account events through Kafka to power real-time order execution, position updates, and risk controls.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/salesforce-kafka-architecture&quot;&gt;Salesforce&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to propagate CRM change events across its platform, feeding real-time automation, Einstein AI features, and customer-facing integrations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/pinterest-kafka-architecture&quot;&gt;Pinterest&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Moves user activity and engagement events through Kafka to train recommendation models and update real-time content ranking signals.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/reddit-kafka-architecture&quot;&gt;Reddit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams user activity, voting, and moderation events through Kafka to power feed ranking, spam detection, and site-wide analytics.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/spotify-kafka-architecture&quot;&gt;Spotify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Processes hundreds of billions of user listening events per day through Kafka to feed personalisation, recommendations, and the Discover Weekly pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/shopify-kafka-architecture&quot;&gt;Shopify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to stream order, merchant, and storefront events across its platform, decoupling the checkout flow from downstream fulfilment and analytics.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/netflix-kafka-architecture&quot;&gt;Netflix&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Runs Kafka as the central nervous system for its data pipeline, handling viewing events, operational metrics, and chaos engineering signals at global scale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/barclays-kafka-architecture&quot;&gt;Barclays&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to stream banking transaction events and market data across trading, risk, and regulatory reporting systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/airbnb-kafka-architecture&quot;&gt;Airbnb&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Streams booking, search, and host activity events through Kafka to support real-time pricing, fraud detection, and data warehouse ingestion.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Created Kafka to solve its own activity stream and operational data pipeline problem; now uses it at trillion-message-per-day scale across all major platform services.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/cloudflare-kafka-architecture&quot;&gt;Cloudflare&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Processes network security events and DNS query logs at internet scale through Kafka, routing data into threat detection and analytics pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://factorhouse.io/articles/uber-kafka-architecture&quot;&gt;Uber&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Uses Kafka to stream trip, financial, and marketplace events across its global platform, supporting surge pricing, driver-partner payments, and real-time analytics.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;kafka-architecture-best-practices&quot;&gt;Kafka architecture best practices&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Set replication factor to 3 and &lt;code&gt;min.insync.replicas&lt;/code&gt; to 2.&lt;/strong&gt; The default &lt;code&gt;replication.factor=1&lt;/code&gt; is a single point of failure. For any production topic, &lt;code&gt;replication.factor=3&lt;/code&gt; with &lt;code&gt;min.insync.replicas=2&lt;/code&gt; ensures that a write is only acknowledged after it is on at least two brokers, and that the cluster can survive a single broker failure without data loss or partition unavailability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep &lt;code&gt;unclean.leader.election.enable=false&lt;/code&gt;.&lt;/strong&gt; This is the default. For any topic where losing acknowledged writes is unacceptable, never override it cluster-wide. If you need to override it for a specific topic for availability reasons, do so at the topic level and document the trade-off explicitly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable idempotent producers.&lt;/strong&gt; Set &lt;code&gt;enable.idempotence=true&lt;/code&gt;. For exactly-once delivery across multiple topics, use the transactional API. The advice to set &lt;code&gt;max.in.flight.requests.per.connection=1&lt;/code&gt; for idempotent producers is outdated: since Kafka 0.11, idempotent producers safely support up to five in-flight requests per partition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Size partition count based on expected peak consumer parallelism, not just throughput.&lt;/strong&gt; Consumer parallelism within a group is bounded by partition count. Under-partitioned topics create bottlenecks that cannot be resolved without increasing partition count (which risks breaking per-key ordering guarantees for keyed records).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cooperative-sticky rebalancing.&lt;/strong&gt; Set &lt;code&gt;partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor&lt;/code&gt; on all consumer groups. Eager rebalancing stops all consumers during reassignment; cooperative-sticky is incremental and significantly reduces the disruption of consumer group membership changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit consumer offsets after processing, not before.&lt;/strong&gt; Set &lt;code&gt;enable.auto.commit=false&lt;/code&gt; and commit offsets manually after records are fully processed. Auto-commit can commit before processing is complete, causing data loss on consumer restart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set rack-aware replica placement.&lt;/strong&gt; Configure &lt;code&gt;broker.rack&lt;/code&gt; on each broker. Without this, multiple replicas of the same partition can end up on brokers in the same availability zone or physical rack, negating the fault tolerance that replication provides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor the critical JMX metrics.&lt;/strong&gt; Alert when &lt;code&gt;OfflinePartitionsCount&lt;/code&gt; is above 0, &lt;code&gt;UnderReplicatedPartitions&lt;/code&gt; is above 0 for more than a few minutes (allow for normal during rolling restarts), &lt;code&gt;UncleanLeaderElectionsPerSec&lt;/code&gt; is above 0, and consumer lag grows unboundedly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce schema compatibility.&lt;/strong&gt; Even if you control both producers and consumers today, schemas drift over time. Registering schemas and enforcing &lt;code&gt;BACKWARD&lt;/code&gt; or &lt;code&gt;FULL&lt;/code&gt; compatibility at the registry level catches breaking changes before they reach production consumers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a Kafka UI for operational visibility.&lt;/strong&gt; CLI tooling is sufficient for debugging in isolation, but for running Kafka in production you need a way to observe broker health, consumer lag, topic throughput, and cluster configuration without SSH access. A &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;management interface&lt;/a&gt; is a standard part of a well-operated Kafka cluster.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;kafka-management-tools&quot;&gt;Kafka management tools&lt;/h3&gt;
&lt;p&gt;If you are running Kafka in production, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; by Factor House is a &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Kafka UI&lt;/a&gt; built for the operational practices described in this article. It gives you real-time visibility into consumer lag, broker health, topic throughput, and cluster configuration in one place - the signals you need when something goes wrong.&lt;/p&gt;
&lt;h2 id=&quot;deployment-options&quot;&gt;Deployment options&lt;/h2&gt;
&lt;p&gt;How you run Kafka shapes your operational responsibilities, scaling limits, and cost profile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-managed on VMs or bare metal:&lt;/strong&gt; Full control over Kafka version, configuration, and hardware. The highest operational overhead: you are responsible for provisioning, upgrades, monitoring, security hardening, and failure recovery. The right choice for organizations with existing infrastructure teams, specific hardware requirements, or compliance constraints that prevent using managed services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes (Strimzi or Confluent Operator):&lt;/strong&gt; Container-native deployment. Strimzi is open source (Apache 2.0) and provides Kubernetes Operators for Kafka clusters, Connect, MirrorMaker, and Bridge. Confluent Operator is a commercial product. Both abstract Kubernetes complexity for Kafka operations and are a reasonable choice for teams already running Kubernetes who want to standardize deployment tooling. The main challenge is that Kafka is a stateful system: getting persistent volume sizing, storage class selection, and pod anti-affinity right requires Kafka-specific knowledge on top of Kubernetes knowledge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Confluent Cloud:&lt;/strong&gt; Fully managed Kafka with proprietary add-on services (Confluent Schema Registry, hosted Connect connectors, Confluent Cloud for Apache Flink). Significantly reduces operational overhead. Higher cost per throughput unit than self-managed options. Proprietary extensions create vendor dependency. Available on AWS, Azure, and GCP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon MSK:&lt;/strong&gt; AWS-native managed Kafka. Integrates with IAM for authentication and authorization, VPC for network isolation, and other AWS services. Kafka version support lags the Apache release schedule by several months; check the current MSK documentation for the latest supported version before planning an upgrade. MSK does not support all Kafka configurations and restricts some broker-level plugin use (relevant for some Connect connectors).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Other managed options:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aiven for Apache Kafka:&lt;/strong&gt; Multi-cloud managed service across AWS, GCP, Azure, and DigitalOcean. Tracks the Apache Kafka release schedule closely. A good option when cloud portability matters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redpanda Cloud:&lt;/strong&gt; Redpanda is Kafka API-compatible but is a different broker implementation (written in C++, not Java). Compatible with most Kafka clients and tools. There are known API gaps relative to Apache Kafka; evaluate compatibility against your specific use case - particularly around transactions, exactly-once semantics, and any Kafka Streams or Connect usage - before committing.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Operational overhead&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Vendor lock-in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Self-managed (VMs/bare metal)&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes (Strimzi)&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Medium-high&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Cloud&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon MSK&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium (AWS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aiven&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Cloud&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;whats-changing-in-kafka-architecture&quot;&gt;What’s changing in Kafka architecture&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ZooKeeper removal (complete):&lt;/strong&gt; ZooKeeper support was removed in Kafka 4.0, released March 18, 2025. All clusters running Kafka 4.x operate on KRaft exclusively. If you are on Kafka 3.x with ZooKeeper, migration to KRaft is required before upgrading to 4.x.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tiered storage (KIP-405):&lt;/strong&gt; Tiered storage allows Kafka to offload older log segments to object storage (S3, GCS, Azure Blob Storage) while keeping recent segments on local broker disk. This decouples data retention from broker disk capacity: you can retain data for months without sizing broker storage to match. Tiered storage reached General Availability in Kafka 3.6 for self-managed clusters, implemented via a pluggable &lt;code&gt;RemoteStorageManager&lt;/code&gt; interface. Confluent Cloud has its own implementation; MSK added tiered storage support separately. Check current documentation for feature flags and known limitations in your deployment environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Queue semantics (KIP-932):&lt;/strong&gt; Kafka’s current partition assignment model gives each partition to exactly one consumer in a group, which preserves per-partition ordering but limits work distribution between consumers. KIP-932 proposes a “share group” model where multiple consumers can compete for records within the same partition, enabling queue-like behavior for workloads where ordering is not required and maximum consumer throughput matters. As of this writing, KIP-932 is available as an early access feature in some Kafka versions. Check the Apache Kafka KIP wiki for current status before building on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Eligible leader replicas (KIP-966):&lt;/strong&gt; Previously, if the ISR for a partition became empty, the only options were waiting for a replica to recover or enabling unclean elections. KIP-966 introduced the Eligible Leader Replica (ELR) set: replicas not currently in the ISR but with a sufficient log offset to be safe candidates for election. This reduces partition unavailability in scenarios where the ISR shrinks to zero due to rolling restarts or multi-broker failure, without the data loss risk of unclean elections.&lt;/p&gt;
&lt;p&gt;For a complete list of active proposals affecting Kafka’s core architecture, the Apache Kafka KIP wiki is the authoritative reference.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>How Netflix uses Apache Kafka in production</title><link>https://factorhouse.io/articles/netflix-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/netflix-kafka-architecture/</guid><description>A deep-dive into Netflix&apos;s Kafka architecture — covering the Keystone pipeline, Data Mesh platform, scale figures from 700 billion to 2 trillion events per day, and the engineering decisions behind it.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Netflix’s &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; story is one of the more thoroughly documented in the industry. Starting with a single pipeline routing 30% of event traffic in early 2015, Netflix has grown to a system processing over 2 trillion events per day across thousands of brokers — with 20,000-plus Flink jobs connected by Kafka topics at the core of its real-time data infrastructure.&lt;/p&gt;
&lt;p&gt;The engineering problem Kafka solves at Netflix is scale and decoupling across a massive, distributed microservice estate. Every interaction a member has — a play, a search, a scroll, a title impression — needs to reach multiple downstream systems within seconds. Kafka sits at the centre of that flow.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Netflix is a streaming entertainment service operating in over 190 countries, with around 300 million subscribers as of 2025. At its scale, real-time data is not a feature; it drives personalisation, content decisions, studio operations, and service reliability. The platform generates enormous event volumes at all hours across all regions, which means the data infrastructure has to match both the throughput and the latency requirements of those use cases.&lt;/p&gt;
&lt;p&gt;Netflix adopted Apache Kafka incrementally. Before Kafka, event data flowed through a system called Suro, a proprietary pipeline open-sourced in 2013. Kafka entered the picture in early 2015 as part of Keystone V1.5 — initially handling about 30% of event traffic alongside Chukwa for real-time consumers. By December 2015, Keystone V2 was in production with Kafka as the primary ingest layer, replacing Chukwa entirely.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Suro open-sourced — Netflix’s pre-Kafka event pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feb 2015&lt;/td&gt;
&lt;td&gt;Keystone V1.5 — Kafka introduced, handling ~30% of event traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dec 2015&lt;/td&gt;
&lt;td&gt;Keystone V2 live — Kafka becomes the primary fronted gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apr 2016&lt;/td&gt;
&lt;td&gt;36 clusters, 4,000+ brokers, 700 billion messages per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mar 2018&lt;/td&gt;
&lt;td&gt;3,000+ brokers globally, 1 trillion messages per day, 99.99% availability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sep 2019&lt;/td&gt;
&lt;td&gt;Inca — sampled distributed tracing system on dedicated Kafka cluster published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;Data Mesh platform: 2 trillion+ events per day, 20,000+ Flink jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nov 2023&lt;/td&gt;
&lt;td&gt;Streaming SQL in Data Mesh: 1,200 SQL processors created in first year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 2025&lt;/td&gt;
&lt;td&gt;Tudum CQRS architecture replaced — Kafka retired for one read-heavy use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 2025&lt;/td&gt;
&lt;td&gt;Real-Time Distributed Graph published: up to 1 million messages per second per topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dec 2025&lt;/td&gt;
&lt;td&gt;Live streaming monitoring stack: 38 million events per second peak capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;netflixs-kafka-use-cases&quot;&gt;Netflix’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Netflix uses Kafka across several distinct domains. The use cases range from high-volume, low-latency event ingest to event-driven microservice coordination and real-time member personalisation.&lt;/p&gt;
&lt;h3 id=&quot;keystone-pipeline--centralised-event-ingest-and-routing&quot;&gt;Keystone pipeline — centralised event ingest and routing&lt;/h3&gt;
&lt;p&gt;The Keystone pipeline is Netflix’s primary data highway. Every interaction a member generates on any device flows through Keystone: plays, pauses, searches, row scrolls, title impressions. Kafka serves as the front door, ingesting these events from client applications and routing them to downstream sinks including Amazon S3, Elasticsearch, Apache Cassandra, and secondary Kafka topics.&lt;/p&gt;
&lt;p&gt;The routing tier runs Flink and Samza jobs on EC2, processing events in near real time and updating member taste profiles in Cassandra within seconds of the originating interaction.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing-as-a-service-keystone-spaas&quot;&gt;Stream processing as a service (Keystone SPaaS)&lt;/h3&gt;
&lt;p&gt;Keystone’s SPaaS tier makes Flink-over-Kafka available to internal teams as a managed service. Teams submit stream processing jobs without owning infrastructure. Use cases include generating real-time features for recommendation models (replacing 24-hour batch jobs), aggregating A/B test results, and building near-real-time personalisation pipelines.&lt;/p&gt;
&lt;p&gt;Nitin Sharma, who led Content Finance infrastructure at Netflix, described the core value: “We’re able to replace batch workflows that ran once a day with streaming pipelines that update model inputs continuously.”&lt;/p&gt;
&lt;h3 id=&quot;data-mesh--inter-service-data-movement-and-processing&quot;&gt;Data Mesh — inter-service data movement and processing&lt;/h3&gt;
&lt;p&gt;The Data Mesh platform, described in a 2022 Netflix TechBlog post, uses Kafka topics as the connective tissue between processing stages. Every upstream Flink Processor writes output to a Kafka topic; every downstream Processor reads from one. The platform manages topic provisioning, schema registration, and job lifecycle.&lt;/p&gt;
&lt;p&gt;As of 2022, Netflix operates 20,000-plus Flink jobs connected by thousands of Kafka topics through this platform. A Streaming SQL layer added in 2023 wraps the Flink Table API behind standard SQL, allowing non-infrastructure teams to build pipelines without writing custom Processors. Within the first year of its launch, 1,200 SQL processors had been created by teams outside the data infrastructure organisation.&lt;/p&gt;
&lt;h3 id=&quot;content-finance--event-driven-microservices&quot;&gt;Content Finance — event-driven microservices&lt;/h3&gt;
&lt;p&gt;Netflix’s Content Finance engineering team (budgeting, talent payments, content accounting, cashflow) migrated from synchronous RPC calls to a Kafka-based event-driven architecture. Services now emit standardised events containing an entity ID, UUID, timestamp, operation type, and optional payload. Flink enriches and reorders these events before routing them to keyed Kafka topics consumed by Spring Boot services.&lt;/p&gt;
&lt;p&gt;Before the migration, synchronous dependencies between services created cascading failures and split-brain state when an upstream service became unavailable. After the migration, at-least-once delivery with idempotent consumers eliminated the reconciliation overhead that had required manual intervention.&lt;/p&gt;
&lt;h3 id=&quot;monitoring-and-observability&quot;&gt;Monitoring and observability&lt;/h3&gt;
&lt;p&gt;Netflix uses Kafka at multiple layers of its observability stack.&lt;/p&gt;
&lt;p&gt;The Title Health microservice ingests real-time title impression events via Kafka to validate content availability — artwork, recommendations, localisation — across thousands of monthly launches. Collector jobs process these events and surface problems before a title reaches significant audience.&lt;/p&gt;
&lt;p&gt;The live streaming monitoring stack, introduced as Netflix expanded into live events, uses Kafka alongside Mantis, Atlas, and Druid to process up to 38 million events per second and deliver critical playback quality metrics within seconds. This stack was designed to detect degradation at the speed live events require.&lt;/p&gt;
&lt;h3 id=&quot;write-ahead-log--database-resilience&quot;&gt;Write-Ahead Log — database resilience&lt;/h3&gt;
&lt;p&gt;Netflix built a Write-Ahead Log (WAL) abstraction that uses Kafka (alongside Amazon SQS) as a pluggable message queue to decouple producers from consumers in database mutation flows. Each WAL namespace gets a dedicated Kafka topic and a dead-letter queue by default. The WAL supports delay queues, cross-region replication, and multi-table atomic mutations. The most common deployment pattern is as a delay queue for scheduled database writes.&lt;/p&gt;
&lt;h3 id=&quot;real-time-distributed-graph--member-taste-profiles&quot;&gt;Real-Time Distributed Graph — member taste profiles&lt;/h3&gt;
&lt;p&gt;Netflix’s Real-Time Distributed Graph (RDG) uses Kafka to carry member actions — views, ratings, interactions — into Flink jobs that populate a graph database representing member preferences. Kafka provides durable, replayable streams so downstream processors can consume events in real time or replay from an earlier offset if needed. Individual Kafka topics in the RDG system carry up to roughly 1 million messages per second.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;Netflix’s published scale figures show consistent growth over a decade:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;36 clusters, 4,000+ broker instances, 700+ billion messages per day&lt;/td&gt;
&lt;td&gt;Netflix TechBlog, “Kafka Inside Keystone Pipeline”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;700+ Kafka topics in production, 450 billion events per day&lt;/td&gt;
&lt;td&gt;Shriya Arora, QCon New York 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;3,000+ brokers globally, 1 trillion messages per day, 99.99% availability&lt;/td&gt;
&lt;td&gt;Allen Wang, QCon London 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Up to 8 million events per second peak throughput&lt;/td&gt;
&lt;td&gt;Netflix TechBlog, “Keystone Real-time Stream Processing Platform”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2 billion trace messages per day through Inca (dedicated Kafka cluster)&lt;/td&gt;
&lt;td&gt;Allen Wang, Netflix TechBlog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;2 trillion+ events per day, 20,000+ Flink jobs connected by Kafka topics&lt;/td&gt;
&lt;td&gt;Netflix TechBlog, “Data Mesh”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;1,200 SQL processors created in the first year of Streaming SQL launch&lt;/td&gt;
&lt;td&gt;Netflix TechBlog, “Streaming SQL in Data Mesh”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Up to ~1 million messages per second per Kafka topic (RDG system)&lt;/td&gt;
&lt;td&gt;Netflix TechBlog, “Real-Time Distributed Graph”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;38 million events per second peak capacity (live streaming monitoring)&lt;/td&gt;
&lt;td&gt;InfoQ, December 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;Retention policies are tailored per topic based on throughput and record size. In the Keystone batch path, Kafka TTL was set to 4-6 hours with raw events backed up in HDFS and S3 for 1-2 days to support replay when Kafka retention had expired. More recent architectures use Apache Iceberg tables as a backfill source for pipelines that need data beyond Kafka’s retention window.&lt;/p&gt;
&lt;h2 id=&quot;netflixs-kafka-architecture&quot;&gt;Netflix’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;high-level-topology&quot;&gt;High-level topology&lt;/h3&gt;
&lt;p&gt;Netflix operates entirely on AWS and chose a multi-cluster topology specifically designed for the cloud’s characteristics. Rather than scaling a single large cluster, Netflix provisions many smaller, mostly immutable clusters. This limits blast radius, simplifies broker upgrades, and aligns with AWS’s preference for stateless, disposable infrastructure.&lt;/p&gt;
&lt;p&gt;The Keystone pipeline uses a two-tier cluster layout:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fronting Kafka clusters&lt;/strong&gt; receive events from all producers. Their purpose is ingest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer Kafka clusters&lt;/strong&gt; serve downstream consumers. Their purpose is read throughput.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Separating these tiers prevents consumer fan-out from affecting producer ingest performance. As the number of downstream consumer groups grew, serving them all from a single cluster became impractical.&lt;/p&gt;
&lt;p&gt;Custom smart clients wrap the standard Kafka producer and consumer interfaces. Producers write to fronting clusters; consumers read from consumer clusters. The smart client handles routing and failover transparently, so application code interacts with a logical endpoint rather than a specific cluster.&lt;/p&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;All Kafka events at Netflix use Apache Avro serialisation. Before any event can be written to a topic, its Avro schema must be registered in an internal Schema Registry. This enforces schema-at-producer: malformed or undeclared events cannot enter the pipeline. Avro provides roughly 3-5x size reduction versus JSON, which matters at multi-trillion-event-per-day volumes.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;The Keystone ingest layer accepts events via both a Java library embedded in producer applications and an HTTP proxy for services or devices that cannot embed the library. Producers target the fronting tier. Configuration details for acks, batching, and idempotency have not been published in detail in Netflix’s public posts, but the shift to rack-aware partition assignment (see Special Techniques below) indicates careful tuning of cross-AZ placement.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer groups read from the consumer tier clusters. Offset management follows standard Kafka committed-offset patterns, with offset-based lag monitoring feeding the Atlas metrics platform via a custom Kafka MetricReporter. Netflix derives consumer lag by calculating log end offsets minus committed offsets per topic-partition.&lt;/p&gt;
&lt;p&gt;A “stuck consumer” detection mechanism flags cases where committed consumer offsets stall within a given window, triggering First Responder, Netflix’s auto-remediation system, to relaunch affected stateless jobs.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Flink is the primary stream processing engine throughout Netflix’s Kafka infrastructure. In the Keystone SPaaS tier, Flink jobs run in Docker containers on EC2 and are managed by the platform’s reconciliation layer. The Data Mesh platform connects thousands of Flink Processors via Kafka topics, with job state stored in Amazon RDS as the authoritative source of truth.&lt;/p&gt;
&lt;p&gt;A Streaming SQL layer (added in 2023) wraps the Flink Table API and translates SQL queries into Flink job graphs, eliminating the need for multiple intermediate Processors and Kafka topics for simple transformations.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Netflix’s public posts do not describe Kafka Connect specifically. The routing layer in Keystone writes to S3 and Elasticsearch using managed Flink/Samza jobs rather than off-the-shelf connectors, suggesting custom integration rather than a standard Connect deployment.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;rack-aware-partition-assignment&quot;&gt;Rack-aware partition assignment&lt;/h3&gt;
&lt;p&gt;Allen Wang contributed Kafka’s rack-aware partition assignment feature upstream to Apache Kafka. The feature assigns partitions to brokers and consumers in the same AWS availability zone where possible, reducing cross-AZ data transfer costs and latency. At Netflix’s scale — thousands of brokers across multiple AZs — the economics of cross-AZ data transfer are material.&lt;/p&gt;
&lt;h3 id=&quot;immutable-small-cluster-design&quot;&gt;Immutable, small-cluster design&lt;/h3&gt;
&lt;p&gt;Netflix deliberately chose many small immutable clusters over one large cluster. In Allen Wang’s words from QCon London 2018, the decision reflects the reality that stateful services are harder to operate in the cloud than stateless ones. Smaller clusters limit the blast radius of failures and make rolling upgrades more predictable. The smart client layer abstracts this topology from application code.&lt;/p&gt;
&lt;h3 id=&quot;external-heartbeat-monitoring-for-kafka&quot;&gt;External heartbeat monitoring for Kafka&lt;/h3&gt;
&lt;p&gt;Netflix’s monitoring service continuously sends heartbeat messages to each Kafka cluster and simultaneously consumes them from the outside. This verifies both the producer path and the consumer path from an external perspective rather than relying solely on broker-internal metrics. Wang presented this pattern at QCon SF 2019 as part of the broader monitoring and tracing infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;inca--sampled-distributed-tracing-on-a-dedicated-kafka-cluster&quot;&gt;Inca — sampled distributed tracing on a dedicated Kafka cluster&lt;/h3&gt;
&lt;p&gt;After evaluating and rejecting Zipkin, Netflix built Inca, a message tracing and loss detection system. Trace signals propagate through Kafka record headers. All trace messages are stored on a dedicated Kafka cluster with replication factor 3, min ISR 2, and EBS-backed storage.&lt;/p&gt;
&lt;p&gt;The stream processing challenge Inca had to solve was unpredictable trace arrival times: trace records for a single request can arrive out of order and at variable delays. Inca uses Flink’s GlobalWindow with custom triggers and treats committed consumer offsets as an external signal to determine when to close windows, rather than relying on event-time watermarks alone. The result is a false-positive loss-detection rate of 0.005%, processing 2 billion trace messages per day.&lt;/p&gt;
&lt;h3 id=&quot;iceberg-as-a-kafka-backfill-source&quot;&gt;Iceberg as a Kafka backfill source&lt;/h3&gt;
&lt;p&gt;When Kafka retention expires, pipelines that need to replay historical data can fall back to Apache Iceberg data warehouse tables. Kafka topic records are persisted to Iceberg, providing a long-retention complement to Kafka’s short-retention window. This pattern is described in the Data Mesh blog post as part of the backfill story.&lt;/p&gt;
&lt;h3 id=&quot;dead-letter-queues-on-every-wal-namespace&quot;&gt;Dead-letter queues on every WAL namespace&lt;/h3&gt;
&lt;p&gt;Netflix’s WAL abstraction automatically provisions a DLQ alongside every Kafka topic it manages. Transient errors are retried with configurable delays; hard errors are parked for manual inspection. The WAL also supports cross-region replication and delay queues for scheduled database writes, without requiring application code to implement these patterns directly.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;Netflix runs Kafka entirely on AWS, with brokers on EC2 instances and EBS storage for the clusters that require durability (such as Inca’s dedicated tracing cluster). The multi-cluster topology is managed by a control-plane service that handles topic creation, partition metadata, and cluster assignment.&lt;/p&gt;
&lt;h3 id=&quot;monitoring-and-observability-1&quot;&gt;Monitoring and observability&lt;/h3&gt;
&lt;p&gt;Kafka operational metrics flow to Atlas, Netflix’s internal dimensional time-series metrics platform, via a custom Kafka MetricReporter. Consumer lag (log end offset minus committed offset per topic-partition) is the primary consumption health signal. SLAs are expressed as message consumption lag, transfer rates, and cross-region replication metrics.&lt;/p&gt;
&lt;p&gt;For end-to-end pipeline health, Netflix uses watchdog monitors that track event propagation from producer to final sink, not just broker-level metrics.&lt;/p&gt;
&lt;h3 id=&quot;auto-remediation&quot;&gt;Auto-remediation&lt;/h3&gt;
&lt;p&gt;When stuck-consumer alerts fire, First Responder automatically relaunches the affected stateless streaming jobs without human intervention. This reduces on-call toil and accelerates mean time to recovery for the most common class of streaming job failure.&lt;/p&gt;
&lt;h3 id=&quot;autoscaling&quot;&gt;Autoscaling&lt;/h3&gt;
&lt;p&gt;Capacity for Flink jobs connected to Kafka is calculated using workload predictions (quadratic or linear regression based on historical throughput) to maintain target processing rates during traffic fluctuations. The Keystone SPaaS reconciliation layer continuously compares desired job state (stored in RDS) against actual state (running Flink jobs) and self-heals divergences.&lt;/p&gt;
&lt;h3 id=&quot;schema-governance&quot;&gt;Schema governance&lt;/h3&gt;
&lt;p&gt;Schema registration is mandatory before any event can be written to a Kafka topic. The internal Schema Registry enforces this at the platform level. Teams cannot publish to Kafka without a registered Avro schema, which prevents malformed events from entering the pipeline and creates a centralised contract for every data stream.&lt;/p&gt;
&lt;h3 id=&quot;retention-policies&quot;&gt;Retention policies&lt;/h3&gt;
&lt;p&gt;Retention is set per topic based on throughput and record size. The general approach balances data availability for replay against storage cost. For use cases where data needs to outlast Kafka retention, Iceberg tables serve as the long-term store.&lt;/p&gt;
&lt;h3 id=&quot;developer-experience&quot;&gt;Developer experience&lt;/h3&gt;
&lt;p&gt;The Streaming SQL layer introduced in 2023 was explicitly designed to lower the bar for non-infrastructure teams. Engineers can write SQL queries to describe transformations; the platform translates them into Flink job graphs. Within a year, teams outside the data infrastructure organisation had created 1,200 SQL processors, indicating that the abstraction achieved meaningful adoption without requiring platform expertise.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;high-level-consumer-partition-loss-2016&quot;&gt;High-level consumer partition loss (2016)&lt;/h3&gt;
&lt;p&gt;In Keystone V1.5, Netflix used Kafka’s high-level consumer API and observed a known bug where the consumer could lose partition ownership and stop consuming some partitions after running stably for a period. The immediate symptom was data loss, not an error. The solution was moving to Keystone V2’s managed routing service with direct partition assignment and monitoring, replacing the high-level consumer with a more explicitly managed consumption model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Partition ownership silently dropped under long-running high-level consumers.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; A known Kafka high-level consumer bug at the time. &lt;strong&gt;Solution:&lt;/strong&gt; Direct partition assignment in the V2 routing layer.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Partition ownership became explicit and monitorable.&lt;/p&gt;
&lt;h3 id=&quot;data-loss-from-low-durability-configurations&quot;&gt;Data loss from low-durability configurations&lt;/h3&gt;
&lt;p&gt;Allen Wang identified three categories of data loss in Netflix’s production Kafka environment, described at QCon SF 2019:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Replication factor of 2 combined with &lt;code&gt;acks=1&lt;/code&gt; producer configuration. When two brokers hold a partition and the leader fails before replication completes, writes are lost.&lt;/li&gt;
&lt;li&gt;Partition leader clock drift causing unexpected log truncation in extreme conditions.&lt;/li&gt;
&lt;li&gt;Deployment tooling assigning duplicate consumer group IDs, causing two independent consumer applications to share offsets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each was addressed through configuration changes and tooling improvements. The lesson Wang drew: data loss in Kafka rarely comes from Kafka’s own logic — it comes from the configuration choices made around it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Unexplained data loss in production, hard to attribute to a single cause.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; Three distinct configuration and tooling mistakes rather than a single failure mode.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; Higher replication factor, &lt;code&gt;acks=all&lt;/code&gt; for critical topics, unique consumer group ID enforcement.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Data loss incidents reduced; tracing infrastructure (Inca) built to detect any residual loss.&lt;/p&gt;
&lt;h3 id=&quot;consumer-fan-out-at-scale&quot;&gt;Consumer fan-out at scale&lt;/h3&gt;
&lt;p&gt;As the number of downstream consumer groups grew, the original single-cluster design could not efficiently serve hundreds of groups with different lag tolerances and throughput requirements. Producers and consumers competed for broker resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Consumer fan-out degraded ingest performance.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; No isolation between the write path and the read path.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; Two-tier cluster topology: fronting clusters for producers, consumer clusters for consumers, with smart clients routing transparently.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Independent scaling of ingest and consumption capacity.&lt;/p&gt;
&lt;h3 id=&quot;late-arriving-events-in-stream-processing-2017&quot;&gt;Late-arriving events in stream processing (2017)&lt;/h3&gt;
&lt;p&gt;When Netflix migrated batch ETL to Flink and Kafka for recommendation model training, late-arriving events — those arriving after their intended event-time window — caused incorrect attribution in the model inputs. The Kafka TTL was 4-6 hours, which was shorter than the arrival delay for some events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Late events attributed to the wrong time window, corrupting model training inputs.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; Event-time windows closed before all relevant events had arrived, and Kafka TTL was shorter than the late-arrival tail.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; Time windowing with a post-processing pass to correctly attribute late events; raw events stored in HDFS for 1-2 days beyond Kafka TTL for replay.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Recommendation model training inputs became accurate and replayable. Shriya Arora presented this at QCon New York 2017.&lt;/p&gt;
&lt;h3 id=&quot;microservice-dependency-brittleness-content-finance&quot;&gt;Microservice dependency brittleness (Content Finance)&lt;/h3&gt;
&lt;p&gt;Netflix Content Finance services experienced cascading failures and inconsistent state when upstream synchronous dependencies became unavailable. Split-brain state between services required manual reconciliation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Synchronous RPC calls created hard dependencies between services.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; No buffering or retry layer between producers and consumers of data changes.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; Kafka-based event-driven architecture with standardised event envelopes, at-least-once delivery, idempotent consumers, and reconciliation events for replay. Flink enriches and reorders events before routing to keyed topics consumed by Spring Boot services.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Services became independently deployable and resilient to upstream failures. Nitin Sharma presented this at Kafka Summit SF 2019.&lt;/p&gt;
&lt;h3 id=&quot;tudum-cache-invalidation-lag-2025&quot;&gt;Tudum cache invalidation lag (2025)&lt;/h3&gt;
&lt;p&gt;The Tudum fan site used a CQRS pattern with Kafka to decouple CMS ingestion from the Cassandra read database. An ingestion service published read-optimised content to a Kafka topic; a page data service consumed it for storage.&lt;/p&gt;
&lt;p&gt;In practice, the CQRS-with-Kafka architecture introduced cache refresh delays. Editors could not preview content updates for up to 60 seconds per key, which created operational friction for a content-heavy site where updates are frequent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Cache invalidation lag of up to 60 seconds per key.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; CQRS overhead was not justified by the write volume — Tudum is a read-heavy, low-write-volume site.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; Replaced Kafka and CQRS with RAW Hollow, Netflix’s in-memory object store, which handles the read-heavy pattern more directly.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Simplified architecture with lower latency for content updates. This is a notable documented case where Netflix actively chose to move away from Kafka when a simpler alternative was more appropriate for the use case. Eugene Yemelyanau and Jake Grice documented this on the Netflix TechBlog in July 2025.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Central message bus for event ingest, inter-service messaging, Data Mesh connective tissue, WAL queue, and monitoring event transport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;All stateless and stateful jobs in Keystone SPaaS and Data Mesh; window sizes from seconds to hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Mandatory serialisation format for all Kafka events; ~3-5x smaller than JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Internal Netflix Schema Registry&lt;/td&gt;
&lt;td&gt;Schema registration required before any event can be written; enforces schema-at-producer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;Amazon S3, Apache Iceberg&lt;/td&gt;
&lt;td&gt;S3 for cold storage and raw event backup; Iceberg for structured backfill when Kafka retention has expired&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database / profile store&lt;/td&gt;
&lt;td&gt;Apache Cassandra&lt;/td&gt;
&lt;td&gt;Member taste profile storage, fed by Flink jobs consuming Kafka streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search index&lt;/td&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;Search index sink from Keystone routing; also used in Netflix Studio Search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time analytics&lt;/td&gt;
&lt;td&gt;Apache Druid&lt;/td&gt;
&lt;td&gt;Used alongside Kafka in the live streaming monitoring stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational stream processing&lt;/td&gt;
&lt;td&gt;Mantis&lt;/td&gt;
&lt;td&gt;Netflix’s reactive stream processing platform; used in live streaming monitoring stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics platform&lt;/td&gt;
&lt;td&gt;Atlas&lt;/td&gt;
&lt;td&gt;Netflix’s internal dimensional time-series metrics platform; receives Kafka operational metrics via custom MetricReporter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed tracing&lt;/td&gt;
&lt;td&gt;Inca&lt;/td&gt;
&lt;td&gt;Netflix-built message tracing and loss detection on a dedicated Kafka cluster; false-positive detection rate 0.005%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job state / reconciliation&lt;/td&gt;
&lt;td&gt;Amazon RDS&lt;/td&gt;
&lt;td&gt;Single source of truth for Keystone SPaaS job desired state; drives reconciliation protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata / coordination&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;Kafka cluster metadata management in the Keystone era&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Spinnaker&lt;/td&gt;
&lt;td&gt;Deployment orchestration for Flink jobs and Kafka-connected services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternative message queue&lt;/td&gt;
&lt;td&gt;Amazon SQS&lt;/td&gt;
&lt;td&gt;Used alongside Kafka in the WAL system for specific namespace configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer application framework&lt;/td&gt;
&lt;td&gt;Spring Boot, Spring Cloud Kafka&lt;/td&gt;
&lt;td&gt;Consumer application framework used in Content Finance Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-memory object store&lt;/td&gt;
&lt;td&gt;RAW Hollow&lt;/td&gt;
&lt;td&gt;Replaced Kafka-based CQRS for Tudum — more suitable for read-heavy, low-write workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Allen Wang&lt;/td&gt;
&lt;td&gt;Architect, Real Time Data Infrastructure, Netflix&lt;/td&gt;
&lt;td&gt;Architected multi-cluster Kafka infrastructure; contributed rack-aware partition assignment to Apache Kafka; spoke at Surge 2016, QCon London 2018, QCon SF 2019, Kafka Summit SF 2017; authored Inca blog post (2019)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monal Daxini&lt;/td&gt;
&lt;td&gt;Lead, Stream Processing, Real Time Data Infrastructure, Netflix&lt;/td&gt;
&lt;td&gt;Led Keystone SPaaS; presented at Flink Forward 2016, Flink Forward 2017, AWS re:Invent 2017; co-authored Evolution of the Netflix Data Pipeline (2016)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shriya Arora&lt;/td&gt;
&lt;td&gt;Senior Data Engineer, Netflix&lt;/td&gt;
&lt;td&gt;Presented “Migrating Batch ETL to Stream Processing” at QCon New York 2017; described late-arriving event handling and Kafka/Flink migration rationale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nitin Sharma&lt;/td&gt;
&lt;td&gt;Content Finance Infrastructure, Netflix&lt;/td&gt;
&lt;td&gt;Authored “How Netflix Uses Kafka for Distributed Streaming” (Confluent blog, 2020); presented “Eventing Things — A Netflix Original!” at Kafka Summit SF 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zhenzhong Xu&lt;/td&gt;
&lt;td&gt;Founding engineer, Real Time Data Infrastructure; later led Stream Processing Engines, Netflix&lt;/td&gt;
&lt;td&gt;Joined 2015; co-authored Evolution of the Netflix Data Pipeline (2016); published “The Four Innovation Phases of Netflix’s Trillions Scale Real-time Data Infrastructure” (Medium, 2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prudhviraj Karumanchi and Vidhya Arvind&lt;/td&gt;
&lt;td&gt;Staff Software Engineers, Data Platform, Netflix&lt;/td&gt;
&lt;td&gt;Co-authored “Building a Resilient Data Platform with Write-Ahead Log at Netflix” (September 2025); presented at QCon SF 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eugene Yemelyanau and Jake Grice&lt;/td&gt;
&lt;td&gt;Technology Evangelist / Staff Engineer, Netflix&lt;/td&gt;
&lt;td&gt;Co-authored “Netflix Tudum Architecture: from CQRS with Kafka to CQRS with RAW Hollow” (July 2025)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;p&gt;Netflix’s Kafka architecture offers several decisions worth considering when you are planning or scaling your own implementation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Separate your ingest and consumption clusters early.&lt;/strong&gt; Netflix’s split between fronting clusters (producers) and consumer clusters (consumers) addressed a real problem: consumer fan-out degraded producer ingest. If your consumer group count is growing, this separation is worth evaluating before it becomes a performance issue rather than after.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat configuration choices as a first-class durability concern.&lt;/strong&gt; Allen Wang’s account of data loss at Netflix is instructive because none of the three root causes were Kafka bugs. They were configuration and tooling decisions: low replication factor, weak ack settings, and duplicate consumer group IDs. Auditing these is lower-effort than building additional reliability infrastructure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce schema-at-producer, not schema-at-consumer.&lt;/strong&gt; Requiring all events to use registered Avro schemas before they reach a topic prevents malformed data from entering the pipeline. Discovering schema issues downstream is significantly more expensive than preventing them at the source.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build external health checks, not only internal ones.&lt;/strong&gt; Netflix’s heartbeat monitoring, which writes and reads from every Kafka cluster via an external service, verifies both the producer and consumer paths independently of broker metrics. Broker health and pipeline health are not the same thing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revisit architectural choices when use case assumptions change.&lt;/strong&gt; Netflix’s decision to replace Kafka-based CQRS on Tudum with an in-memory store is a useful reminder that Kafka is not always the right tool. The team documented that CQRS overhead was not justified by the actual write volume. Matching the architecture to the access pattern is more important than consistency for its own sake.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Allen Wang, Monal Daxini et al., “Evolution of the Netflix Data Pipeline” — &lt;a href=&quot;https://netflixtechblog.com/evolution-of-the-netflix-data-pipeline-da246ca36905&quot;&gt;https://netflixtechblog.com/evolution-of-the-netflix-data-pipeline-da246ca36905&lt;/a&gt; (2016)&lt;/li&gt;
&lt;li&gt;Netflix TechBlog, “Kafka Inside Keystone Pipeline” — &lt;a href=&quot;https://netflixtechblog.com/kafka-inside-keystone-pipeline-dd5aeabaf6bb&quot;&gt;https://netflixtechblog.com/kafka-inside-keystone-pipeline-dd5aeabaf6bb&lt;/a&gt; (2016)&lt;/li&gt;
&lt;li&gt;Allen Wang, “Cloud-Native and Scalable Kafka Architecture” (QCon London 2018, InfoQ writeup) — &lt;a href=&quot;https://www.infoq.com/presentations/cloud-native-kafka-netflix/&quot;&gt;https://www.infoq.com/presentations/cloud-native-kafka-netflix/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Allen Wang, “Monitoring and Tracing @Netflix Streaming Data Infrastructure” (QCon SF 2019, InfoQ writeup) — &lt;a href=&quot;https://www.infoq.com/presentations/netflix-streaming-data-infrastructure/&quot;&gt;https://www.infoq.com/presentations/netflix-streaming-data-infrastructure/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Allen Wang, “Inca — Message Tracing and Loss Detection For Streaming Data @Netflix” — &lt;a href=&quot;https://netflixtechblog.medium.com/inca-message-tracing-and-loss-detection-for-streaming-data-netflix-de4836fc38c9&quot;&gt;https://netflixtechblog.medium.com/inca-message-tracing-and-loss-detection-for-streaming-data-netflix-de4836fc38c9&lt;/a&gt; (2019)&lt;/li&gt;
&lt;li&gt;Shriya Arora, “Migrating Batch ETL to Stream Processing at Netflix” (QCon NY 2017, InfoQ) — &lt;a href=&quot;https://www.infoq.com/articles/netflix-migrating-stream-processing/&quot;&gt;https://www.infoq.com/articles/netflix-migrating-stream-processing/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Netflix TechBlog, “Keystone Real-time Stream Processing Platform” (InfoQ) — &lt;a href=&quot;https://www.infoq.com/news/2018/09/Netflix-Keystone-Real-Time-Proc/&quot;&gt;https://www.infoq.com/news/2018/09/Netflix-Keystone-Real-Time-Proc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nitin Sharma, “How Netflix Uses Kafka for Distributed Streaming” (Confluent blog) — &lt;a href=&quot;https://www.confluent.io/blog/how-kafka-is-used-by-netflix/&quot;&gt;https://www.confluent.io/blog/how-kafka-is-used-by-netflix/&lt;/a&gt; (2020)&lt;/li&gt;
&lt;li&gt;Nitin Sharma, “Eventing Things — A Netflix Original!” (Kafka Summit SF 2019) — &lt;a href=&quot;https://www.slideshare.net/slideshow/eventing-things-a-netflix-original-nitin-sharma-netflix-kafka-summit-sf-2019-179806392/179806392&quot;&gt;https://www.slideshare.net/slideshow/eventing-things-a-netflix-original-nitin-sharma-netflix-kafka-summit-sf-2019-179806392/179806392&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Bo Lei et al., “Data Mesh — A Data Movement and Processing Platform @ Netflix” — &lt;a href=&quot;https://netflixtechblog.com/data-mesh-a-data-movement-and-processing-platform-netflix-1288bcab2873&quot;&gt;https://netflixtechblog.com/data-mesh-a-data-movement-and-processing-platform-netflix-1288bcab2873&lt;/a&gt; (2022)&lt;/li&gt;
&lt;li&gt;Guil Pires et al., “Streaming SQL in Data Mesh” — &lt;a href=&quot;https://netflixtechblog.com/streaming-sql-in-data-mesh-0d83f5a00d08&quot;&gt;https://netflixtechblog.com/streaming-sql-in-data-mesh-0d83f5a00d08&lt;/a&gt; (2023)&lt;/li&gt;
&lt;li&gt;Eugene Yemelyanau and Jake Grice, “Netflix Tudum Architecture: from CQRS with Kafka to CQRS with RAW Hollow” (InfoQ) — &lt;a href=&quot;https://www.infoq.com/news/2025/08/netflix-tudum-cqrs-raw-hollow/&quot;&gt;https://www.infoq.com/news/2025/08/netflix-tudum-cqrs-raw-hollow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Prudhviraj Karumanchi and Vidhya Arvind, “Building a Resilient Data Platform with Write-Ahead Log at Netflix” (InfoQ) — &lt;a href=&quot;https://www.infoq.com/news/2025/10/netflix-wal-resilience/&quot;&gt;https://www.infoq.com/news/2025/10/netflix-wal-resilience/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adrian Taruc and James Dalton, “How and Why Netflix Built a Real-Time Distributed Graph” — &lt;a href=&quot;https://netflixtechblog.com/how-and-why-netflix-built-a-real-time-distributed-graph-part-1-ingesting-and-processing-data-80113e124acc&quot;&gt;https://netflixtechblog.com/how-and-why-netflix-built-a-real-time-distributed-graph-part-1-ingesting-and-processing-data-80113e124acc&lt;/a&gt; (2025)&lt;/li&gt;
&lt;li&gt;InfoQ, “From On-Demand to Live: How Netflix Built a Real-Time Monitoring Pipeline” — &lt;a href=&quot;https://www.infoq.com/news/2025/12/netflix-live-streaming-pipeline/&quot;&gt;https://www.infoq.com/news/2025/12/netflix-live-streaming-pipeline/&lt;/a&gt; (2025)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;kpow&quot;&gt;Kpow&lt;/h3&gt;
&lt;p&gt;If you are operating Kafka at scale and want full visibility into consumer lag, topic throughput, and broker health from a single interface, give &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; a try. You can connect it to any Kafka cluster in minutes and get a free 30-day trial with full access — deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How New Relic uses Apache Kafka in production</title><link>https://factorhouse.io/articles/new-relic-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/new-relic-kafka-architecture/</guid><description>A deep-dive into New Relic&apos;s Kafka architecture — covering use cases, scale, engineering decisions and key contributors.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;New Relic processes more than 15 million messages per second across an aggregate data rate approaching 1 Tbps, routing every telemetry data point through &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; from the moment it arrives to the moment it becomes queryable. Across more than 100 independent clusters, Kafka carries the full pipeline: ingestion, transformation, aggregation, and storage.&lt;/p&gt;
&lt;p&gt;The engineering challenge New Relic solved was not just throughput. It was building a data pipeline that could absorb billions of data points per minute, isolate failures to a small portion of customers, and scale horizontally without rebuilding from scratch.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;New Relic is an observability platform used by engineers to monitor application performance, infrastructure, and distributed systems. The platform ingests data from agents running in customer environments and stores it in NRDB (New Relic Database), a proprietary time-series store capable of scanning trillions of events with a median query response time of around 60 milliseconds.&lt;/p&gt;
&lt;p&gt;New Relic has been building on Kafka since at least 2018, when principal software engineer Amy Boyle published the company’s first detailed account of how Kafka connected the platform’s microservices and event processing pipelines. At that stage, the platform ran a single Kafka cluster in its own data centre, with thousands of broker nodes. By 2020, that cluster had reached the limits of horizontal scaling, which triggered a migration to Amazon Web Services and a shift to a cell-based architecture that now underpins the platform’s fault isolation model.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Amy Boyle publishes the first public engineering account of New Relic’s Kafka architecture, covering event processing, the changelog pattern, and the durable cache pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;New Relic begins migrating from a single on-premises Kafka cluster (275+ brokers) to a cell-based architecture on AWS using Amazon MSK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Migration completes; 95% of data ingestion moved to AWS, with more than 100 independent Kafka clusters now in operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021-07-29&lt;/td&gt;
&lt;td&gt;Major incident: a Kafka broker failure, conflicting manual and automated recovery, and a retention change propagated across all cells causes a multi-hour data collection and alerting outage for US-region customers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-04&lt;/td&gt;
&lt;td&gt;Anton Rodriguez presents the company’s eBPF-based Kafka monitoring approach at Kafka Summit London&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-01&lt;/td&gt;
&lt;td&gt;Tony Mancill’s Kafka best practices article updated, citing 15 million messages/sec and an aggregate data rate approaching 1 Tbps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;new-relics-kafka-use-cases&quot;&gt;New Relic’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Telemetry data bus for NRDB&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka serves as the primary data bus for New Relic’s Telemetry Data Platform. As described by Nic Benders, GVP and Chief Architect, Kafka “carries all customer data through each stage of our processing, evaluation, and storage pipeline.” Multiple downstream services aggregate, normalise, decorate, and transform telemetry data as it moves through Kafka before reaching NRDB. In normal operation, Kafka also absorbs backpressure: if a downstream stage slows down, Kafka buffers incoming data without dropping it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event data processing - Events Pipeline team&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Events Pipeline team handles fine-grained monitoring records: application errors, page views, and e-commerce transactions. Each record flows through a chain of containerised processing services connected via Kafka topics, passing through parsing, query matching, and aggregation stages in sequence. The team owns the partitioning strategy, consumer assignment configuration, and aggregation pipeline design for this data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microservice coordination across 40+ engineering teams&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;New Relic’s platform is built by more than 40 engineering teams. Kafka decouples these teams through asynchronous messaging, allowing services to produce and consume data independently without tight synchronous dependencies between producers and consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Time to glass” pipeline&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Engineering teams on the Telemetry Data Platform track “time to glass”: how long it takes for a data point ingested by a customer agent to become queryable. Kafka connects each processing stage in this chain, and the pipeline is instrumented with OpenTelemetry distributed tracing to measure latency at each hop.&lt;/p&gt;
&lt;h2 id=&quot;scale--throughput&quot;&gt;Scale &amp;amp; throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Messages per second&lt;/td&gt;
&lt;td&gt;More than 15 million&lt;/td&gt;
&lt;td&gt;Tony Mancill, New Relic engineering blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate data rate&lt;/td&gt;
&lt;td&gt;Approaching 1 Tbps&lt;/td&gt;
&lt;td&gt;Tony Mancill, New Relic engineering blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data points per minute ingested into NRDB&lt;/td&gt;
&lt;td&gt;Over 3 billion&lt;/td&gt;
&lt;td&gt;Nic Benders; Wendy Shepperd, New Relic engineering blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data volume per month&lt;/td&gt;
&lt;td&gt;150 PB (at migration, 2021); 200 PB (later post)&lt;/td&gt;
&lt;td&gt;Wendy Shepperd; Daniel Kim, New Relic engineering blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka clusters in operation&lt;/td&gt;
&lt;td&gt;More than 100&lt;/td&gt;
&lt;td&gt;Anton Rodriguez, Confluent podcast / Kafka Summit London 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brokers in original single cluster&lt;/td&gt;
&lt;td&gt;Over 275&lt;/td&gt;
&lt;td&gt;Anton Rodriguez, Confluent podcast / Kafka Summit London 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Since the AWS migration, clusters are scoped by cell. Each cell contains one Kafka cluster alongside the ingestion service, pipeline services, and NRDB cluster it serves. Customer event data is partitioned by account to support efficient per-customer data locality within each cluster.&lt;/p&gt;
&lt;h2 id=&quot;new-relics-kafka-architecture&quot;&gt;New Relic’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;from-monolith-to-cells&quot;&gt;From monolith to cells&lt;/h3&gt;
&lt;p&gt;Before 2020, New Relic ran its Kafka workload in a single cluster in its own data centre. As described by Wendy Shepperd, GVP of Engineering, the company had “a massive single Kafka cluster in our data center with thousands of nodes processing data.” That cluster could not scale further horizontally.&lt;/p&gt;
&lt;p&gt;The architectural response was to decompose the platform into independent cells, each a self-contained unit: one Kafka cluster, one ingestion service, one set of data pipeline services, and one NRDB cluster. With approximately 10 cells in the US region, a failure in one cell affects roughly 10% of customers rather than the full region. The 2021 incident (described below) revealed that infrastructure automation had not fully respected cell boundaries, resulting in a configuration change propagating across all cells simultaneously.&lt;/p&gt;
&lt;h3 id=&quot;amazon-msk&quot;&gt;Amazon MSK&lt;/h3&gt;
&lt;p&gt;New Relic migrated to Amazon Managed Streaming for Apache Kafka (MSK) for its cell clusters as part of the AWS transition. Containerised consumer services run on Amazon EKS within each cell. The migration moved 95% of data ingestion to AWS in under one year.&lt;/p&gt;
&lt;h3 id=&quot;partition-model&quot;&gt;Partition model&lt;/h3&gt;
&lt;p&gt;For permanent event storage, data is “partitioned by which customer account the data belongs to,” supporting efficient per-account retrieval. For the CPU-intensive match service - which must hold all registered queries in memory - random partitioning is used to distribute load evenly across consumer instances without per-key routing logic.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;The Events Pipeline team manages partition assignment configuration to minimise the cost of rebalances on stateful consumers. StickyAssignor reduces state shuffling during rebalances. CooperativeStickyAssignor (available from Kafka 2.4) allows consumers to continue processing during a rebalance rather than stopping entirely. Static Membership (available from Kafka 2.3) avoids triggering rebalances when clients consistently identify themselves across restarts.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques--engineering-innovations&quot;&gt;Special techniques &amp;amp; engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Changelog pattern for stateful service startup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Services that maintain in-memory state consume their Kafka topic from the earliest offset on startup, replaying history to reconstruct state. The Queries topic uses a short TTL-based retention window of one hour to keep the replay time bounded. An early misconfiguration - &lt;code&gt;segment.ms&lt;/code&gt; not aligned with &lt;code&gt;retention.ms&lt;/code&gt; - caused the topic to take minutes to read on startup. Aligning the two values resolved the issue immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Durable cache” pattern&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Replaying large volumes of ingest data on every service restart was not practical at New Relic’s scale. The solution was to introduce a “durable cache” pattern: stateful services periodically snapshot their in-memory state to a dedicated Kafka topic (a “snapshots” topic with the same partition count as the primary topic). On restart, a service reads the snapshot, then resumes consuming from the offset stored in the snapshot metadata, bypassing the need to replay the primary topic from the beginning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LMAX Disruptor for ingest concurrency&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;New Relic uses the LMAX Disruptor - “an asynchronous blocking queue that distributes objects to worker threads” - in its ingest consumers to parallelise decompression, deserialisation, and business logic across threads. Each worker thread handles one partition, preserving single-threaded ordering guarantees per partition while maximising CPU utilisation across cores.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two-stage aggregation for hotspot resolution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When the Events Pipeline team discovered that the top 1.5% of customer queries accounted for approximately 90% of processed events, it became clear that partitioning the aggregation topic by query ID was creating severe load imbalance. The solution was to split the aggregation service into two stages. Stage one uses random partitioning, distributing all events evenly across consumer instances for parallel partial aggregation. Stage two partitions by query ID, merging the partial results into final outputs. This arrangement significantly condenses stream volume before the final keyed step, eliminating the hotspot problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;eBPF-based Kafka monitoring via Pixie&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With more than 100 Kafka clusters and services written in multiple languages, deploying per-service Kafka instrumentation was impractical. Anton Rodriguez, Principal Software Engineer, presented New Relic’s solution at Kafka Summit London 2022: Pixie, a CNCF open-source project that uses eBPF (Extended Berkeley Packet Filter) to observe Kafka traffic at the Linux kernel network layer without any application-level code changes. Pixie measures consumer lag in milliseconds rather than offset counts, identifies rebalancing events through control message analysis, and works across consumer languages on Kubernetes. The approach gives New Relic uniform observability across its full cluster estate without per-service instrumentation overhead.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;New Relic runs Kafka on Amazon MSK, with one MSK cluster per cell. Each cell is an isolated unit: its own MSK cluster, ingestion frontend, processing services, and NRDB cluster. Consumer services run as containerised workloads on Amazon EKS within the same cell.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and observability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Consumer lag is monitored in milliseconds via Pixie, providing a time-based view of pipeline health rather than a raw offset count. The full Telemetry Data Platform pipeline is instrumented with OpenTelemetry distributed tracing to track data from agent ingestion through each Kafka-connected transformation stage to the point it becomes queryable in NRDB. This “time to glass” metric gives engineers a latency view across the entire processing chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Incident response&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The July 2021 incident led to a public retrospective in which New Relic identified five compounding failure points in its incident response tooling and automation: no safety guardrails in emergency tools to prevent dangerous configuration changes; human error under high-pressure conditions; false confidence built up from past tool use; alert noise masking critical disk-space warnings; and automation scope that allowed configuration changes to propagate beyond the affected cell. Post-incident commitments included adding guardrails to tooling and restricting the blast radius of automation to individual cells.&lt;/p&gt;
&lt;h2 id=&quot;challenges--how-they-solved-them&quot;&gt;Challenges &amp;amp; how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Horizontal scaling limit on the monolithic cluster&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Problem: The original single on-premises Kafka cluster, with thousands of broker nodes, could not scale further horizontally.&lt;/p&gt;
&lt;p&gt;Root cause: A monolithic cluster design with no workload isolation between data types, teams, or customers.&lt;/p&gt;
&lt;p&gt;Solution: Migration to AWS using Amazon MSK, replacing the single cluster with more than 100 cell-level clusters.&lt;/p&gt;
&lt;p&gt;Outcome: 95% of data ingestion moved to AWS in under one year. Each new cell adds capacity independently, and failures are isolated to approximately 10% of customers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;July 2021: broker failure cascades across cells&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Problem: A Kafka broker in one US-region cell became unresponsive. Engineers manually initiated a restart while automated remediation triggered simultaneously, extending the degradation window. To preserve data during recovery, engineers extended Kafka retention settings using internal tooling. The retention change was silently applied to all cells by the infrastructure automation, exhausting disk space across the platform. A disk-space alert fired but was missed amid simultaneous unrelated alerts.&lt;/p&gt;
&lt;p&gt;Root cause: Five compounding failures: no safety guardrails in emergency tooling; human error under stress; false confidence from prior tool use; alert noise masking a critical signal; and automation scope that bypassed cell isolation.&lt;/p&gt;
&lt;p&gt;Outcome: Multi-hour data collection and alerting outage for US-region customers. New Relic published a detailed retrospective committing to tooling and automation improvements. Source: Nic Benders, newrelic.com/blog/best-practices/new-relic-reliability-incident-learning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition hotspots in the aggregation pipeline&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Problem: The top 1.5% of customer queries accounted for approximately 90% of processed events. Partitioning the aggregation topic by query ID concentrated this workload on a small number of partitions, causing load imbalance.&lt;/p&gt;
&lt;p&gt;Root cause: Uneven distribution of query weight in the customer base.&lt;/p&gt;
&lt;p&gt;Solution: Two-stage aggregation pipeline - random partitioning for partial aggregation, then query-ID partitioning for final merge.&lt;/p&gt;
&lt;p&gt;Outcome: Hot partitions eliminated; stream volume significantly reduced before the final keyed aggregation stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag measurement across 100+ clusters&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Problem: Instrumenting consumer lag across more than 100 clusters, served by services in multiple languages, was not scalable.&lt;/p&gt;
&lt;p&gt;Root cause: Scale and heterogeneity of the cluster estate and application stack.&lt;/p&gt;
&lt;p&gt;Solution: eBPF via Pixie, observing Kafka traffic at the kernel network layer without application changes.&lt;/p&gt;
&lt;p&gt;Outcome: Consumer lag reported in milliseconds across all clusters and languages; rebalancing events detected from control messages; no per-service instrumentation required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log segment misconfiguration causing slow topic consumption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Problem: Applications consuming the Queries topic took minutes to read it on startup, creating unacceptably long initialisation times.&lt;/p&gt;
&lt;p&gt;Root cause: &lt;code&gt;segment.ms&lt;/code&gt; was not aligned with &lt;code&gt;retention.ms&lt;/code&gt;, causing Kafka to create large log segments that contained mostly expired data but still had to be scanned.&lt;/p&gt;
&lt;p&gt;Solution: Aligned &lt;code&gt;segment.ms&lt;/code&gt; to match &lt;code&gt;retention.ms&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Outcome: Consumption time reduced from minutes to a manageable window.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Core data bus for NRDB and all inter-service telemetry routing; one cluster per cell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed Kafka&lt;/td&gt;
&lt;td&gt;Amazon MSK&lt;/td&gt;
&lt;td&gt;Kafka hosting for all cell clusters on AWS, adopted during the 2020 migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Amazon EKS&lt;/td&gt;
&lt;td&gt;Kubernetes platform for containerised Kafka consumers and data pipeline services within each cell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka monitoring&lt;/td&gt;
&lt;td&gt;Pixie (CNCF)&lt;/td&gt;
&lt;td&gt;eBPF-based consumer lag and rebalance monitoring across 100+ clusters without per-service instrumentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed tracing&lt;/td&gt;
&lt;td&gt;OpenTelemetry&lt;/td&gt;
&lt;td&gt;Traces data from agent ingestion through all Kafka-connected processing stages to NRDB for “time to glass” measurement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency library&lt;/td&gt;
&lt;td&gt;LMAX Disruptor&lt;/td&gt;
&lt;td&gt;Asynchronous blocking queue used by ingest consumers to parallelise decompression, deserialisation, and business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;NRDB&lt;/td&gt;
&lt;td&gt;Proprietary time-series database; downstream destination for all Kafka-processed telemetry; stores metrics, events, logs, and traces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / Team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amy Boyle&lt;/td&gt;
&lt;td&gt;Principal Software Engineer, New Relic&lt;/td&gt;
&lt;td&gt;Authored the primary engineering posts on event processing architecture (changelog pattern, durable cache, LMAX Disruptor) and partitioning strategies (two-stage aggregation, hotspot resolution, assignor selection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tony Mancill&lt;/td&gt;
&lt;td&gt;Lead Software Engineer, data ingest team, New Relic&lt;/td&gt;
&lt;td&gt;Authored “20 best practices for Apache Kafka at scale,” drawing on production experience at 15 million messages/sec; primary source for throughput figures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anton Rodriguez&lt;/td&gt;
&lt;td&gt;Principal Software Engineer, New Relic&lt;/td&gt;
&lt;td&gt;Presented “Monitoring Kafka Without Instrumentation Using eBPF” at Kafka Summit London 2022; primary source for cluster count (100+), broker count (275+), and the Pixie monitoring approach&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nic Benders&lt;/td&gt;
&lt;td&gt;GVP and Chief Architect, New Relic&lt;/td&gt;
&lt;td&gt;Authored the July 2021 incident retrospective; primary source for cell architecture design and Kafka’s role as the platform data bus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wendy Shepperd&lt;/td&gt;
&lt;td&gt;GVP of Engineering, New Relic&lt;/td&gt;
&lt;td&gt;Primary source for the AWS migration: original monolithic cluster constraints, cell architecture shift, Amazon MSK adoption, and the 150 PB/month scale figure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daniel Kim&lt;/td&gt;
&lt;td&gt;Principal Developer Relations Engineer, New Relic&lt;/td&gt;
&lt;td&gt;Authored the distributed tracing with OpenTelemetry post; source for the “time to glass” metric and the 200 PB/month data volume figure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cell architecture changes the failure model.&lt;/strong&gt; New Relic’s shift from a single cluster to 100+ cell-scoped clusters reduced the blast radius of any single failure from 100% of customers to around 10%. If you are running a monolithic cluster and approaching scaling limits, decomposing by workload, region, or customer segment can give you both operational leverage and a clearer capacity model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State management at ingest scale requires an explicit design.&lt;/strong&gt; New Relic developed two patterns - the changelog pattern for small, bounded state and the durable cache pattern for large, unbounded state - because the naive approach of replaying the full topic backlog on restart became impractical. If your Kafka consumers maintain in-memory state, these are worth evaluating before you hit scale limits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partition hotspots are a distribution problem, not a Kafka problem.&lt;/strong&gt; The Events Pipeline team’s discovery that 1.5% of queries drove 90% of events required a two-stage aggregation design rather than a configuration change. When you see uneven partition load, the partition key is usually the lever to adjust, and two-stage approaches (random partitioning for distribution, then keyed partitioning for correctness) are a repeatable solution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instrumentation at scale may require kernel-level approaches.&lt;/strong&gt; Once New Relic crossed 100 clusters across heterogeneous service languages, per-service Kafka instrumentation was no longer a viable path. eBPF via Pixie gave uniform coverage without application changes. If you are managing Kafka at a similar scale, it is worth evaluating whether observability approaches that bypass the application layer are better suited to your environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retention changes during incidents carry hidden risk.&lt;/strong&gt; New Relic’s 2021 post-mortem is a useful reference for any team that uses emergency tooling to modify Kafka configuration under pressure. Extending retention to preserve data is a reasonable instinct, but the interaction between retention settings and available disk space, compounded by automation that did not respect cell boundaries, turned a contained failure into a region-wide outage. Guardrails in operational tooling and well-scoped automation blast radius are worth building before you need them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources--further-reading&quot;&gt;Sources &amp;amp; further reading&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Author&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Using Apache Kafka for Real-Time Event Processing at New Relic&lt;/td&gt;
&lt;td&gt;Amy Boyle, New Relic&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Effective strategies for Kafka topic partitioning&lt;/td&gt;
&lt;td&gt;Amy Boyle, New Relic&lt;/td&gt;
&lt;td&gt;Updated 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;20 best practices for Apache Kafka at scale&lt;/td&gt;
&lt;td&gt;Tony Mancill, New Relic&lt;/td&gt;
&lt;td&gt;Updated January 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Monitoring extreme-scale Apache Kafka using eBPF at New Relic&lt;/td&gt;
&lt;td&gt;Anton Rodriguez, New Relic&lt;/td&gt;
&lt;td&gt;Kafka Summit London, April 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Our commitment to reliability and incident learning&lt;/td&gt;
&lt;td&gt;Nic Benders, New Relic&lt;/td&gt;
&lt;td&gt;August 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Transitioning to the cloud: New Relic’s journey to AWS&lt;/td&gt;
&lt;td&gt;Wendy Shepperd, New Relic&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Distributed tracing for Kafka with OpenTelemetry&lt;/td&gt;
&lt;td&gt;Daniel Kim, New Relic&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;If you are running Kafka in production, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives your team a single interface for monitoring consumer lag, inspecting topics, and managing cluster configuration. You can connect it to any Kafka cluster in minutes and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Notion uses Apache Kafka in production</title><link>https://factorhouse.io/articles/notion-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/notion-kafka-architecture/</guid><description>A deep-dive into Notion&apos;s Kafka architecture — covering use cases, scale, engineering decisions, and key contributors across their data lake and AI pipelines.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Notion is a productivity and knowledge management platform that lets individuals and teams build wikis, project trackers, databases, and documents in a single workspace. The core data model is block-based: every piece of content, from a paragraph to a table row to an inline image, is a block stored in Postgres.&lt;/p&gt;
&lt;p&gt;Notion grew by millions of members in 2021, driven in part by pandemic-era adoption of remote collaboration tools. That growth exposed the limits of its existing data infrastructure: the legacy messaging architecture was not going to scale, and moving large Postgres datasets to analytics systems was taking more than a day end to end.&lt;/p&gt;
&lt;p&gt;Notion began building a new data lake infrastructure in spring 2022, completed it by autumn 2022, and has continued extending it since. The same &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt;-based pipeline that solved the analytics latency problem in 2022 now also powers Notion AI search, retrieval-augmented generation (RAG), and real-time product integrations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spring 2022:&lt;/strong&gt; Notion begins building in-house data lake on Debezium, Kafka, Apache Hudi, Spark, and S3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autumn 2022:&lt;/strong&gt; Data lake pipeline completed; ingestion latency drops from more than a day to minutes for most tables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2022–2023:&lt;/strong&gt; Migration to event-driven architecture on Confluent Cloud completed; engineering team reports tripling its productivity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2023–2024:&lt;/strong&gt; Notion AI features (Autofill, AI search, RAG) launched on top of the Kafka-backed data pipeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2024:&lt;/strong&gt; Pipeline processing tens of MB/sec of Postgres row changes with minimal operational overhead, delivering net savings of over $1 million per year&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;April 2026:&lt;/strong&gt; Multi-region Kafka architecture for data residency compliance documented publicly&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;notions-kafka-use-cases&quot;&gt;Notion’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;change-data-capture-for-data-lake-ingestion&quot;&gt;Change data capture for data lake ingestion&lt;/h3&gt;
&lt;p&gt;The primary use of Kafka at Notion is as the message bus in its CDC pipeline. Debezium captures row-level changes from each Postgres host and publishes them to Kafka. A Spark-based job (Apache Hudi Deltastreamer) consumes those messages and replicates the full state of each Postgres table into S3, where it is available for analytics, AI training, and search indexing.&lt;/p&gt;
&lt;p&gt;Before this pipeline existed, batch exports moved data from Postgres to analytics systems in over a day. After the migration, most tables are available within minutes; the largest table (Notion’s block table) takes up to two hours. That latency range is acceptable for the analytics and AI workloads, which do not require sub-second freshness.&lt;/p&gt;
&lt;h3 id=&quot;event-logging-for-analytics&quot;&gt;Event logging for analytics&lt;/h3&gt;
&lt;p&gt;The logging API endpoint writes analytics events to a regional Apache Kafka cluster when users take actions such as creating a block. Kafka then sinks that data to a region-specific Snowflake table, where it feeds usage analytics and business reporting.&lt;/p&gt;
&lt;h3 id=&quot;ai-and-vector-database-synchronisation&quot;&gt;AI and vector database synchronisation&lt;/h3&gt;
&lt;p&gt;Updates to workspace content are written to a regional Kafka cluster. Spark jobs consume those messages, generate updated embeddings, and write them back to the regional vector database. This keeps Notion AI’s search and generation capabilities current as users edit their workspaces.&lt;/p&gt;
&lt;p&gt;Adam Hudson, Senior Software Engineer at Notion, described the goal: “Confluent’s platform allows us to stream changes as they happen, ensuring AI tools provide relevant information.”&lt;/p&gt;
&lt;h3 id=&quot;product-integrations-and-real-time-notifications&quot;&gt;Product integrations and real-time notifications&lt;/h3&gt;
&lt;p&gt;Notion uses Confluent Cloud’s managed Kafka to power real-time notifications and data synchronisation with third-party tools including Slack, Jira, and Google Drive. Pre-built Confluent connectors replaced a previous custom connector approach. Ekanth Sethuramalingam, Engineering Lead at Notion, described the old approach as “too expensive and difficult to maintain at scale.”&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;As documented by XZ Tie and co-authors (Notion Blog, July 2024) and Ekanth Sethuramalingam and Adam Hudson (Confluent case study):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Daily active users:&lt;/strong&gt; 100+ million&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Postgres shards feeding Kafka:&lt;/strong&gt; 480&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput:&lt;/strong&gt; Tens of MB/sec of Postgres row changes (as of May 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingestion latency:&lt;/strong&gt; Minutes for most tables; up to two hours for the block table&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost impact:&lt;/strong&gt; Net savings of over $1 million in 2022, with proportionally higher savings in 2023 and 2024&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Productivity gain:&lt;/strong&gt; Engineering team tripled its productivity after migrating to Confluent Cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;notions-kafka-architecture&quot;&gt;Notion’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;Notion runs Confluent Cloud (fully managed Apache Kafka) integrated with AWS. Ekanth Sethuramalingam summarised the decision: “We believe in solving problems unique to Notion. Having our team manage infrastructure was out of the question.” Confluent Cloud handles automatic cluster scaling, storage retention adjustments, and broker upgrades without manual intervention.&lt;/p&gt;
&lt;p&gt;Debezium CDC connectors are the one component that Notion self-manages, running on AWS EKS (Elastic Kubernetes Service). The team configures one Debezium connector per Postgres host.&lt;/p&gt;
&lt;h3 id=&quot;topic-design&quot;&gt;Topic design&lt;/h3&gt;
&lt;p&gt;Notion uses one Kafka topic per Postgres table. All Debezium connectors across all 480 Postgres shards write to the same topic for each table. The alternative, one topic per shard per table, would have created 480 times as many topics and significantly complicated downstream Hudi ingestion. The consolidated approach trades some message-level isolation for substantial reductions in topic count and operational complexity.&lt;/p&gt;
&lt;h3 id=&quot;regional-kafka-clusters&quot;&gt;Regional Kafka clusters&lt;/h3&gt;
&lt;p&gt;Notion runs separate Kafka clusters per data-residency region, currently US and EU. Each region has its own Debezium, Kafka, Spark, and Snowflake stack. Customer data is written to its region’s Kafka cluster and processed entirely within that region. This design satisfies data residency compliance requirements without requiring message-level filtering or cross-region routing.&lt;/p&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;Confluent Schema Registry is in use for data governance. Notion has also indicated plans to adopt Tableflow, Confluent’s feature for materialising Kafka topics into Apache Iceberg or Delta Lake tables.&lt;/p&gt;
&lt;h3 id=&quot;data-pipeline-flows&quot;&gt;Data pipeline flows&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CDC data lake pipeline:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Postgres (480 shards)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Debezium CDC (AWS EKS, one connector per Postgres host)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Kafka (one topic per table, Confluent Cloud)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Apache Hudi Deltastreamer (Spark, COPY_ON_WRITE + UPSERT)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; S3 (data lake)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Snowflake (Snowpipe or Snowflake Sink Connector)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;AI/embeddings pipeline:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Workspace content changes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Regional Kafka cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Spark jobs (embedding generation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Regional vector database&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Event logging pipeline:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;API endpoint (user action events)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Regional Kafka cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -&gt; Region-specific Snowflake table&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Debezium (source connector) and Snowflake Sink Connector are the two primary connectors in production. S3 and PostgreSQL connectors are also listed as part of Notion’s Confluent Cloud setup.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;consolidated-fan-in-from-480-shards-into-per-table-topics&quot;&gt;Consolidated fan-in from 480 shards into per-table topics&lt;/h3&gt;
&lt;p&gt;Rather than creating one topic per shard per table, Notion routes CDC events from all 480 Postgres shards into a single topic per table. This reduces the total topic count by a factor of 480 relative to a shard-per-topic design. Consumers, including the Hudi Deltastreamer job, see a unified stream of row changes regardless of which shard originated them.&lt;/p&gt;
&lt;h3 id=&quot;apache-hudi-with-copy_on_write-and-upsert-for-update-heavy-workloads&quot;&gt;Apache Hudi with COPY_ON_WRITE and UPSERT for update-heavy workloads&lt;/h3&gt;
&lt;p&gt;Notion’s data is highly update-driven. A collaborative document block can be edited continuously by multiple users. Notion evaluated several open table formats and chose Apache Hudi specifically because of its native integration with Debezium CDC message formats and better performance characteristics on workloads where updates dominate over appends. They use the COPY_ON_WRITE table type with UPSERT operations, which rewrites data files on each batch to maintain a fully queryable S3 table.&lt;/p&gt;
&lt;h3 id=&quot;full-stack-regional-replication-for-data-residency&quot;&gt;Full-stack regional replication for data residency&lt;/h3&gt;
&lt;p&gt;Notion’s approach to data residency is to avoid cross-region data flows entirely rather than filter or redact messages in transit. Each data-residency region runs its own complete Kafka-based pipeline stack. When a user in the EU edits a block, the change is captured locally, written to the EU Kafka cluster, processed by EU Spark jobs, and stored in the EU vector database and Snowflake instance. No portion of that data touches infrastructure outside the EU.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Confluent Cloud for the Kafka broker layer; AWS EKS for Debezium connectors. The decision to use a fully managed broker service was explicit: Notion’s engineering team is focused on product problems, and managing Kafka broker infrastructure was considered a poor use of that capacity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connector stability:&lt;/strong&gt; Because of the maturity of Debezium and EKS management tooling, Notion has upgraded the EKS and Kafka clusters only a few times over two years of production operation. The authors of the data lake post note this as one of the practical benefits of the chosen stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data freshness monitoring:&lt;/strong&gt; Notion tracks end-to-end ingestion latency per table. Small tables appear in Snowflake within minutes; the block table, which is the largest and most heavily updated, takes up to two hours. This per-table latency tracking allows the team to detect ingestion slowdowns before they affect downstream analytics or AI features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pre-built connector strategy:&lt;/strong&gt; Rather than building custom connectors to third-party tools, Notion relies on Confluent Cloud’s pre-built connector library. This reduces maintenance overhead and allows the data platform team to focus on pipelines that are specific to Notion.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;legacy-messaging-architecture-couldnt-scale-with-rapid-growth&quot;&gt;Legacy messaging architecture couldn’t scale with rapid growth&lt;/h3&gt;
&lt;p&gt;Notion grew by millions of members in 2021. The existing messaging infrastructure was not built for that scale, and custom connectors to third-party tools required ongoing maintenance investment. Notion migrated to an event-driven architecture on Confluent Cloud. Within approximately one year, the migration was complete, the custom connector approach was retired, and the engineering team reported tripling its productivity.&lt;/p&gt;
&lt;h3 id=&quot;batch-postgres-exports-taking-more-than-a-day-to-reach-analytics&quot;&gt;Batch Postgres exports taking more than a day to reach analytics&lt;/h3&gt;
&lt;p&gt;Before the data lake migration, moving Postgres data to analytics systems relied on batch exports that took over a day end to end. That lag made it difficult to build analytics and AI features that required reasonably fresh data. Notion built the CDC pipeline with Debezium, Kafka, and Hudi Deltastreamer to replace batch exports with a continuous incremental stream. End-to-end ingestion time dropped to minutes for most tables.&lt;/p&gt;
&lt;h3 id=&quot;data-residency-compliance-across-regions&quot;&gt;Data residency compliance across regions&lt;/h3&gt;
&lt;p&gt;Expanding internationally required Notion to guarantee that EU customer data would never leave the EU. A centralised Kafka cluster would require complex per-message routing logic to satisfy this. Notion’s solution was to replicate the entire pipeline stack independently in each region. Cross-region data flows are not needed because each region is self-contained.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (Confluent Cloud)&lt;/td&gt;
&lt;td&gt;Fully managed; one topic per Postgres table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connector&lt;/td&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;Deployed on AWS EKS; one connector per Postgres host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Spark (Hudi Deltastreamer)&lt;/td&gt;
&lt;td&gt;Consumes Kafka topics; COPY_ON_WRITE + UPSERT writes to S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Data governance; Tableflow adoption planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Debezium (source), Snowflake Sink Connector, S3 Connector, PostgreSQL Connector&lt;/td&gt;
&lt;td&gt;Pre-built Confluent connectors used throughout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Amazon S3&lt;/td&gt;
&lt;td&gt;Data lake; raw and processed data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data warehouse&lt;/td&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Analytics target; loaded via Snowpipe or Snowflake Sink Connector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute/orchestration&lt;/td&gt;
&lt;td&gt;AWS EKS&lt;/td&gt;
&lt;td&gt;Debezium connector deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector database&lt;/td&gt;
&lt;td&gt;Regional vector DB (undisclosed)&lt;/td&gt;
&lt;td&gt;Updated via Kafka and Spark embedding pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source database&lt;/td&gt;
&lt;td&gt;PostgreSQL (480 shards)&lt;/td&gt;
&lt;td&gt;CDC source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;XZ Tie, Nathan Louie, Thomas Chow, Darin Im, Abhishek Modi, Wendy Jiao&lt;/strong&gt; — Co-authors of the data lake architecture post, &lt;a href=&quot;https://www.notion.com/blog/building-and-scaling-notions-data-lake&quot;&gt;Notion Blog, 1 July 2024&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ekanth Sethuramalingam&lt;/strong&gt; (Engineering Lead, Notion) — Quoted on the migration to Confluent Cloud and the rationale for managed infrastructure, &lt;a href=&quot;https://www.confluent.io/customers/notion/&quot;&gt;Confluent case study&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adam Hudson&lt;/strong&gt; (Senior Software Engineer, Notion) — Quoted on Confluent’s role in AI feature delivery; co-authored the multi-region data systems post, &lt;a href=&quot;https://www.confluent.io/customers/notion/&quot;&gt;Confluent case study&lt;/a&gt; and &lt;a href=&quot;https://www.notion.com/blog/enabling-multi-region-data-systems-at-notion&quot;&gt;Notion Blog, 9 April 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Justin Lee&lt;/strong&gt; (Engineering, Notion) — Co-authored the multi-region data systems architecture post, &lt;a href=&quot;https://www.notion.com/blog/enabling-multi-region-data-systems-at-notion&quot;&gt;Notion Blog, 9 April 2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consolidating multi-shard CDC into per-table topics reduces operational complexity significantly.&lt;/strong&gt; If you are running CDC from a sharded Postgres setup, routing all shards to a single topic per table rather than a topic per shard per table can reduce your total topic count by an order of magnitude and simplify consumer configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose your table format based on your write pattern, not just ecosystem popularity.&lt;/strong&gt; Notion evaluated open table formats and selected Apache Hudi specifically because of its update-heavy workload characteristics and native Debezium CDC integration. If your CDC workload is predominantly updates rather than inserts, Hudi’s COPY_ON_WRITE UPSERT semantics may suit it better than alternatives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fully managed Kafka shifts the cost from operations to product.&lt;/strong&gt; Notion’s explicit reasoning for choosing Confluent Cloud was that managing Kafka broker infrastructure was not a good use of engineering capacity. Two years in, they had upgraded their clusters only a few times. If your team’s core competency is product rather than infrastructure, the operational savings from managed Kafka can be significant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data residency compliance is easier to design in than to bolt on.&lt;/strong&gt; Notion handles data residency by running entirely independent Kafka stacks per region rather than implementing cross-region filtering. If you are building multi-region infrastructure, structuring each region as a self-contained pipeline from the start avoids the complexity of message-level routing and filtering later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Track ingestion latency per table, not just aggregate throughput.&lt;/strong&gt; Notion monitors end-to-end latency by table rather than relying on a single pipeline-wide metric. This lets the team quickly identify when a specific table is running behind without needing to diagnose the entire pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;XZ Tie, Nathan Louie, Thomas Chow, Darin Im, Abhishek Modi, Wendy Jiao, “&lt;a href=&quot;https://www.notion.com/blog/building-and-scaling-notions-data-lake&quot;&gt;How Notion build and grew our data lake to keep up with rapid growth&lt;/a&gt;” (1 July 2024)&lt;/li&gt;
&lt;li&gt;Ekanth Sethuramalingam, Adam Hudson (quoted), “&lt;a href=&quot;https://www.confluent.io/customers/notion/&quot;&gt;How Notion Scales its AI and Lowers Operations Costs With Confluent&lt;/a&gt;” (Confluent, accessed May 2026)&lt;/li&gt;
&lt;li&gt;Justin Lee, Adam Hudson, “&lt;a href=&quot;https://www.notion.com/blog/enabling-multi-region-data-systems-at-notion&quot;&gt;Enabling Multi-Region Data Systems at Notion&lt;/a&gt;” (9 April 2026)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are monitoring a Kafka cluster at any scale, you can try &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; free for 30 days. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How PagerDuty uses Apache Kafka in production</title><link>https://factorhouse.io/articles/pagerduty-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/pagerduty-kafka-architecture/</guid><description>A deep-dive into PagerDuty&apos;s Kafka architecture, covering event ingestion, notification scheduling, task execution, and the engineering decisions behind each.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;PagerDuty operates one of the more instructive &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; deployments in the incident management space: Apache Kafka underpins at least four distinct production systems, each with its own partitioning strategy, language runtime, and failure mode. The engineering blog documents these systems in unusual detail, including a candid post-incident report from August 2025 in which a single API quirk in the pekko-connectors-kafka library generated 4.2 million new Kafka producers per hour, 84 times the normal rate, exhausting JVM heap across the cluster and rejecting roughly 95% of incoming events for 38 minutes.&lt;/p&gt;
&lt;p&gt;Kafka’s role at PagerDuty is to decouple a high-throughput alerting platform from the downstream systems responsible for processing events, scheduling notifications, and delivering them to on-call engineers, often under strict latency requirements.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;PagerDuty provides incident management and on-call scheduling software used by operations teams to detect, triage, and respond to production incidents. The platform handles hundreds of millions of API requests daily from customer monitoring systems, and downstream must reliably reach the right on-call engineer through the right contact method, at the right time.&lt;/p&gt;
&lt;p&gt;PagerDuty’s adoption of Kafka was not a single architectural decision but a gradual replacement of earlier queuing approaches. The original notification system, called Artemis, used Apache Cassandra as a polling-based message queue. By 2017, PagerDuty engineers were publishing blog posts describing a Kafka-backed distributed task scheduler. By 2018, a senior engineer presented a Kafka Summit talk describing the migration of the core event ingestion pipeline from Cassandra-based queuing to Kafka. By January 2019, the Notification Scheduling Service (NSS) had replaced Artemis entirely and was live in production.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;April 2017&lt;/td&gt;
&lt;td&gt;David Van Geest publishes the first post in the “Distributed Task Scheduling with Akka, Kafka, Cassandra” series, describing the Scheduler architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2017&lt;/td&gt;
&lt;td&gt;Part 2 of the Scheduler series published, covering dynamic load handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2018&lt;/td&gt;
&lt;td&gt;Chris Morris presents “From Propeller to Jet” at Kafka Summit San Francisco, describing the migration from Cassandra queuing to Kafka for event ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;December 2018&lt;/td&gt;
&lt;td&gt;PagerDuty/scheduler open-sourced on GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;January 2019&lt;/td&gt;
&lt;td&gt;Notification Scheduling Service (NSS), the Elixir/Kafka replacement for Artemis, goes live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2019&lt;/td&gt;
&lt;td&gt;Jon Grieman presents the Elixir + CQRS incident timeline service at ElixirConf; notes it has been in production for three years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 2020&lt;/td&gt;
&lt;td&gt;Tanvir Pathan publishes “Writing Intelligent Health Checks for Kafka Services”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 2020&lt;/td&gt;
&lt;td&gt;Elora Burns publishes “Elixir at PagerDuty: Faster Processing with Stateful Services”, documenting NSS architecture and performance results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 2021&lt;/td&gt;
&lt;td&gt;Tamim Khan publishes post-mortem of the dentry cache investigation affecting Kafka hosts in staging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28 August 2025&lt;/td&gt;
&lt;td&gt;Two Kafka outages (03:53 UTC and 16:38 UTC) disrupt event ingestion for US customers due to a pekko-connectors-kafka producer proliferation bug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 September 2025&lt;/td&gt;
&lt;td&gt;PagerDuty publishes a full post-incident report for the August 28 outages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;pagerdutys-kafka-use-cases&quot;&gt;PagerDuty’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;PagerDuty uses Kafka across four production systems. Each serves a distinct function and was built by a different engineering team, which explains the variation in language runtimes, partitioning strategies, and architectural patterns across the deployment.&lt;/p&gt;
&lt;h3 id=&quot;event-ingestion-pipeline&quot;&gt;Event ingestion pipeline&lt;/h3&gt;
&lt;p&gt;Kafka is described by PagerDuty engineers as “the backbone of our decoupled, async architecture.” Customer monitoring systems publish alerts through PagerDuty’s Events API; those events are written to Kafka topics and consumed by downstream services that handle incident creation, escalation routing, and notification dispatch.&lt;/p&gt;
&lt;p&gt;The Event Ingestion Admin (EIA) service sits in this pipeline. It consumes from multiple Kafka topics, including &lt;code&gt;incoming_events&lt;/code&gt; and &lt;code&gt;failed_events&lt;/code&gt;, and persists event state to ElastiCache for observability. The EIA service is written in Elixir and uses a GenServer-based consumer that polls partition offsets every 10 seconds to detect consumer stall.&lt;/p&gt;
&lt;h3 id=&quot;notification-scheduling-service&quot;&gt;Notification Scheduling Service&lt;/h3&gt;
&lt;p&gt;The Notification Scheduling Service (NSS) replaced the earlier Artemis system, which used Cassandra as a polling queue. Artemis required all processing to run in a single US West Coast datacenter to keep Cassandra query latency acceptable. NSS, built in Elixir on the BEAM runtime, uses Kafka to distribute notification traffic while decoupling message distribution from datacenter placement.&lt;/p&gt;
&lt;p&gt;NSS publishes three categories of message across three parallel topic sets: updates to incident assignments and state, updates to user contact methods and notification rules, and feedback from downstream systems such as telephony confirmations. Users are consistently routed to the same partition set across all three topic categories, which allows a single Elixir process to hold all state for a given user in memory without cross-process synchronisation.&lt;/p&gt;
&lt;p&gt;Elora Burns documented the results when NSS went live in January 2019: 10x throughput, half the lag time, and one-tenth the compute and storage footprint compared to Artemis.&lt;/p&gt;
&lt;h3 id=&quot;distributed-task-scheduler&quot;&gt;Distributed task scheduler&lt;/h3&gt;
&lt;p&gt;The Scheduler library, open-sourced by PagerDuty in December 2018, uses Kafka as its queuing and partitioning layer for scheduled task execution. Services publish serialised task metadata to a Kafka topic; the Scheduler library consumes those messages and executes each task at its scheduled time. Use cases include emailing an engineer two days before an on-call shift and push-notifying a responder one minute after an incident opens.&lt;/p&gt;
&lt;p&gt;Cassandra provides durable task persistence alongside Kafka: tasks are written to Cassandra at publication time and removed on completion. The Kafka message carries enough information to locate the persisted task record; Cassandra is the source of truth for durability.&lt;/p&gt;
&lt;h3 id=&quot;incident-timeline-cqrs-service&quot;&gt;Incident timeline CQRS service&lt;/h3&gt;
&lt;p&gt;An Elixir service powering PagerDuty’s incident timeline entries uses Kafka with a CQRS pattern. This service refactored a component of the Rails monolith into an Elixir umbrella application backed by Kafka for event streaming. Jon Grieman described it at ElixirConf 2019 as one of PagerDuty’s most frequently used services at that time, and noted it had already been in production for three years.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;PagerDuty does not publish aggregate cluster-level metrics, but several concrete figures appear across the engineering blog posts and the August 2025 incident report.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Partitions per topic (EIA service)&lt;/td&gt;
&lt;td&gt;64-100&lt;/td&gt;
&lt;td&gt;Tanvir Pathan, May 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka producer rate at incident peak&lt;/td&gt;
&lt;td&gt;4.2 million new producers/hour (84x normal)&lt;/td&gt;
&lt;td&gt;PagerDuty incident report, September 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event rejection rate at peak&lt;/td&gt;
&lt;td&gt;~95% of create event requests rejected&lt;/td&gt;
&lt;td&gt;PagerDuty incident report, September 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sustained error rate&lt;/td&gt;
&lt;td&gt;18.87% of create event requests returning 5xx&lt;/td&gt;
&lt;td&gt;PagerDuty incident report, September 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster partition limit (noted as a constraint)&lt;/td&gt;
&lt;td&gt;~50,000 partitions per cluster&lt;/td&gt;
&lt;td&gt;Elora Burns, June 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduler topic partition count&lt;/td&gt;
&lt;td&gt;“Low hundreds” at provisioning time&lt;/td&gt;
&lt;td&gt;PagerDuty/scheduler GitHub, design.md&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NSS throughput improvement over Artemis&lt;/td&gt;
&lt;td&gt;10x throughput, 0.5x lag, 0.1x compute and storage&lt;/td&gt;
&lt;td&gt;Elora Burns, June 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;No absolute messages-per-second or bytes-per-second figures have been published. No total topic count or cluster count is disclosed publicly.&lt;/p&gt;
&lt;h2 id=&quot;pagerdutys-kafka-architecture&quot;&gt;PagerDuty’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;PagerDuty operates a multi-datacenter deployment. The Scheduler documentation describes runbooks for isolating a degraded datacenter by sequentially shutting down application nodes, Kafka brokers, and Cassandra nodes in the affected site, then allowing the healthy datacenter to recover. The NSS post notes that decoupling message distribution from datacenter placement was a primary motivation for replacing Artemis.&lt;/p&gt;
&lt;p&gt;Multiple Kafka clusters are in use. The Scheduler documentation references a shared internal “bitpipe” Kafka cluster offered as a shared resource, separate from per-service clusters. Individual topics use partitioned replication with leader/follower broker configuration.&lt;/p&gt;
&lt;h3 id=&quot;broader-data-platform&quot;&gt;Broader data platform&lt;/h3&gt;
&lt;p&gt;Kafka acts as the async messaging layer between the Events API and downstream processing services, including the EIA service, NSS, and the incident timeline service. Cassandra provides durable persistence alongside Kafka in both the Scheduler and the earlier Artemis system. ElastiCache (Redis) stores ingested event state consumed from EIA Kafka topics. The JVM-side producer code uses Akka (now Pekko), while NSS and the EIA service run on Elixir/BEAM.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;The Events API service uses pekko-connectors-kafka for its Kafka producer abstraction. PagerDuty’s August 2025 incident traced directly to how this library handles producer instantiation: passing Kafka settings to the library creates a new producer instance silently, without a &lt;code&gt;new&lt;/code&gt; keyword, rather than reusing an existing one. This API design was characterised in the incident report as a “visibility gap” that made the bug difficult to catch in code review.&lt;/p&gt;
&lt;p&gt;The Transactional Outbox pattern is used to guarantee at-least-once delivery. Messages are written to a database table as part of the originating database transaction; a scraper then reads pending messages and publishes them to Kafka. During the August 2025 outage, this ensured no event data was lost even while the Kafka tier was degraded.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;The Scheduler library implements a single-threaded Kafka consumer poll loop. All partition reassignment callbacks (&lt;code&gt;onPartitionsRevoked&lt;/code&gt;, &lt;code&gt;onPartitionsAssigned&lt;/code&gt;) execute within this poll thread, enabling synchronous blocking during rebalances. On partition revocation, the actor system shuts down, in-flight tasks complete, and database connections close before the partition is yielded. This design avoids concurrency issues during rebalancing at the cost of a simpler execution model.&lt;/p&gt;
&lt;p&gt;The EIA service uses a GenServer-based consumer in Elixir. The health check polls Kafka partition offsets every 10 seconds and compares the result against a stored snapshot to detect consumer stall, independent of message volume. Consul pings the health endpoint on the same 10-second interval.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;ksqlDB is referenced in the August 2025 incident report as part of the Kafka tier, but no further architectural detail is available in published sources.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-decisions&quot;&gt;Special techniques and engineering decisions&lt;/h2&gt;
&lt;h3 id=&quot;consistent-user-to-partition-routing-in-nss&quot;&gt;Consistent user-to-partition routing in NSS&lt;/h3&gt;
&lt;p&gt;The NSS design routes each user to a fixed set of Kafka partitions across all three parallel topic sets. The mapping is static: all traffic for a given user arrives on the same partitions regardless of which topic set carries the message. This allows a single Elixir Partition Owner process to hold the complete state for each user in memory, eliminating cross-process synchronisation. The approach accepts the constraint that repartitioning requires redeployment in exchange for simplicity in the stateful consumer design.&lt;/p&gt;
&lt;h3 id=&quot;partition-locked-serial-task-execution-in-the-scheduler&quot;&gt;Partition-locked serial task execution in the Scheduler&lt;/h3&gt;
&lt;p&gt;The Scheduler library assigns tasks to partitions using a modulo-based Partitioner class keyed on &lt;code&gt;orderingId&lt;/code&gt;. All tasks for the same &lt;code&gt;orderingId&lt;/code&gt; route to the same partition and are processed serially by a single node. This guarantees ordering within a task group without any distributed locking. The partition count is fixed at provisioning time in the “low hundreds” because Kafka does not support changing partition count at runtime for an existing topic.&lt;/p&gt;
&lt;h3 id=&quot;transactional-outbox-for-delivery-guarantees&quot;&gt;Transactional outbox for delivery guarantees&lt;/h3&gt;
&lt;p&gt;PagerDuty applies the Transactional Outbox pattern as a reliability layer between its application databases and Kafka. Messages are written to a database table within the same transaction as the business operation that generates them. A background scraper reads the outbox table and publishes messages to Kafka. This separates the durability guarantee (the database transaction) from the messaging guarantee (Kafka delivery), and proved effective during the August 2025 outage when Kafka was temporarily unavailable but no events were lost.&lt;/p&gt;
&lt;h3 id=&quot;single-threaded-consumer-poll-loop-for-controlled-rebalancing&quot;&gt;Single-threaded consumer poll loop for controlled rebalancing&lt;/h3&gt;
&lt;p&gt;Rather than handling partition assignment callbacks on a separate thread, the Scheduler’s consumer design runs all Kafka consumer interaction, including reassignment callbacks, within a single poll thread. This simplifies shutdown during rebalancing: the callback can block, wait for in-flight work to drain, and close resources in a predictable order before yielding the partition. The trade-off is that the poll thread cannot process new messages during a rebalance, which the design accepts as a reasonable cost for operational simplicity.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;monitoring-and-observability&quot;&gt;Monitoring and observability&lt;/h3&gt;
&lt;p&gt;The Scheduler service uses Datadog dashboards with configurable tags to track instances across applications and environments. Published metrics include tasks enqueued to Kafka, task batch persistence to Cassandra, task execution counts, task queue depth in the SchedulerActor and ExecutorActor, and stale task counts via raw Cassandra queries.&lt;/p&gt;
&lt;p&gt;The EIA service uses a GenServer-based health check that polls Kafka partition offsets every 10 seconds and compares them against a stored snapshot. Two earlier implementations were discarded: a time-based threshold approach produced false positives on low-throughput topics with natural traffic gaps, and an offset-tracking approach that updated state on every message caused the GenServer to crash under production load.&lt;/p&gt;
&lt;p&gt;The August 2025 incident report identified gaps in monitoring that allowed the producer proliferation bug to go undetected until customer impact was severe: no alerting on Kafka producer count, no JVM heap alerting on the Kafka tier, no Kafka producer or consumer telemetry anomaly detection, and no automated service dependency map visibility.&lt;/p&gt;
&lt;h3 id=&quot;incident-response-and-failover&quot;&gt;Incident response and failover&lt;/h3&gt;
&lt;p&gt;For the Scheduler service, the documented runbook for datacenter degradation is: shut down all Scheduler application nodes, then shut down all Kafka brokers, then shut down all Cassandra nodes in the affected datacenter. This sequence allows the healthy datacenter to recover without split-brain state.&lt;/p&gt;
&lt;p&gt;The August 2025 outage was mitigated in both instances by doubling JVM heap size and performing rolling restarts. Root cause identification required correlating JVM heap exhaustion with Kafka producer count metrics, a connection that was not immediately obvious from the monitoring available at the time.&lt;/p&gt;
&lt;h3 id=&quot;planned-improvements-post-august-2025&quot;&gt;Planned improvements post-August 2025&lt;/h3&gt;
&lt;p&gt;Following the incident, PagerDuty committed to: expanding JVM and Kafka-level monitoring, strengthening service dependency mapping, automating customer impact metrics into incident workflows, adding stricter change management guardrails with rollout readiness checklists, running monthly chaos engineering drills for incident procedures, and automating status page update workflows.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;producer-proliferation-from-a-library-api-quirk-august-2025&quot;&gt;Producer proliferation from a library API quirk (August 2025)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; A new API usage-tracking feature caused 4.2 million new Kafka producers to be created per hour at 75% feature rollout, 84 times the normal rate, exhausting JVM heap across the cluster and rejecting roughly 95% of incoming events over a 38-minute window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The pekko-connectors-kafka library instantiates a new Kafka producer when Kafka settings are passed directly to it, rather than when a pre-created producer instance is passed. The API does not require a &lt;code&gt;new&lt;/code&gt; keyword, making instantiation implicit and easy to miss in code review. The new feature triggered this path on every API request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Mitigation in both incidents involved doubling JVM heap size and performing rolling restarts. After the second incident, engineers identified the root cause and rolled back the feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; No event data was lost due to the Transactional Outbox pattern. PagerDuty committed to a range of monitoring, change management, and chaos engineering improvements to prevent similar gaps. The incident affected US customers across two separate 38-minute windows separated by approximately 13 hours on August 28, 2025.&lt;/p&gt;
&lt;h3 id=&quot;dentry-cache-exhaustion-causing-kafka-host-unresponsiveness-2020-2021&quot;&gt;Dentry cache exhaustion causing Kafka host unresponsiveness (2020-2021)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Kafka hosts in PagerDuty’s staging environment became intermittently unresponsive for tens of seconds, causing client connectivity failures, under-replicated partitions, and leader elections. Newly provisioned machines showed identical symptoms after several days, ruling out hardware degradation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; A third-party vendor application generated filesystem lookups for non-existent file paths on the root directory, creating massive negative dentry cache entries in the Linux kernel. &lt;code&gt;perf top&lt;/code&gt; showed the kernel spending 55% of CPU time in &lt;code&gt;__fsnotify_update_child_dentry_flags&lt;/code&gt;. The negative dentry entries accumulated over time until they triggered the lockup behaviour.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Dropping the dentry cache immediately resolved the lockup. The vendor issued a patched build that fixed the root cause.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The investigation, documented by Tamim Khan, illustrates how Kafka host instability can originate entirely outside Kafka itself, and highlights the value of kernel-level profiling tools when broker behaviour is inexplicable at the application layer.&lt;/p&gt;
&lt;h3 id=&quot;health-check-false-positives-for-low-throughput-topics-2020&quot;&gt;Health check false positives for low-throughput topics (2020)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The EIA service’s first health check approach flagged a consumer as unhealthy if no message had been processed in the previous 10 seconds. This produced false positives on topics with naturally sparse traffic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The threshold-based design assumed continuous message flow. A second approach that tracked offsets on every event crashed the GenServer under production message volumes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The final design polls Kafka partition offsets every 10 seconds and compares the result against a stored snapshot. If offsets have not advanced across two consecutive polls on an active topic, the consumer is flagged as stalled. This approach is independent of message volume and stable under production load.&lt;/p&gt;
&lt;h3 id=&quot;scaling-notification-delivery-beyond-cassandra-polling-pre-2019&quot;&gt;Scaling notification delivery beyond Cassandra polling (pre-2019)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The Artemis notification system used Cassandra polling as its queuing mechanism. The polling approach tied all processing to a single US West Coast datacenter to keep query latency acceptable, limiting geographic redundancy and imposing high infrastructure costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; NSS replaced Artemis with an Elixir/Kafka design that routes users to fixed partition sets across topic categories. Kafka handles message distribution; Elixir processes on BEAM hold user state in memory without database polling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; 10x throughput, half the lag, and one-tenth the compute and storage footprint at launch. The design also removed the datacenter placement constraint that had limited Artemis.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Core async messaging layer for event ingestion, notification delivery, task scheduling, and incident timeline event streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;ksqlDB&lt;/td&gt;
&lt;td&gt;Referenced as part of the Kafka tier in the August 2025 incident report; no further architectural detail published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Apache Cassandra&lt;/td&gt;
&lt;td&gt;Durable task persistence in the Scheduler; primary datastore in the prior Artemis notification system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;ElastiCache (Redis)&lt;/td&gt;
&lt;td&gt;Stores ingested event state consumed from EIA Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime (JVM)&lt;/td&gt;
&lt;td&gt;Scala with Akka / Pekko&lt;/td&gt;
&lt;td&gt;Implementation language for the Scheduler library; Pekko (formerly Akka) provides the actor framework and the pekko-connectors-kafka Kafka client abstraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime (BEAM)&lt;/td&gt;
&lt;td&gt;Elixir&lt;/td&gt;
&lt;td&gt;Runtime for NSS, the EIA health check service, and the incident timeline CQRS service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client&lt;/td&gt;
&lt;td&gt;pekko-connectors-kafka&lt;/td&gt;
&lt;td&gt;Kafka producer and consumer abstraction used by the Events API JVM service; source of the August 2025 producer proliferation bug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service discovery&lt;/td&gt;
&lt;td&gt;Consul&lt;/td&gt;
&lt;td&gt;Service discovery and health endpoint polling at 10-second intervals for the EIA service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;Dashboards and metrics for the Scheduler service, tracking task enqueue rates, execution counts, and queue depth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architectural pattern&lt;/td&gt;
&lt;td&gt;Transactional Outbox&lt;/td&gt;
&lt;td&gt;Guarantees at-least-once Kafka message delivery by persisting messages to a database table before publishing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architectural pattern&lt;/td&gt;
&lt;td&gt;CQRS&lt;/td&gt;
&lt;td&gt;Applied to the incident timeline Elixir/Kafka service to separate write and read models&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;David Van Geest&lt;/td&gt;
&lt;td&gt;Engineer, PagerDuty&lt;/td&gt;
&lt;td&gt;Authored the “Distributed Task Scheduling with Akka, Kafka, Cassandra” blog series (April and August 2017); primary designer of the open-source Scheduler library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chris Morris&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, PagerDuty&lt;/td&gt;
&lt;td&gt;Presented “From Propeller to Jet: Upgrading Your Engines Mid-Flight” at Kafka Summit San Francisco 2018, describing the migration from Cassandra queuing to Kafka for event ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elora Burns&lt;/td&gt;
&lt;td&gt;Engineer, PagerDuty&lt;/td&gt;
&lt;td&gt;Authored “Elixir at PagerDuty: Faster Processing with Stateful Services” (June 2020), documenting the NSS Kafka partitioning architecture and performance results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tanvir Pathan&lt;/td&gt;
&lt;td&gt;Engineer, PagerDuty&lt;/td&gt;
&lt;td&gt;Authored “Writing Intelligent Health Checks for Kafka Services” (May 2020), detailing the EIA Kafka consumer health check design and iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tamim Khan&lt;/td&gt;
&lt;td&gt;Engineer, PagerDuty&lt;/td&gt;
&lt;td&gt;Authored “How Lookups for Non-Existent Files Can Lead to Host Unresponsiveness” (May 2021), diagnosing the dentry cache issue affecting Kafka hosts in staging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jon Grieman&lt;/td&gt;
&lt;td&gt;Senior Software Developer, PagerDuty&lt;/td&gt;
&lt;td&gt;Presented “Elixir + CQRS: Architecting for Availability, Operability, and Maintainability at PagerDuty” at ElixirConf 2019, covering the Kafka-backed incident timeline service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistent partition routing enables stateful in-memory consumers.&lt;/strong&gt; NSS’s fixed user-to-partition mapping means each Elixir process owns a predictable slice of user state. If you’re building a stateful Kafka consumer, designing your partition key and count before you go live gives you a stable foundation that is difficult to change later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Library API design can conceal instantiation behaviour.&lt;/strong&gt; The pekko-connectors-kafka producer proliferation bug originated from an API where passing a settings object to a method created a new producer silently, without any indication to the caller. When adopting a Kafka client library, verify how it handles producer and consumer lifecycle, particularly whether convenience methods reuse or recreate underlying resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Transactional Outbox pattern decouples durability from availability.&lt;/strong&gt; PagerDuty lost no event data during a 38-minute outage because the business transaction and the Kafka publish were separated by an outbox table. If you cannot tolerate event loss during Kafka unavailability, the pattern is worth the additional complexity of the scraper component.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka host instability can originate outside Kafka.&lt;/strong&gt; The dentry cache exhaustion case shows that Kafka broker unresponsiveness was caused entirely by a third-party application generating filesystem lookups on the same host. Kernel-level profiling (&lt;code&gt;perf top&lt;/code&gt;) was required to identify the root cause. If you experience intermittent broker lockups that don’t correlate with Kafka metrics, look at what else is running on the broker hosts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partition count is a provisioning-time decision.&lt;/strong&gt; The Scheduler library documentation explicitly notes that partition count cannot be changed at runtime and recommends setting it in the “low hundreds” upfront. PagerDuty also notes the approximately 50,000-partition-per-cluster limit when designing NSS. Plan your partition count before your first production deployment, not as a response to scale pressure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/august-28-kafka-outages-what-happened-and-how-were-improving/&quot;&gt;August 28 Kafka outages: what happened and how we’re improving&lt;/a&gt; — PagerDuty Engineering Blog, September 2025&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/kafka-health-checks/&quot;&gt;Writing intelligent health checks for Kafka services&lt;/a&gt; — Tanvir Pathan, PagerDuty Engineering Blog, May 2020&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/elixir-stateful-services/&quot;&gt;Elixir at PagerDuty: faster processing with stateful services&lt;/a&gt; — Elora Burns, PagerDuty Engineering Blog, June 2020&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/distributed-task-scheduling-pt1/&quot;&gt;Distributed task scheduling with Akka, Kafka, and Cassandra (Part 1)&lt;/a&gt; — David Van Geest, PagerDuty Engineering Blog, April 2017&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/distributed-task-scheduling-pt2/&quot;&gt;Distributed task scheduling with Akka, Kafka, and Cassandra (Part 2)&lt;/a&gt; — David Van Geest, PagerDuty Engineering Blog, August 2017&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pagerduty.com/eng/lookups-non-existent-files-kafka/&quot;&gt;How lookups for non-existent files can lead to host unresponsiveness&lt;/a&gt; — Tamim Khan, PagerDuty Engineering Blog, May 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/kafka-summit-sf18/from-propeller-to-jet/&quot;&gt;From Propeller to Jet: Upgrading Your Engines Mid-Flight&lt;/a&gt; — Chris Morris, Kafka Summit San Francisco 2018&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://noti.st/jgrieman/cbhvkA&quot;&gt;Elixir + CQRS: Architecting for Availability at PagerDuty&lt;/a&gt; — Jon Grieman, ElixirConf 2019&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/PagerDuty/scheduler&quot;&gt;PagerDuty/scheduler&lt;/a&gt; — GitHub, PagerDuty open-source (design.md, operations.md, user.md)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/news/2025/09/pagerduty-kafka-outage/&quot;&gt;PagerDuty Kafka Outage (InfoQ coverage)&lt;/a&gt; — InfoQ, September 2025&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re running Kafka in production, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a unified interface for monitoring consumer lag, inspecting topic data, and managing cluster configuration across multiple clusters. You can connect it to any Kafka cluster in minutes and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Pinterest uses Apache Kafka in production</title><link>https://factorhouse.io/articles/pinterest-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/pinterest-kafka-architecture/</guid><description>A deep-dive into Pinterest&apos;s Kafka architecture — covering use cases, scale, engineering decisions, and key contributors. From 15 million to 40 million messages per second across 3,000 brokers.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pinterest runs one of the largest &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; deployments in production: more than 3,000 brokers across 50+ clusters, handling 40 million messages per second at inbound peak and over 1.2 petabytes of data per day. What makes the story worth studying is not the scale alone, but the engineering that Pinterest built around it — a custom management platform, a byte-level replication enhancement, a broker-decoupled tiered storage sidecar, and eventually a second PubSub system that uses Kafka as its own internal notification queue.&lt;/p&gt;
&lt;p&gt;Kafka sits at the center of Pinterest’s data infrastructure: it carries user activity events from hundreds of millions of users, drives real-time advertising budget enforcement, feeds machine learning signal pipelines over 300 billion ideas, and now underpins a next-generation CDC ingestion system that reduced data latency from 24 hours to under 15 minutes.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Pinterest is a visual discovery platform with hundreds of millions of active users who save, search, and engage with ideas across categories ranging from home design to food to fashion. The platform’s core data challenge is connecting users to relevant content at scale, which requires near-real-time understanding of what users are doing across a corpus of 300 billion ideas.&lt;/p&gt;
&lt;p&gt;Pinterest began building its Kafka-based logging infrastructure around 2014, when the engineering team evaluated open-source logging alternatives and started work on Singer, an internal logging agent that would eventually ship over one trillion messages per day to Kafka. The first public description of the Kafka pipeline appeared in February 2015, describing a chain from Singer through Kafka into Secor (a log persistence service) and then to S3 for offline analytics via Spark.&lt;/p&gt;
&lt;p&gt;By November 2018, Pinterest was running 2,000+ brokers, handling 800 billion messages per day at a peak rate of 15 million messages per second. By December 2020, those figures had grown to 3,000+ brokers across 50+ clusters with a peak inbound throughput of 40 million messages per second.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;Pinterest begins evaluating open-source logging alternatives; Singer development starts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015-02&lt;/td&gt;
&lt;td&gt;First public description of Kafka-based real-time analytics pipeline (Singer → Kafka → Secor → S3 → Spark/MemSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017-08&lt;/td&gt;
&lt;td&gt;DoctorKafka open-sourced (pinterest/DoctorK); managing 1,000+ brokers at the time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018-11&lt;/td&gt;
&lt;td&gt;Scale snapshot: 2,000+ brokers, 800 billion messages/day, 1.2 PB/day, 15 million messages/sec peak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-04&lt;/td&gt;
&lt;td&gt;AZ-aware partitioning deployed, delivering 25% reduction in cross-AZ transfer costs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-06&lt;/td&gt;
&lt;td&gt;Singer open-sourced (pinterest/singer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-10&lt;/td&gt;
&lt;td&gt;Real-time experiment analytics pipeline with Flink reaches production — described as Pinterest’s first Flink application in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Formal evaluation of PubSub scalability alternatives begins; leads to MemQ design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Shallow Mirror deployed in production; major Kafka upgrade to 2.3.1 completed; broker fleet migrated from magnetic to SSD storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020-mid&lt;/td&gt;
&lt;td&gt;MemQ enters production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020-12&lt;/td&gt;
&lt;td&gt;Scale snapshot: 50+ clusters, 3,000+ brokers, 3,000+ topics, ~500,000 partitions, 40 million+ messages/sec inbound peak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021-11&lt;/td&gt;
&lt;td&gt;MemQ announced publicly: up to 90% cheaper than equivalent Kafka deployment for ML data transport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-03&lt;/td&gt;
&lt;td&gt;Unified PubSub Client (PSC) announced; Kafka and MemQ abstracted behind Topic URIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-11&lt;/td&gt;
&lt;td&gt;PSC in production at scale: over 90% Java app migration, 100% Flink migration, over 80% reduction in Flink restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-05&lt;/td&gt;
&lt;td&gt;Broker-decoupled tiered storage enters production (20+ topics, ~200 TB/day offloaded to S3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-02&lt;/td&gt;
&lt;td&gt;Next-generation CDC ingestion pipeline published: Debezium/TiCDC → Kafka → Flink → Iceberg reduces data latency from 24+ hours to 15 minutes across thousands of pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;pinterests-kafka-use-cases&quot;&gt;Pinterest’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Kafka at Pinterest spans data transport, real-time analytics, CDC, ML signal pipelines, and spam detection. Different engineering teams own different parts of the pipeline, which gives a clear picture of how deeply Kafka is embedded across the organization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event streaming and data warehouse transport&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The foundational use case is shipping user activity events (impressions, clicks, close-ups, repins) from application servers into the data warehouse. Singer, Pinterest’s open-source logging agent, runs as a daemonset on tens of thousands of hosts and writes over one trillion messages per day to Kafka with at-least-once delivery and sub-5ms upload latency. From Kafka, Secor persists events to S3 for offline analytics and ML training pipelines. This is the base layer that most other pipelines build on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time advertising budget computation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The ads platform uses a Kafka-backed streaming pipeline to compute and enforce advertiser spend limits in real time. This requires low latency and strict ordering guarantees, making Kafka the natural fit rather than the cost-optimized MemQ system Pinterest later built for ML data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fresh content indexing and recommendation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka events are consumed by Flink and Spark Streaming to keep the content index up to date with newly published Pins. This drives the recommendation system’s awareness of new content as it is created.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spam detection (Guardian)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Trust and Safety team taps user activity events from Kafka and passes them through Guardian, a custom real-time analytics and rules engine built on a columnar query model. A separate Flink-based pipeline also processes these events for spam filtering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visual signals pipeline (Kappa architecture)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Content Acquisition and Media Platform team rebuilt their visual signals infrastructure around Kafka and Flink in a Kappa architecture. The pipeline ingests data across 50 ingestion pipelines covering 300 billion ideas. Apache Flink does stateful stream processing over image and video signals, replacing the previous Lambda architecture that maintained both batch and streaming code paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time experiment analytics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pinterest runs hundreds of concurrent product experiments. Previously, detecting a statistically significant drop in an experiment metric required 10+ hours of batch processing. A Kafka-based Flink pipeline — described as Pinterest’s first Flink application in production when it launched in October 2019 — brings detection time down to minutes. The pipeline reads from two Kafka topics (&lt;code&gt;filtered_events&lt;/code&gt; and &lt;code&gt;filtered_experiment_activations&lt;/code&gt;) and supports 200-300 concurrent experiment groups.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Change data capture (CDC)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Two generations of CDC pipelines use Kafka as the transport layer. An earlier system uses Maxwell to capture incremental database changes from MySQL and publish them to Kafka for downstream ad systems. A more recent system, published in February 2026, captures changes from MySQL, TiDB, and KVStore using Debezium and TiCDC, writes them to Kafka with under-one-second latency, and then processes them through Flink into Apache Iceberg tables on S3. Base tables refresh every 15 minutes to 1 hour, compared to 24+ hours with the previous full-table batch approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ML training data transport (MemQ)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For ML training data, where latency requirements are relaxed, Pinterest built MemQ: a cloud-native PubSub system that uses S3 micro-batching and achieves up to 90% cost savings compared to an equivalent Kafka deployment. MemQ handles the high-volume, latency-tolerant workloads that Kafka handles less efficiently. Notably, MemQ uses Kafka internally as its own notification queue.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;As of the December 2020 snapshot published by the Logging Platform team:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Production clusters&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brokers&lt;/td&gt;
&lt;td&gt;3,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;3,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitions&lt;/td&gt;
&lt;td&gt;~500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound peak throughput&lt;/td&gt;
&lt;td&gt;~25 GB/s (40 million+ messages/second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound peak throughput&lt;/td&gt;
&lt;td&gt;~50 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily messages (from Singer alone)&lt;/td&gt;
&lt;td&gt;1 trillion+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max brokers per cluster&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication factor&lt;/td&gt;
&lt;td&gt;3 (across three availability zones)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS regions&lt;/td&gt;
&lt;td&gt;3 (us-east-1 primary, us-east-2, eu-west-1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiered storage (since May 2024)&lt;/td&gt;
&lt;td&gt;~200 TB/day offloaded from broker disk to S3 across 20+ onboarded topics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Between November 2018 and December 2020, inbound peak throughput grew from 15 million to 40 million messages per second, driven by the addition of visual signal pipelines, the real-time experiment analytics system, and ongoing user growth.&lt;/p&gt;
&lt;h2 id=&quot;pinterests-kafka-architecture&quot;&gt;Pinterest’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;Pinterest runs Kafka entirely on Amazon Web Services, self-managed across three regions: us-east-1 (the primary region, carrying the majority of brokers), us-east-2, and eu-west-1. The cluster fleet runs on EC2 instances. As of the 2018 snapshot, the default instance type was d2.2xlarge with magnetic storage; high-fanout read clusters used d2.8xlarge. By 2020, the team had migrated to SSD-backed instances after identifying magnetic disk I/O wait as a bottleneck during broker replacement events.&lt;/p&gt;
&lt;p&gt;Brokers in each cluster are spread across three availability zones, with topics assigned to specific clusters (the “brokerset” concept described below) to limit the blast radius of a cluster failure.&lt;/p&gt;
&lt;h3 id=&quot;brokerset-virtual-clusters-within-physical-clusters&quot;&gt;Brokerset: virtual clusters within physical clusters&lt;/h3&gt;
&lt;p&gt;Rather than mapping topics to individual brokers, Pinterest uses a “brokerset” concept: a static partition assignment group that functions as a virtual Kafka cluster within a physical cluster. Topics are assigned to a brokerset, and partition leaders are placed using a stride-based algorithm that ensures balanced distribution across availability zones. This means that a single physical cluster can host multiple isolated workloads, and a failure in one brokerset does not affect topics assigned to others.&lt;/p&gt;
&lt;p&gt;The maximum cluster size is capped at 200 brokers. Clusters beyond a certain size become difficult to operate, so Pinterest provisions new clusters rather than growing existing ones past this limit.&lt;/p&gt;
&lt;h3 id=&quot;data-pipeline-flow&quot;&gt;Data pipeline flow&lt;/h3&gt;
&lt;p&gt;The canonical pipeline runs: application servers → Singer (logging agent, running as a daemonset on each host) → Kafka → downstream consumers (Apache Flink, Spark Streaming, Kafka Streams) and S3 via Secor. Schema formats include Apache Thrift and Protocol Buffers.&lt;/p&gt;
&lt;p&gt;The CDC pipeline takes a different path: MySQL, TiDB, and KVStore databases → Debezium or TiCDC connectors → Kafka (sub-one-second write latency) → Apache Flink → Apache Iceberg tables on S3 → Apache Spark (upsert Merge Into operations every 15 minutes to 1 hour).&lt;/p&gt;
&lt;h3 id=&quot;cross-region-replication&quot;&gt;Cross-region replication&lt;/h3&gt;
&lt;p&gt;Pinterest uses MirrorMaker v1 to replicate data between the three AWS regions. They extended MirrorMaker with a technique they call Shallow Mirror, which skips decompression and recompression by operating at the RecordBatch level. This reduces CPU load on the MirrorMaker fleet by over 80% at peak. Pinterest proposed Shallow Mirror to the Apache Kafka community as KIP-712.&lt;/p&gt;
&lt;h3 id=&quot;management-platform-orion&quot;&gt;Management platform: Orion&lt;/h3&gt;
&lt;p&gt;From around 2021, Pinterest replaced its earlier tooling (DoctorKafka and the Kafka Manager CMAK) with Orion, an open-source cluster management and automation platform (available at pinterest/orion on GitHub). Orion handles automated broker replacement, rolling restarts, configuration updates, cluster upgrades, topic creation via a managed wizard with an approval workflow, and end-to-end data lineage tracking. It provides configurable sensor and operator pipelines that abstract operational tasks into composable automations.&lt;/p&gt;
&lt;h3 id=&quot;pubsub-abstraction-psc&quot;&gt;PubSub abstraction: PSC&lt;/h3&gt;
&lt;p&gt;Pinterest introduced PSC (PubSub Client) in 2022 as a unified Java client library that abstracts Kafka and MemQ behind a Topic URI scheme in the format: &lt;code&gt;protocol:/rn:service:environment:cloud_region:cluster:topic&lt;/code&gt;. Applications do not reference broker hostnames directly; service discovery is handled by PSC at runtime. By November 2023, over 90% of Java applications and 100% of Flink jobs had migrated to PSC. The platform team uses PSC to push standardized client configurations and to perform automated error remediation across the fleet.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Producers are required to compress at source. Pinterest observed that using a single-partition partitioner (rather than round-robin) before compression increases compression ratios approximately 2x by improving data locality within each batch. The team standardized inter-broker protocol and log message format versions across all clusters to eliminate unnecessary format conversions.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer group management is handled through Orion’s topic governance workflow. The real-time experiment analytics Flink job, as one example, runs at parallelism 256 across 8 master nodes and 50 worker nodes (EC2 c5d.9xlarge instances), checkpointing 100 GB of state to HDFS every 5 seconds. PSC handles consumer offset management across both Kafka and MemQ topics through the same interface.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink is Pinterest’s primary stream processing engine for Kafka, used across visual signal pipelines, experiment analytics, CDC ingestion, and spam detection. Apache Spark Streaming also consumes from Kafka for certain workloads. Kafka Streams is used for monetisation and metrics use cases.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;CDC ingestion uses Debezium (for MySQL) and TiCDC (for TiDB) as Kafka source connectors. An earlier CDC system used Maxwell for MySQL changelog capture. Secor functions as a custom Kafka-to-S3 sink for log persistence.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AZ-aware partitioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pinterest built a custom Kafka partitioner that directs producer messages and consumer reads to partition leaders located in the same AWS availability zone as the client. The partitioner uses the EC2 Metadata API to determine the client’s AZ, then cross-references Kafka’s rack awareness metadata to identify local partition leaders. If AZ metadata is unavailable, it falls back to selecting from all available workers. The result is a 25% reduction in cross-AZ data transfer costs. A companion custom S3 transporter partitioner applies the same logic for Secor-based log writes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shallow Mirror&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Standard MirrorMaker v1 deserializes every RecordBatch and recompresses it before forwarding, which drives high CPU utilization and causes out-of-memory errors at Pinterest’s peak traffic. Shallow Mirror bypasses this by iterating over RecordBatches as byte buffer pointers rather than deserializing them. The implementation required solving three problems: correctly updating the BaseOffset field in each batch without full deserialization, handling the “first batch problem” where MirrorMaker crops the first batch in a fetch response, and maintaining throughput for small-batch high-frequency producers. Deployed in production in 2020, Shallow Mirror reduced MirrorMaker CPU load by over 80% at peak and was proposed to the Apache Kafka community as KIP-712.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Graph-algorithm-based leader rebalancing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When partition leadership becomes imbalanced across brokers, naively swapping leaders can still leave some brokers overloaded. Pinterest’s Logging Platform team published a two-part solution. Part 1 uses breadth-first search to find single-step leader swap sequences that improve balance. Part 2 models the rebalancing problem as a maximum-flow network to compute optimal swap sequences for more complex imbalances. Critically, both approaches move only partition leaders, not partition data, so the rebalancing operation has a very low cost compared to partition reassignment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker-decoupled tiered storage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Rather than adding tiered storage inside the broker process (the approach taken by KIP-405, the native Kafka tiered storage implementation), Pinterest implemented a sidecar architecture. A Segment Uploader process runs alongside each broker, watching the broker’s log directories via filesystem watchers. When a segment is finalized, the Segment Uploader uploads it to S3 and tracks progress via &lt;code&gt;offset.wm&lt;/code&gt; files. ZooKeeper is used for partition leadership detection to avoid duplicate uploads. S3 object keys use MD5 hash-based prefix entropy to distribute request load and avoid S3 hotspots.&lt;/p&gt;
&lt;p&gt;Consumers can be configured in four modes: Remote Only, Kafka Only, Remote Preferred, and Kafka Preferred. The team extended &lt;code&gt;log.segment.delete.delay.ms&lt;/code&gt; from the default 60 seconds to 5 minutes for low-traffic topics to avoid missed segment uploads during retention cleanup. Since May 2024, the system has processed approximately 200 TB of data per day across 20+ onboarded topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Static membership (KIP-345)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In cloud environments, Kafka Streams consumers restart frequently for reasons unrelated to actual consumer failure: host restarts, rolling deployments, spot instance preemptions. Each restart triggers a full group rebalance and RocksDB state rebuild. Pinterest engineer Liquan Pei collaborated with Confluent’s Boyang Chen on the static membership protocol, presented at Kafka Summit SF 2019 and merged into Kafka as KIP-345. Static membership lets consumers re-join a group without triggering a full rebalance by using a persistent instance ID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upstream Kafka contributions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pinterest’s Logging Platform team has contributed several KIPs and patches to the Apache Kafka project: KIP-91 (the &lt;code&gt;delivery.timeout.ms&lt;/code&gt; producer configuration), KIP-245 (passing Properties objects to the KafkaStreams constructor), KIP-276 and KIP-300 (precursors to KIP-345), KAFKA-6896 (producer and consumer metrics in Kafka Streams), and KAFKA-7023 / KAFKA-7103 (RocksDB bulk loading optimizations for Kafka Streams state stores). Vahid Hashemian from the Logging Platform team is an Apache Kafka Committer and PMC Member.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Automated cluster healing: DoctorKafka (2017-2020)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first automation Pinterest built was DoctorKafka (open-sourced in August 2017 at pinterest/DoctorK), which monitored broker metrics via a dedicated Kafka topic, detected broker failures, and automatically reassigned workload. Broker failures were handled almost daily at Pinterest’s scale; DoctorKafka reduced failure-related alerts by over 95%. To prevent cascading replica loss when multiple brokers failed simultaneously (given a replication factor of 3), DoctorKafka rate-limited broker replacements to one per time period. The system also analyzed 24-48 hours of historical broker statistics and treated network bandwidth as the primary resource metric for rebalancing decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orion (2020 onward)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Orion replaced DoctorKafka and CMAK as Pinterest’s primary cluster management interface. It provides automated broker replacement, rolling upgrades, configuration updates, and cluster provisioning. Topic creation goes through a managed wizard with an approval workflow inside Orion, and lineage is tracked end-to-end from producer to consumer. Orion is open-sourced at pinterest/orion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client configuration standardization via PSC&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before PSC, applications maintained direct Kafka client dependencies with hardcoded broker hostnames and SSL passwords scattered across configurations. This created outage exposure during maintenance events. PSC introduced URI-based service discovery, standardized client configurations delivered by the platform team, and automated error remediation for common remediable exceptions. The result was over an 80% reduction in Flink application restarts caused by remediable client exceptions, and approximately 275 fewer FTE hours per year in keep-the-lights-on work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka version management&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As of 2018, Pinterest maintained a monthly cadence for tracking open-source Kafka release branches. By 2020, the fleet ran on version 2.3.1 with cherry-picked fixes. A key operational improvement was standardizing the inter-broker protocol version and log message format version across all clusters, which eliminated the overhead of unnecessary in-flight message format conversions during rolling upgrades.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker configuration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Broker heap size is set to 8 GB, increased from 4 GB after TLS was enabled (TLS adds approximately 122 KB of memory per SSL KafkaChannel at scale). Pinterest evaluated EBS st1 volumes as a storage alternative to local disks but found d2 local storage superior for their access patterns; later, they migrated from magnetic to SSD-backed instances after identifying I/O wait as the bottleneck during broker replacement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upgrade validation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before rolling out a Kafka version upgrade, Pinterest constructs a parallel test pipeline that mirrors the topology of the target production pipeline. Double publishing sends data through both old and new infrastructure simultaneously, and the team compares throughput, latency, and error metrics before promoting the new version to production.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Daily broker failures at cloud scale (2017)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At 1,000+ brokers, hardware failures were routine. Partition reassignment alone was not fast enough to handle degraded brokers, and manual intervention did not scale. The solution was DoctorKafka: automated detection of broker degradation via metrics published to a Kafka topic, followed by automatic workload reassignment. Rate-limiting was critical: without it, simultaneous failures of multiple brokers could exceed the replication factor, causing data loss. The result was over a 95% reduction in failure-related operational alerts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-AZ data transfer costs (2019)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Default Kafka producer and consumer behavior assigns clients to any available partition leader regardless of availability zone. At Pinterest’s scale, this generated substantial cross-AZ network transfer costs. The team built a custom partitioner using the EC2 Metadata API and Kafka rack awareness to route producers and consumers to partition leaders in their own AZ. The custom S3 transporter partitioner applied the same logic for Secor writes. Result: 25% reduction in cross-AZ transfer costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MirrorMaker CPU spikes during cross-region replication (2020)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Standard MirrorMaker v1 deserializes and recompresses every RecordBatch during cross-region replication. At Pinterest’s throughput, this caused sustained CPU spikes and out-of-memory errors during peak traffic. The Shallow Mirror implementation bypassed serialization by treating RecordBatches as opaque byte buffers, requiring custom handling for BaseOffset modification and the batch-cropping issue introduced by MirrorMaker’s fetch response handling. The result was over an 80% reduction in MirrorMaker CPU load.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Imbalanced partition leadership causing broker overload (2020)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As the cluster fleet grew, partition leadership distribution became uneven, overloading individual brokers. Naive leader swap approaches could move leaders without materially improving the overall balance. Pinterest modeled the rebalancing problem using graph algorithms: BFS for single-step swaps, and maximum-flow networks for general multi-step rebalancing. Both approaches operate only on leader assignments, not on partition data, keeping the operation lightweight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Magnetic disk I/O bottleneck during broker replacement (pre-2020)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On d2.2xlarge instances with magnetic storage, brokers undergoing replacement showed 10-30% CPU I/O wait during the recovery process. Migrating to SSD-backed instances reduced I/O wait to under 0.1% at the p100 level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client misconfiguration and tight coupling to broker endpoints (pre-2022)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before PSC, applications embedded Kafka broker hostnames, SSL passwords, and client configurations directly. Maintenance events that changed broker endpoints required coordinated client restarts. PSC addressed this by introducing URI-based topic addressing with server-side service discovery, platform-managed client configurations, and automated remediation for known failure modes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka unsuitable for ML training data transport (2018-2020)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka’s strict ordering guarantees, partition rigidity, rebalancing overhead, and replication costs made it an expensive fit for high-volume ML training data pipelines, where sub-second latency is not required. The solution was MemQ: a cloud-native PubSub system using S3 micro-batching that achieves up to 90% cost reduction versus an equivalent Kafka deployment. MemQ handles latency-tolerant ML workloads while Kafka continues to handle all use cases that require strict ordering and low latency. MemQ uses Kafka as its internal notification queue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Legacy batch database ingestion taking 24+ hours (pre-2026)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Full-table batch reprocessing reprocessed over 95% of unchanged data on every cycle, creating a 24-hour data latency and unnecessary compute cost. The next-generation CDC pipeline (Debezium/TiCDC → Kafka → Flink → Apache Iceberg) processes only the roughly 5% of records that change in a given day. CDC events land in Kafka within under one second. Base tables on S3 refresh every 15 minutes to 1 hour. Bucket partitioning by primary key hash, combined with bucket joins in downstream Spark queries, delivered a 40%+ reduction in compute cost.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Central message bus and event streaming platform for all latency-sensitive data transport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;MemQ (pinterest/memq)&lt;/td&gt;
&lt;td&gt;Cloud-native PubSub system for latency-tolerant ML training data transport; uses Kafka internally as its notification queue; up to 90% cheaper than equivalent Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log shipping&lt;/td&gt;
&lt;td&gt;Singer (pinterest/singer)&lt;/td&gt;
&lt;td&gt;Open-source logging agent running on tens of thousands of hosts; ships 1 trillion+ messages/day to Kafka with at-least-once delivery and sub-5ms latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;Stateful stream processing for real-time experiment analytics, visual signal pipelines (Kappa architecture), and CDC database ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Spark Streaming&lt;/td&gt;
&lt;td&gt;Stream processing consumer of Kafka topics; also handles periodic Merge Into operations on Iceberg tables in the CDC pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;In-process stream processing for monetisation and metrics use cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State backend&lt;/td&gt;
&lt;td&gt;RocksDB&lt;/td&gt;
&lt;td&gt;State backend for Kafka Streams jobs; Pinterest contributed bulk loading optimizations (KAFKA-7023, KAFKA-7103)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-region replication&lt;/td&gt;
&lt;td&gt;MirrorMaker v1 + Shallow Mirror&lt;/td&gt;
&lt;td&gt;Cross-region Kafka replication; Pinterest’s Shallow Mirror extension bypasses decompression for 80%+ CPU reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster management&lt;/td&gt;
&lt;td&gt;Orion (pinterest/orion)&lt;/td&gt;
&lt;td&gt;Open-source management and automation platform; broker replacement, rolling upgrades, topic governance, lineage tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client abstraction&lt;/td&gt;
&lt;td&gt;PSC (PubSub Client)&lt;/td&gt;
&lt;td&gt;Unified Java client library abstracting Kafka and MemQ behind Topic URI-based service discovery; 100% Flink adoption, 90%+ Java app adoption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connectors&lt;/td&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;MySQL CDC source connector writing change events to Kafka with sub-one-second latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connectors&lt;/td&gt;
&lt;td&gt;TiCDC&lt;/td&gt;
&lt;td&gt;TiDB CDC source connector writing change events to Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connectors&lt;/td&gt;
&lt;td&gt;Maxwell&lt;/td&gt;
&lt;td&gt;Earlier MySQL CDC connector used for ad system database changelog capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table format&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;Table format for CDC and base tables on S3 in the next-generation ingestion pipeline; uses Merge-on-Read strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log persistence&lt;/td&gt;
&lt;td&gt;Secor&lt;/td&gt;
&lt;td&gt;Pinterest’s Kafka-to-S3 log persistence service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object storage&lt;/td&gt;
&lt;td&gt;Amazon S3&lt;/td&gt;
&lt;td&gt;Remote storage for tiered storage segments, Iceberg tables, Flink checkpoints (CSV snapshots), and MemQ message storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkpoint storage&lt;/td&gt;
&lt;td&gt;HDFS&lt;/td&gt;
&lt;td&gt;Flink checkpoint storage for experiment analytics jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination&lt;/td&gt;
&lt;td&gt;ZooKeeper&lt;/td&gt;
&lt;td&gt;Kafka coordination; also used by the tiered storage sidecar for partition leadership detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;Amazon EC2&lt;/td&gt;
&lt;td&gt;Kafka broker hosting (d2.2xlarge default, d2.8xlarge for high-fanout, later SSD instances); Flink worker nodes (c5d.9xlarge for experiment analytics)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialization&lt;/td&gt;
&lt;td&gt;Apache Thrift / Protocol Buffers&lt;/td&gt;
&lt;td&gt;Event serialization formats for Singer and Kafka producers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client libraries&lt;/td&gt;
&lt;td&gt;librdkafka, kafka-python, confluent-kafka-python&lt;/td&gt;
&lt;td&gt;Kafka clients for non-Java (C++) and Python services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source databases&lt;/td&gt;
&lt;td&gt;MySQL, TiDB, KVStore&lt;/td&gt;
&lt;td&gt;CDC source databases feeding the next-generation ingestion pipeline via Debezium and TiCDC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ambud Sharma&lt;/td&gt;
&lt;td&gt;Tech Lead and Engineering Manager, Logging Platform&lt;/td&gt;
&lt;td&gt;Led MemQ design and open-source release; co-authored AZ-aware partitioning and Singer open-source posts; overall Logging Platform leadership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vahid Hashemian&lt;/td&gt;
&lt;td&gt;Staff Software Engineer, Logging Platform (Apache Kafka Committer and PMC Member)&lt;/td&gt;
&lt;td&gt;Co-authored “How Pinterest runs Kafka at scale” (2018) and Confluent blog post (2021); co-authored PSC articles (2022, 2023); upstream Kafka contributions including KIP-91 and KIP-245&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Henry Cai&lt;/td&gt;
&lt;td&gt;Software Engineer, Data Engineering&lt;/td&gt;
&lt;td&gt;Co-authored “How Pinterest runs Kafka at scale” (2018); lead author of the Shallow Mirror article (2021); co-authored AZ-aware partitioning and Singer open-source posts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jeff Xiang&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Logging Platform&lt;/td&gt;
&lt;td&gt;Lead author of the tiered storage article (2024); co-authored PSC articles (2022, 2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ping-Min Lin&lt;/td&gt;
&lt;td&gt;Software Engineer, Logging Platform&lt;/td&gt;
&lt;td&gt;Authored graph algorithms for Kafka operations (Parts 1 and 2, 2020); MemQ performance optimization; co-authored AZ-aware partitioning post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liquan Pei&lt;/td&gt;
&lt;td&gt;Software Engineer&lt;/td&gt;
&lt;td&gt;Co-authored “How Pinterest runs Kafka at scale” (2018); co-presented KIP-345 static membership at Kafka Summit SF 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yu Yang&lt;/td&gt;
&lt;td&gt;Data Engineering&lt;/td&gt;
&lt;td&gt;Authored DoctorKafka open-source announcement (2017)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ankit Patel&lt;/td&gt;
&lt;td&gt;Software Engineer, Content Acquisition and Media Platform&lt;/td&gt;
&lt;td&gt;Authored visual signals Lambda-to-Kappa architecture article (2020)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parag Kesar, Ben Liu&lt;/td&gt;
&lt;td&gt;Software Engineers, Data Engineering&lt;/td&gt;
&lt;td&gt;Co-authored real-time experiment analytics with Flink article (2019)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liang Mou, Yisheng Zhou, Elizabeth Nguyen, Owen Zhang&lt;/td&gt;
&lt;td&gt;Logging Platform&lt;/td&gt;
&lt;td&gt;Co-authored next-generation DB ingestion article (2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Design for cluster isolation from the start.&lt;/strong&gt; Pinterest’s brokerset concept — virtual clusters with stride-based partition assignment within a physical cluster — gives you topic-level failure isolation without the operational overhead of provisioning a separate physical cluster for every workload. The key is capping cluster size (Pinterest uses 200 brokers) and using the brokerset as the unit of risk management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AZ-aware routing pays for itself at scale.&lt;/strong&gt; Default Kafka clients are AZ-agnostic, and at a large enough fleet the cross-AZ data transfer costs are non-trivial. Pinterest built a custom partitioner using EC2 Metadata and Kafka rack awareness that routes producers and consumers to local partition leaders, achieving a 25% reduction in transfer costs. This is a relatively low-effort change for the savings it produces in cloud environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Replication overhead is often decompression, not network.&lt;/strong&gt; Pinterest found that MirrorMaker’s CPU cost was dominated by decompression and recompression, not by network I/O. The Shallow Mirror approach — treating RecordBatches as opaque byte buffers during replication — reduced MirrorMaker CPU load by over 80%. Before investing in replication hardware, it is worth profiling whether the bottleneck is actually the wire or the processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker-side tiered storage is not the only option.&lt;/strong&gt; Pinterest deliberately chose a sidecar architecture for tiered storage rather than waiting for KIP-405’s in-broker implementation. The sidecar Segment Uploader is operationally independent of the broker process, which simplifies upgrades and means a broker restart does not interrupt the upload pipeline. If tiered storage is a priority, evaluate whether broker-coupled or broker-decoupled implementations better fit your upgrade cadence and failure domains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A unified client abstraction reduces operational debt significantly.&lt;/strong&gt; PSC gave Pinterest’s platform team a single control point for client configurations, service discovery, and automated error remediation across both Kafka and MemQ. The result was over 80% fewer Flink restarts from client errors and an estimated 275 fewer FTE hours per year in KTLO work. A thin abstraction layer that separates application code from broker topology is worth the investment when you operate at more than a handful of clusters.&lt;/p&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/how-pinterest-runs-kafka-at-scale-ff9c6f735be&quot;&gt;How Pinterest runs Kafka at scale&lt;/a&gt; - Henry Cai, Shawn Nguyen, Yi Yin, Liquan Pei et al., Pinterest Engineering Blog, 2018&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/real-time-analytics-at-pinterest-1ef11fdb1099&quot;&gt;Real-time analytics at Pinterest&lt;/a&gt; - Krishna Gade, Pinterest Engineering Blog, 2015&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/running-kafka-at-scale-at-pinterest/&quot;&gt;Running Kafka at scale at Pinterest&lt;/a&gt; - Eric Lopez, Heng Zhang, Henry Cai, Jeff Xiang et al., Confluent Engineering Blog, 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/pinterest-tiered-storage-for-apache-kafka-%EF%B8%8F-a-broker-decoupled-approach-c33c69e9958b&quot;&gt;Pinterest tiered storage for Apache Kafka: a broker-decoupled approach&lt;/a&gt; - Jeff Xiang, Vahid Hashemian, Pinterest Engineering Blog, 2024&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/open-sourcing-doctorkafka-kafka-cluster-healing-and-workload-balancing-e51ad25b6b17&quot;&gt;Open-sourcing DoctorKafka: Kafka cluster healing and workload balancing&lt;/a&gt; - Yu Yang, Pinterest Engineering Blog, 2017&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/using-graph-algorithms-to-optimize-kafka-operations-part-1-abbabd606a25&quot;&gt;Using graph algorithms to optimize Kafka operations, Part 1&lt;/a&gt; - Ping-Min Lin, Pinterest Engineering Blog, 2020&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/shallow-mirror-f543b14bb25&quot;&gt;Shallow Mirror&lt;/a&gt; - Henry Cai, Pinterest Engineering Blog, 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/fighting-spam-with-guardian-a-real-time-analytics-and-rules-engine-938e7e61fa27&quot;&gt;Fighting spam with Guardian: a real-time analytics and rules engine&lt;/a&gt; - Hongkai Pan, Pinterest Engineering Blog, 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/pinterest-visual-signals-infrastructure-evolution-from-lambda-to-kappa-architecture-f8f58b127d98&quot;&gt;Pinterest visual signals infrastructure: evolution from Lambda to Kappa architecture&lt;/a&gt; - Ankit Patel, Pinterest Engineering Blog, 2020&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/real-time-experiment-analytics-at-pinterest-using-apache-flink-841c8df98dc2&quot;&gt;Real-time experiment analytics at Pinterest using Apache Flink&lt;/a&gt; - Parag Kesar, Ben Liu, Pinterest Engineering Blog, 2019&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/memq-an-efficient-scalable-cloud-native-pubsub-system-4402695dd4e7&quot;&gt;MemQ: an efficient, scalable cloud-native PubSub system&lt;/a&gt; - Ambud Sharma, Pinterest Engineering Blog, 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/unified-pubsub-client-at-pinterest-397ccfaf508e&quot;&gt;Unified PubSub Client at Pinterest&lt;/a&gt; - Vahid Hashemian, Jeff Xiang, Pinterest Engineering Blog, 2022&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/open-sourcing-singer-pinterests-performant-and-reliable-logging-agent-610fecf35566&quot;&gt;Open-sourcing Singer: Pinterest’s performant and reliable logging agent&lt;/a&gt; - Ambud Sharma, Indy Prentice, Henry Cai, Shawn Nguyen et al., Pinterest Engineering Blog, 2019&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pinterest/orion&quot;&gt;pinterest/orion on GitHub&lt;/a&gt; - Pinterest Engineering&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/next-generation-db-ingestion-at-pinterest-66844b7153b7&quot;&gt;Next-generation DB ingestion at Pinterest&lt;/a&gt; - Liang Mou, Yisheng Zhou, Elizabeth Nguyen, Owen Zhang, Pinterest Engineering Blog, 2026&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/running-unified-pubsub-client-in-production-at-pinterest-64ae2e721daa&quot;&gt;Running Unified PubSub Client in production at Pinterest&lt;/a&gt; - Jeff Xiang, Vahid Hashemian, Jesus Zuniga, Pinterest Engineering Blog, 2023&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/pinterest-engineering/optimizing-kafka-for-the-cloud-4e936643fde0&quot;&gt;Optimizing Kafka for the cloud&lt;/a&gt; - Eric Lopez, Henry Cai, Heng Zhang, Ping-Min Lin et al., Pinterest Engineering Blog, 2019&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.slideshare.net/DiscoverPinterest/static-membership-rebalance-strategy-designed-for-the-cloud-boyang-chenconfluent-liquan-pei-pinterest-kafka-summit-sf-2019-179255682&quot;&gt;Static membership rebalance strategy designed for the cloud (Kafka Summit SF 2019)&lt;/a&gt; - Liquan Pei (Pinterest), Boyang Chen (Confluent), Kafka Summit SF 2019&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are managing a Kafka deployment at scale, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a single interface for monitoring consumer lag, inspecting topics, and managing cluster operations across multiple clusters. A free 30-day trial is available and connects to any Kafka cluster in minutes via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Salesforce uses Apache Kafka in production</title><link>https://factorhouse.io/articles/salesforce-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/salesforce-kafka-architecture/</guid><description>A deep-dive into Salesforce&apos;s Kafka architecture — covering use cases, scale, engineering decisions and key contributors across a fleet of 100+ clusters processing 3+ trillion events per day.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Salesforce has published more detail about its &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; operations than most companies its size, which makes its engineering blog an unusually complete record of what it takes to run Apache Kafka at genuine enterprise scale. By 2021, the company’s fleet had grown to 100+ clusters, 2,500+ brokers, 50,000+ topics, 300,000+ partitions, 40+ PB of storage, and throughput exceeding 3 trillion events per day — a figure that had been doubling annually.&lt;/p&gt;
&lt;p&gt;The engineering problem Kafka solves at Salesforce is not a single use case but a platform-level one: how do you provide a unified, reliable event transport across a multi-tenant SaaS estate that spans hundreds of global data centres, hundreds of thousands of customer orgs, and applications ranging from operational metrics to real-time AI?&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Salesforce is a cloud-based CRM and enterprise software company founded in 1999 and headquartered in San Francisco. It serves more than 150,000 businesses globally across its Sales Cloud, Service Cloud, Marketing Cloud, and Agentforce AI product lines.&lt;/p&gt;
&lt;p&gt;Salesforce adopted Kafka in production no later than May 2014, when it hosted an Apache Kafka edition of the SF Logging Meetup at its headquarters. By June 2014, Rajasekar Elango, a lead developer on the Monitoring and Management Team, was presenting the company’s early Kafka setup at a public Kafka Meetup — a 3-node ZooKeeper, 5-broker cluster with SSL/TLS mutual authentication and Avro serialisation.&lt;/p&gt;
&lt;p&gt;The trigger for adoption was operational observability: the DVA (Diagnostics, Visibility, and Analytics) team needed a scalable transport layer for metrics and logs across a growing global data centre footprint.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;May 2014&lt;/td&gt;
&lt;td&gt;Apache Kafka in production use confirmed; SF Logging Meetup hosted at Salesforce HQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 2014&lt;/td&gt;
&lt;td&gt;Rajasekar Elango presents SSL/TLS Kafka setup at public Kafka Meetup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Late 2015&lt;/td&gt;
&lt;td&gt;Next-generation log pipeline (project Ajna / DeepSea) development begins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Ajna fleet reaches 30+ clusters and hundreds of billions of events per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;December 2016&lt;/td&gt;
&lt;td&gt;Log pipeline completeness measured at only ~25%; reliability programme begins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;September 2017&lt;/td&gt;
&lt;td&gt;Log pipeline reaches 7-nines completeness (99.99999%), sustained consistently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2018&lt;/td&gt;
&lt;td&gt;Mirus (custom cross-cluster replication tool) fully replaces MirrorMaker in all production data centres&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas: Lei Ye and Paul Davidson disclose fleet at 100+ clusters, 3+ trillion events/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2024&lt;/td&gt;
&lt;td&gt;Hyperforce perimeter telemetry pipeline published: Kafka + Spark Streaming + Druid, 60 TB/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2025&lt;/td&gt;
&lt;td&gt;Marketing Cloud 760-node zero-downtime migration into Ajna completed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;July 2025&lt;/td&gt;
&lt;td&gt;Agentforce AI agent audit pipeline published: 20 million model interactions/month over Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May 2026&lt;/td&gt;
&lt;td&gt;Conversational AI scaling post published: Kafka introduced for 30K+ concurrent conversations, targeting 100K&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;salesforces-kafka-use-cases&quot;&gt;Salesforce’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Salesforce’s use cases cover the full breadth of what Kafka is typically used for in a large enterprise, from infrastructure telemetry to customer-facing products to AI pipelines.&lt;/p&gt;
&lt;h3 id=&quot;operational-metrics-and-system-health-project-ajna&quot;&gt;Operational metrics and system health (project Ajna)&lt;/h3&gt;
&lt;p&gt;The DVA team built an internal Kafka-as-a-Service platform code-named Ajna to transport CPU metrics, machine reachability data, system logs, network flow data, application logs, JMX metrics, and custom database metrics across all global data centres to near-real-time dashboards. Ajna is the backbone from which most other Kafka use cases at Salesforce have grown.&lt;/p&gt;
&lt;p&gt;Source: Nishant Gupta, &lt;a href=&quot;https://engineering.salesforce.com/expanding-visibility-with-apache-kafka-e305b12c4aba/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;log-shipping-pipeline&quot;&gt;Log shipping pipeline&lt;/h3&gt;
&lt;p&gt;The Infrastructure group built a next-generation log pipeline on top of Ajna, routing logs from local per-data-centre Kafka clusters through cross-WAN replication to an aggregate cluster in the Secure Zone, and from there into DeepSea (an internal Hadoop/HDFS store) via a MapReduce consumer job called Kafka Camus. The target was 5-nines completeness; it eventually reached 7 nines.&lt;/p&gt;
&lt;p&gt;Source: Sanjeev Sahu, &lt;a href=&quot;https://engineering.salesforce.com/our-journey-to-a-near-perfect-log-pipeline-6ae2f80cf7a0/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;platform-events-and-change-data-capture&quot;&gt;Platform Events and Change Data Capture&lt;/h3&gt;
&lt;p&gt;Salesforce’s Platform Events and Change Data Capture products expose a time-ordered, immutable event stream to hundreds of thousands of multi-tenant customer orgs. Kafka’s log-based storage model directly inspired the design: events are offset-based, durable, and replayable. Serialisation uses Apache Avro, with event definitions driven by Salesforce’s metadata layer.&lt;/p&gt;
&lt;p&gt;Source: Alexey Syomichev, &lt;a href=&quot;https://engineering.salesforce.com/how-apache-kafka-inspired-our-platform-events-architecture-2f351fe4cf63/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;real-time-ml-insights-for-einstein-activity-capture&quot;&gt;Real-time ML insights for Einstein Activity Capture&lt;/h3&gt;
&lt;p&gt;The Activity Platform ingests 100+ million customer interactions per day through Kafka. A chain of Kafka Streams jobs processes the stream: a Gatekeeper spam filter passes clean events to a series of extractors (TensorFlow-based, regex/keyword, Spark ML, and Einstein Conversation Insights), which feed a transformer and then a persistor that writes sales activity insights to Cassandra.&lt;/p&gt;
&lt;p&gt;Source: Rohit Deshpande, &lt;a href=&quot;https://engineering.salesforce.com/real-time-einstein-insights-using-kafka-streams-ca94008c2c6f/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;service-cloud-chatbot-audit-and-debug&quot;&gt;Service Cloud chatbot audit and debug&lt;/h3&gt;
&lt;p&gt;Apache Kafka on Heroku backs the chatbot event pipeline for Service Cloud, targeting tens of millions of debug events daily with up to 7-day retention. The pipeline applies five fault-tolerance patterns on top of Kafka, described further in the special techniques section below.&lt;/p&gt;
&lt;p&gt;Source: Mark Holton, &lt;a href=&quot;https://engineering.salesforce.com/building-a-scalable-event-pipeline-with-heroku-and-salesforce-2549cb20ce06/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;hyperforce-perimeter-telemetry&quot;&gt;Hyperforce perimeter telemetry&lt;/h3&gt;
&lt;p&gt;Salesforce’s Hyperforce infrastructure generates 60 TB of raw perimeter telemetry daily from commercial CDNs. Spark Streaming normalises this data into protobuf format; Kafka then carries it to an Imply/Druid hypercube (18 dimensions, 13 measurements) serving real-time debugging dashboards for live-site incidents, latency analysis, and DDoS detection with sub-second query latency and 2-minute data freshness.&lt;/p&gt;
&lt;p&gt;Source: Srinivas Ranganathan, &lt;a href=&quot;https://engineering.salesforce.com/unlocking-real-time-insights-engineering-the-hyperforce-experience-for-all/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;conversational-ai-context-storage-for-agentforce&quot;&gt;Conversational AI context storage for Agentforce&lt;/h3&gt;
&lt;p&gt;The Conversation Storage Service (CSS) uses Kafka for buffering, batching, and ordering conversational context that powers real-time AI systems — sentiment analysis, agent assist, and supervisor insights — across 50,000+ concurrent conversations at a peak ingestion rate of 30,000+ events per minute. The scaling target is 100,000 concurrent conversations.&lt;/p&gt;
&lt;p&gt;Source: Ashima Kochar and Deepak Mali, &lt;a href=&quot;https://engineering.salesforce.com/scaling-ai-driven-conversations-from-10k-to-100k-while-maintaining-real-time-consistency/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;agentforce-ai-agent-audit-trail&quot;&gt;Agentforce AI agent audit trail&lt;/h3&gt;
&lt;p&gt;Kafka pub-sub ingestion handles the variable, spike-prone traffic from 20 million model interactions per month across 500+ enterprise customers, feeding a 30-day audit data retention pipeline backed by Salesforce Data Cloud and S3.&lt;/p&gt;
&lt;p&gt;Source: Madhavi Kavathekar, &lt;a href=&quot;https://engineering.salesforce.com/architecting-ai-agent-auditing-systems-in-agentforce-overcoming-data-cloud-and-kafka-integration-challenges/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;cross-cluster-replication&quot;&gt;Cross-cluster replication&lt;/h3&gt;
&lt;p&gt;Salesforce replaced Apache MirrorMaker with its own open-source tool, Mirus, for cross-data-centre replication. Mirus is built on the Kafka Connect source connector API and has been in production since April 2018.&lt;/p&gt;
&lt;p&gt;Source: Paul Davidson, &lt;a href=&quot;https://engineering.salesforce.com/open-sourcing-mirus-3ec2c8a38537/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;The fleet figures below are from the Kafka Summit Americas 2021 talk by Lei Ye and Paul Davidson, supplemented with per-system figures from individual engineering blog posts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total clusters&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total brokers&lt;/td&gt;
&lt;td&gt;2,500+ (largest clusters: 250+ brokers each)&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;50,000+&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitions&lt;/td&gt;
&lt;td&gt;300,000+&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;40+ PB&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;3+ trillion events/day (doubling annually)&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fleet availability&lt;/td&gt;
&lt;td&gt;Over 99.9%&lt;/td&gt;
&lt;td&gt;Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketing Cloud cluster (pre-migration 2025)&lt;/td&gt;
&lt;td&gt;760+ nodes, 12 clusters, 1 million messages/second, 15 TB/day&lt;/td&gt;
&lt;td&gt;Dheeraj Bansal and Ankit Jain, April 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einstein Activity Capture&lt;/td&gt;
&lt;td&gt;100+ million customer interactions/day&lt;/td&gt;
&lt;td&gt;Rohit Deshpande&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyperforce telemetry&lt;/td&gt;
&lt;td&gt;60 TB raw perimeter data/day&lt;/td&gt;
&lt;td&gt;Srinivas Ranganathan, August 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agentforce AI&lt;/td&gt;
&lt;td&gt;20 million model interactions/month, 2 billion predictions/month&lt;/td&gt;
&lt;td&gt;Madhavi Kavathekar, July 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversational AI (CSS)&lt;/td&gt;
&lt;td&gt;30,000+ events/minute peak; scaling target of 100K concurrent conversations&lt;/td&gt;
&lt;td&gt;Ashima Kochar and Deepak Mali, May 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chatbot pipeline&lt;/td&gt;
&lt;td&gt;Tens of millions of events/day, 7-day retention&lt;/td&gt;
&lt;td&gt;Mark Holton&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ajna fleet (2016 snapshot)&lt;/td&gt;
&lt;td&gt;30+ clusters, ~300 Mbps aggregate throughput, hundreds of billions of events/day&lt;/td&gt;
&lt;td&gt;Nishant Gupta, July 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;salesforces-kafka-architecture&quot;&gt;Salesforce’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;ajna-kafka-as-a-service&quot;&gt;Ajna: Kafka-as-a-Service&lt;/h3&gt;
&lt;p&gt;Ajna is Salesforce’s internal Kafka platform. Each production data centre runs an “Ajna Local” cluster for ingest. Data replicates over WAN to an “Ajna Aggregate” cluster in a Secure Zone (DMZ), from which consumers — Argus (time series), DeepSea (Hadoop/HDFS), and stream-processing applications — pull downstream. Before April 2018, replication used Apache MirrorMaker; since then it has used Mirus.&lt;/p&gt;
&lt;p&gt;The platform runs on a hybrid deployment model: on-premises clusters orchestrated with Choria (self-healing automation) and public cloud clusters deployed on Kubernetes. Continuous automated patching is applied across the fleet.&lt;/p&gt;
&lt;h3 id=&quot;cross-site-connectivity&quot;&gt;Cross-site connectivity&lt;/h3&gt;
&lt;p&gt;Rather than using Kafka’s advertised listeners over a VPN, Salesforce implemented a “Native Kafka Endpoint” using a standard load balancer fronting Envoy running in Layer 4 SNI-routing mode. This gives clients a stable single endpoint for cross-data-centre Kafka connections without VPN tunnels.&lt;/p&gt;
&lt;p&gt;Source: Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/p&gt;
&lt;h3 id=&quot;topic-design-and-schema-management&quot;&gt;Topic design and schema management&lt;/h3&gt;
&lt;p&gt;Platform Events and Change Data Capture events use Apache Avro serialisation, with event definitions driven by Salesforce’s metadata layer. For the Conversation Storage Service, messages are keyed by conversation ID, ensuring all events for a given conversation land in the same partition and preserve ordering for downstream AI systems.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;The Agentforce audit pipeline uses Kafka’s pub-sub model to absorb bursty, business-hour traffic spikes before handing off to Data Cloud. Dynamic flow control mechanisms handle real-time traffic adjustment in that pipeline.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;For the Conversation Storage Service, Salesforce applies curated consumer lag limits to maintain ordering guarantees for real-time AI workflows. Where consumer lag creates read-after-write consistency problems (at 50K concurrent conversations), an in-memory cache (VegaCache) bridges the gap rather than forcing changes to the Kafka consumer configuration.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;The Einstein Activity Capture pipeline relies entirely on Kafka Streams for its ML processing chain. The application team chose Kafka Streams specifically because upgrades can be managed without coordinating with the infrastructure team — unlike the prior Storm-based pipeline.&lt;/p&gt;
&lt;p&gt;Source: Rohit Deshpande, &lt;a href=&quot;https://engineering.salesforce.com/real-time-einstein-insights-using-kafka-streams-ca94008c2c6f/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;mirus-custom-cross-cluster-replication&quot;&gt;Mirus: custom cross-cluster replication&lt;/h3&gt;
&lt;p&gt;Mirus is Salesforce’s open-source replacement for Apache MirrorMaker, built on the Kafka Connect source connector API. Each &lt;code&gt;MirusSourceTask&lt;/code&gt; runs an independent &lt;code&gt;KafkaConsumer&lt;/code&gt;/&lt;code&gt;KafkaProducer&lt;/code&gt; pair for a subset of partitions, enabling higher throughput over internet links than MirrorMaker’s single-producer model. A &lt;code&gt;KafkaMonitor&lt;/code&gt; thread tracks partition changes dynamically, allowing configuration changes via the Connect REST API without a process restart. The tool includes custom JMX metrics for replication lag.&lt;/p&gt;
&lt;p&gt;Source: Paul Davidson, &lt;a href=&quot;https://engineering.salesforce.com/open-sourcing-mirus-3ec2c8a38537/&quot;&gt;engineering.salesforce.com&lt;/a&gt; and &lt;a href=&quot;https://github.com/salesforce/mirus&quot;&gt;github.com/salesforce/mirus&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;envoy-as-layer-4-sni-routing-proxy&quot;&gt;Envoy as Layer 4 SNI-routing proxy&lt;/h3&gt;
&lt;p&gt;For cross-site Kafka connectivity, Salesforce runs Envoy in SNI-routing mode behind a standard load balancer, giving clients a stable single endpoint for cross-data-centre connections. This avoids the need to publish per-broker advertised listeners across WAN links.&lt;/p&gt;
&lt;h3 id=&quot;conversation-level-kafka-partitioning&quot;&gt;Conversation-level Kafka partitioning&lt;/h3&gt;
&lt;p&gt;For the Conversation Storage Service, messages are keyed by conversation ID, ensuring all events for a given conversation land in the same partition and preserve ordering for downstream AI systems that need to reason over conversational context in sequence.&lt;/p&gt;
&lt;h3 id=&quot;fault-tolerant-chatbot-pipeline-patterns&quot;&gt;Fault-tolerant chatbot pipeline patterns&lt;/h3&gt;
&lt;p&gt;The Service Cloud chatbot pipeline layers five fault-tolerance patterns on top of Kafka: a per-endpoint circuit breaker (OPEN/HALF_OPEN/CLOSED states), a bulkhead (concurrent request limits per endpoint), timeout-plus-retry (maximum 6 attempts over 16 hours), UUID-per-event idempotency with upsert semantics, and Kafka partition replication across availability zones.&lt;/p&gt;
&lt;p&gt;Source: Mark Holton, &lt;a href=&quot;https://engineering.salesforce.com/building-a-fault-tolerant-data-pipeline-for-chatbots-47d74bc31f5b/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;ssl-contribution-to-open-source-kafka&quot;&gt;SSL contribution to open-source Kafka&lt;/h3&gt;
&lt;p&gt;Salesforce’s DVA team contributed SSL support to the Kafka 0.8.2 series before it was part of the mainline project, and later advocated for per-topic throttling at the project level.&lt;/p&gt;
&lt;p&gt;Source: Nishant Gupta, &lt;a href=&quot;https://engineering.salesforce.com/expanding-visibility-with-apache-kafka-e305b12c4aba/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-byte-embedded-hash-id-for-deduplication&quot;&gt;4-byte embedded hash ID for deduplication&lt;/h3&gt;
&lt;p&gt;Because the log pipeline uses “at least once” delivery semantics, a 4-byte hashed unique ID is embedded in each log message so downstream consumers can detect and drop duplicates on replay.&lt;/p&gt;
&lt;p&gt;Source: Sanjeev Sahu, &lt;a href=&quot;https://engineering.salesforce.com/our-journey-to-a-near-perfect-log-pipeline-6ae2f80cf7a0/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Hybrid. On-premises clusters use Choria for automated patching and self-healing. Public cloud clusters run on Kubernetes. Continuous automated patching applies across the entire fleet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and observability stack:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cruise Control (LinkedIn open-source)&lt;/td&gt;
&lt;td&gt;Automated cluster workload rebalancing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Xinfra Monitor (LinkedIn open-source)&lt;/td&gt;
&lt;td&gt;Synthetic end-to-end availability and latency checks via synthetic workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argus (Salesforce open-source)&lt;/td&gt;
&lt;td&gt;OpenTSDB-based time series monitoring and alerting for Kafka and downstream systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Splunk&lt;/td&gt;
&lt;td&gt;Log analysis for Kafka operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrangler (Salesforce internal)&lt;/td&gt;
&lt;td&gt;Cross-cluster UI for topic management and fleet-wide visibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fidelity Assessment Service (internal)&lt;/td&gt;
&lt;td&gt;Tracks log pipeline completeness end-to-end; results surfaced in Einstein Analytics dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Source: Lei Ye and Paul Davidson, Kafka Summit Americas 2021&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automated recovery:&lt;/strong&gt; Salesforce built a Kafka Auto-Fix Operations Tool that detects offline or under-replicated partitions, frozen brokers, and disk failures. It identifies the most advanced replica, then runs leader election and partition reassignment automatically. The target mean time to recovery is approximately 2 minutes with a zero data loss guarantee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fault injection testing:&lt;/strong&gt; The infrastructure team uses a bespoke testing framework comprising a Test Coordinator (control plane web service), Load Generator (simulates producer/consumer traffic), Ops Tools (cluster configuration), a Fault Injection Framework built on Apache Trogdor and Kibosh, and a Result Analysis component (log and metric tracking). Test scenarios cover functional, performance, load, scale, upgrade/downgrade, and patching runs on deterministic infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Migration monitoring:&lt;/strong&gt; During the 2025 Marketing Cloud migration, the team ran 15 custom monitoring dashboards (cluster-level to node-level) with 24/7 automated alerting throughout.&lt;/p&gt;
&lt;p&gt;Source: Dheeraj Bansal and Ankit Jain, &lt;a href=&quot;https://engineering.salesforce.com/inside-marketing-clouds-kafka-cluster-upgrade-migrating-760-nodes-with-1000000-messages-second/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;mirrormaker-instability-on-wan-replication&quot;&gt;MirrorMaker instability on WAN replication&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Apache MirrorMaker 0.8.x was unstable and required a full process restart for any configuration change. Its single-producer model created throughput bottlenecks on internet-based WAN links between data centres.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; MirrorMaker’s architecture — one process per cluster pair, one producer per process — could not scale to Salesforce’s cross-data-centre replication volumes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Salesforce built and open-sourced Mirus, a Kafka Connect-based tool with multiple independent consumer-producer pairs per worker process and dynamic REST API configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Mirus fully replaced MirrorMaker across all production data centres from April 2018.&lt;/p&gt;
&lt;p&gt;Source: Paul Davidson, &lt;a href=&quot;https://engineering.salesforce.com/open-sourcing-mirus-3ec2c8a38537/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;log-pipeline-completeness-from-25-to-seven-nines&quot;&gt;Log pipeline completeness: from 25% to seven nines&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Between December 2016 and September 2017, the log shipping pipeline could not reliably deliver logs to DeepSea. Completeness was as low as 25% at the start of the programme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root causes:&lt;/strong&gt; Six distinct issues were identified: multi-threaded consumer logic couldn’t handle dynamic volume changes; MapReduce fault tolerance was set too low; Logstash had file-skipping and zombie-state bugs; MirrorMaker batch size (~1 MB) caused batch rejections; and “at least once” semantics produced duplicates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solutions:&lt;/strong&gt; Multi-threaded consumers with dynamic volume adjustment; MapReduce fault tolerance raised to 50%; Logstash reconfigured to read from the beginning; a novel buffer replay for zombie states; MirrorMaker batch size cut from ~1 MB to 250 KB; a 4-byte embedded hash ID per log record for deduplication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Completeness reached 99.99999% (7 nines) by September 2017 and has been sustained consistently since.&lt;/p&gt;
&lt;p&gt;Source: Sanjeev Sahu, &lt;a href=&quot;https://engineering.salesforce.com/our-journey-to-a-near-perfect-log-pipeline-6ae2f80cf7a0/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;marketing-cloud-migration-760-nodes-zero-downtime&quot;&gt;Marketing Cloud migration: 760 nodes, zero downtime&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Marketing Cloud ran its own Kafka fleet — 760+ nodes, 12 clusters, 1 million messages/second — on CentOS 7 with a different Kafka version and authentication mechanism from the central Ajna platform (RHEL 9). Unifying it into Ajna required a zero-downtime migration under live production traffic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Accumulated divergence between Marketing Cloud’s self-managed Kafka and the centralised Ajna system over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; An automated orchestration pipeline with pre-, in-flight, and post-validation checks; disk-level checksum validation before and after each step; rack-aware replica placement across separate physical racks; phased node rollout with health checks; 15 custom monitoring dashboards; and synthetic data validation post-migration with baseline signature matching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Zero-downtime migration completed; Marketing Cloud Kafka unified into Ajna.&lt;/p&gt;
&lt;p&gt;Source: Dheeraj Bansal and Ankit Jain, &lt;a href=&quot;https://engineering.salesforce.com/inside-marketing-clouds-kafka-cluster-upgrade-migrating-760-nodes-with-1000000-messages-second/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;read-after-write-consistency-at-50k-concurrent-conversations&quot;&gt;Read-after-write consistency at 50K concurrent conversations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; At 50,000 concurrent conversations, Kafka consumers lagged behind producers, causing AI workflows to read stale conversational context immediately after a write.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Kafka’s durability-vs-latency trade-off: freshly written messages were not yet visible to consumers by the time the AI system queried for the latest state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; VegaCache, an in-memory cache, was introduced to serve recent writes directly, bypassing the consumer lag for latency-sensitive reads while keeping Kafka as the durable ordered source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Read-after-write consistency maintained at scale.&lt;/p&gt;
&lt;p&gt;Source: Ashima Kochar and Deepak Mali, &lt;a href=&quot;https://engineering.salesforce.com/scaling-ai-driven-conversations-from-10k-to-100k-while-maintaining-real-time-consistency/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;agentforce-audit-bursty-traffic-and-data-cloud-ingestion-mismatch&quot;&gt;Agentforce audit: bursty traffic and Data Cloud ingestion mismatch&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; AI agent traffic is highly bursty (business-hour spikes); Data Cloud’s ingestion API expected customer-owned S3 buckets, but Agentforce needed Salesforce-controlled S3 buckets — a file-size and ownership mismatch that blocked the integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Architectural mismatch between Data Cloud’s design assumptions and Agentforce’s multi-tenant security model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Kafka’s pub-sub model absorbs traffic spikes before handoff to Data Cloud; dynamic flow control mechanisms handle real-time traffic adjustment; iterative proof-of-concept work resolved the S3 ownership problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The system supports 500+ enterprise customers with 20 million model interactions per month under a 30-day contractual audit retention window.&lt;/p&gt;
&lt;p&gt;Source: Madhavi Kavathekar, &lt;a href=&quot;https://engineering.salesforce.com/architecting-ai-agent-auditing-systems-in-agentforce-overcoming-data-cloud-and-kafka-integration-challenges/&quot;&gt;Salesforce Engineering Blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Kafka 2.6 across main fleet (2021); early versions 0.8.2 and 0.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-cluster replication&lt;/td&gt;
&lt;td&gt;Mirus (Salesforce open-source), Apache MirrorMaker (legacy)&lt;/td&gt;
&lt;td&gt;Mirus replaced MirrorMaker in all data centres from April 2018; built on Kafka Connect source connector API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry / serialisation&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Used for Platform Events, Change Data Capture, and early monitoring pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams, Apache Spark Streaming&lt;/td&gt;
&lt;td&gt;Kafka Streams for Einstein Activity Capture ML chain; Spark Streaming for Hyperforce telemetry normalisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect (Mirus source connector), Kafka Camus&lt;/td&gt;
&lt;td&gt;Camus is a MapReduce job consuming from Ajna Aggregate to DeepSea (HDFS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster coordination&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;60+ ZooKeeper nodes in Marketing Cloud fleet alone before 2025 migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy / networking&lt;/td&gt;
&lt;td&gt;Envoy&lt;/td&gt;
&lt;td&gt;Layer 4 SNI-routing proxy for cross-site Native Kafka Endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;Cruise Control, Xinfra Monitor, Argus, Splunk, Wrangler, Datadog Vector&lt;/td&gt;
&lt;td&gt;Cruise Control for workload rebalancing; Argus is OpenTSDB-based; Wrangler is Salesforce’s internal cross-cluster UI; Vector collects CDN telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment / infra&lt;/td&gt;
&lt;td&gt;Choria (on-premises), Kubernetes (public cloud)&lt;/td&gt;
&lt;td&gt;Choria provides self-healing and automated patching for on-premises clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML inference&lt;/td&gt;
&lt;td&gt;TensorFlow, Spark ML&lt;/td&gt;
&lt;td&gt;Both used in Einstein Activity Capture extractor chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration (app layer)&lt;/td&gt;
&lt;td&gt;Nomad (migrated from DC/OS)&lt;/td&gt;
&lt;td&gt;Used by Einstein Activity Capture pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;Apache Hadoop/HDFS (DeepSea), Cassandra, S3 (AWS)&lt;/td&gt;
&lt;td&gt;DeepSea for log retention; Cassandra for Einstein insights; S3 for Agentforce audit and Mirus replication target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time analytics&lt;/td&gt;
&lt;td&gt;Imply / Apache Druid&lt;/td&gt;
&lt;td&gt;18-dimension, 13-measurement hypercube for Hyperforce telemetry; sub-second query latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;VegaCache (Salesforce internal)&lt;/td&gt;
&lt;td&gt;In-memory cache for read-after-write consistency in Conversation Storage Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log processing&lt;/td&gt;
&lt;td&gt;Logstash&lt;/td&gt;
&lt;td&gt;Runs on each host, publishing to local Ajna Kafka cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fault injection&lt;/td&gt;
&lt;td&gt;Trogdor, Kibosh&lt;/td&gt;
&lt;td&gt;Foundation for Salesforce’s bespoke fault injection testing framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;Configuration storage in Einstein pipeline; initial database for Conversation Storage Service before Kafka introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nishant Gupta&lt;/td&gt;
&lt;td&gt;Sr. Director of Engineering, DVA team&lt;/td&gt;
&lt;td&gt;Authored “Expanding Visibility with Apache Kafka”; presented Ajna platform publicly in July 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rajasekar Elango&lt;/td&gt;
&lt;td&gt;Lead Developer, Monitoring and Management Team&lt;/td&gt;
&lt;td&gt;Presented Secure Kafka at Salesforce at Kafka Meetup, June 2014&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alexey Syomichev&lt;/td&gt;
&lt;td&gt;Engineer, Salesforce&lt;/td&gt;
&lt;td&gt;Authored “How Apache Kafka Inspired Our Platform Events Architecture”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sanjeev Sahu&lt;/td&gt;
&lt;td&gt;Engineer, Infrastructure&lt;/td&gt;
&lt;td&gt;Authored “Our Journey to a Near Perfect Log Pipeline”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rohit Deshpande&lt;/td&gt;
&lt;td&gt;Engineer, Activity Platform / Einstein&lt;/td&gt;
&lt;td&gt;Authored “Real-time Einstein Insights Using Kafka Streams”; contributed three Kafka Improvement Proposals (KIPs) to the Apache Kafka project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paul Davidson&lt;/td&gt;
&lt;td&gt;Software Architect, Kafka Platform&lt;/td&gt;
&lt;td&gt;Original developer of Mirus; co-presented at Kafka Summit Americas 2021; authored “Open Sourcing Mirus”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lei Ye&lt;/td&gt;
&lt;td&gt;Principal Software Engineer, Infrastructure&lt;/td&gt;
&lt;td&gt;Co-presented “Tales from the Four-Comma Club” at Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mark Holton&lt;/td&gt;
&lt;td&gt;Engineer, Service Cloud&lt;/td&gt;
&lt;td&gt;Authored both chatbot pipeline blog posts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dheeraj Bansal&lt;/td&gt;
&lt;td&gt;Principal Member of Technical Staff&lt;/td&gt;
&lt;td&gt;Led the Marketing Cloud 760-node Kafka migration (April 2025)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ankit Jain&lt;/td&gt;
&lt;td&gt;Senior Director of Software Engineering, Platform Architecture and Distributed Systems&lt;/td&gt;
&lt;td&gt;Co-authored the Marketing Cloud migration post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Srinivas Ranganathan&lt;/td&gt;
&lt;td&gt;Director of Software Engineering&lt;/td&gt;
&lt;td&gt;Authored the Hyperforce perimeter telemetry pipeline post (August 2024)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Madhavi Kavathekar&lt;/td&gt;
&lt;td&gt;Director of Software Engineering&lt;/td&gt;
&lt;td&gt;Authored the Agentforce AI agent auditing systems post (July 2025)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashima Kochar&lt;/td&gt;
&lt;td&gt;Lead Software Engineer, Service Cloud&lt;/td&gt;
&lt;td&gt;Lead author on the conversational AI scaling post (May 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deepak Mali&lt;/td&gt;
&lt;td&gt;Engineer, Service Cloud&lt;/td&gt;
&lt;td&gt;Co-author on the conversational AI scaling post (May 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build for replication from the start.&lt;/strong&gt; Salesforce outgrew Apache MirrorMaker’s single-producer model once WAN replication volumes increased. If cross-datacenter or cross-region replication is in your roadmap, evaluate whether your replication tool can handle dynamic configuration changes without a process restart and whether its throughput model scales with your partition count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log pipeline completeness requires end-to-end measurement.&lt;/strong&gt; Salesforce’s log pipeline sat at 25% completeness for months before a dedicated completeness-tracking service (the Fidelity Assessment Service) made the problem visible end-to-end. Instrumenting each stage independently is insufficient; you need a view that correlates events from ingest to sink.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“At least once” delivery requires an idempotency strategy.&lt;/strong&gt; At Salesforce’s scale, duplicates from at-least-once delivery are not hypothetical edge cases. Embedding a hashed unique ID per message and using upsert semantics at the consumer is a concrete, low-overhead approach that avoids the complexity of exactly-once transaction semantics in many pipeline architectures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer lag is a first-class consistency concern for stateful workloads.&lt;/strong&gt; The Conversation Storage Service’s read-after-write consistency problem is common when Kafka is used as a state transport for AI or event-sourcing workloads. An in-memory cache for recent writes, rather than tightening consumer lag SLOs on the Kafka cluster itself, can resolve the issue with less operational risk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in automated recovery tooling before you need it.&lt;/strong&gt; Salesforce built its Kafka Auto-Fix Operations Tool to achieve a ~2-minute MTTR for partition and broker failures. At 2,500+ brokers, manual recovery runbooks cannot achieve this. Automating leader election and partition reassignment for predictable failure modes is worth the investment before the fleet reaches a size where manual intervention becomes the bottleneck.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Author&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Expanding Visibility with Apache Kafka&lt;/td&gt;
&lt;td&gt;Nishant Gupta&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;How Apache Kafka Inspired Our Platform Events Architecture&lt;/td&gt;
&lt;td&gt;Alexey Syomichev&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Real-time Einstein Insights Using Kafka Streams&lt;/td&gt;
&lt;td&gt;Rohit Deshpande&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Our Journey to a Near Perfect Log Pipeline&lt;/td&gt;
&lt;td&gt;Sanjeev Sahu&lt;/td&gt;
&lt;td&gt;December 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Open Sourcing Mirus&lt;/td&gt;
&lt;td&gt;Paul Davidson&lt;/td&gt;
&lt;td&gt;October 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Building a Scalable Event Pipeline with Heroku and Salesforce&lt;/td&gt;
&lt;td&gt;Mark Holton&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Building a Fault-Tolerant Data Pipeline for Chatbots&lt;/td&gt;
&lt;td&gt;Mark Holton&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Inside Marketing Cloud’s Kafka Cluster Upgrade: Migrating 760 Nodes with 1,000,000 Messages/Second&lt;/td&gt;
&lt;td&gt;Dheeraj Bansal, Ankit Jain&lt;/td&gt;
&lt;td&gt;April 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Architecting AI Agent Auditing Systems in Agentforce&lt;/td&gt;
&lt;td&gt;Madhavi Kavathekar&lt;/td&gt;
&lt;td&gt;July 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Scaling AI-Driven Conversations from 10K to 100K While Maintaining Real-Time Consistency&lt;/td&gt;
&lt;td&gt;Ashima Kochar, Deepak Mali&lt;/td&gt;
&lt;td&gt;May 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Unlocking Real-Time Insights: Engineering the Hyperforce Experience&lt;/td&gt;
&lt;td&gt;Srinivas Ranganathan&lt;/td&gt;
&lt;td&gt;August 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Tales from the Four-Comma Club — Managing Kafka as a Service at Salesforce&lt;/td&gt;
&lt;td&gt;Lei Ye, Paul Davidson&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Enabling Real-Time Scenarios at Scale Using Kafka&lt;/td&gt;
&lt;td&gt;Nishant Gupta&lt;/td&gt;
&lt;td&gt;July 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Secure Kafka at Salesforce.com&lt;/td&gt;
&lt;td&gt;Rajasekar Elango&lt;/td&gt;
&lt;td&gt;June 2014&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Mirus — GitHub&lt;/td&gt;
&lt;td&gt;Paul Davidson (primary maintainer)&lt;/td&gt;
&lt;td&gt;Last release June 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you are running Kafka at scale and want visibility into consumer lag, partition health, and broker performance across your clusters, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a Kafka management and monitoring tool built for platform and data engineering teams. You can try it free for 30 days and connect it to any Kafka cluster in minutes via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Shopify uses Apache Kafka in production</title><link>https://factorhouse.io/articles/shopify-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/shopify-kafka-architecture/</guid><description>A deep-dive into Shopify&apos;s Kafka architecture — covering CDC at 100,000 records/sec, Kubernetes deployment, the Sarama Go client library, and BFCM scale engineering.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shopify processes 1.75 trillion &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; messages every month across a fleet of self-managed clusters running on Google Kubernetes Engine. At the centre of that infrastructure is a change data capture pipeline that streams mutations from more than 100 MySQL database shards at a sustained 65,000 records per second, peaking at 100,000 records per second on Black Friday. Kafka is how Shopify moves data between its sharded monolith, its real-time processing layer, and its data warehouse — and has been since 2014, when the company also wrote and open-sourced its own Go Kafka client library because no suitable one existed.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Shopify is a commerce platform that lets merchants of any size build and run online and physical retail operations. As of 2020, the platform had processed over $40 billion in total merchant sales across 175 countries, with a baseline of 2 million requests per minute scaling to 10 million at peak. The engineering organization is primarily Ruby on Rails and Go.&lt;/p&gt;
&lt;p&gt;Shopify adopted Apache Kafka in 2014 as a central event bus for log aggregation and internal event collection. The initial deployment replaced a plain log-file batch system that introduced latency between event generation and availability in downstream dashboards and Hadoop storage. From 2014 to 2016, clusters were managed across data centre regions using Chef. In 2017, Shopify began migrating to Google Cloud Platform, completing the move to Kubernetes-managed Kafka by 2018. The change data capture system, which would become one of the most technically demanding parts of the Kafka estate, was rebuilt on Kafka Connect and Debezium in 2021.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2013: Shopify open-sources Sarama, a Go client library for Apache Kafka 0.8, built internally to avoid a JVM dependency&lt;/li&gt;
&lt;li&gt;2014: Kafka adopted as the internal data bus; Ruby on Rails producer pipeline built using SysV message queues and Go Sarama&lt;/li&gt;
&lt;li&gt;2016: Multi-tenant Kafka clusters managed across data centre regions via Chef&lt;/li&gt;
&lt;li&gt;2017: Cloud migration begins; flash sales Kafka architecture presented at Kafka Summit San Francisco&lt;/li&gt;
&lt;li&gt;2018: Full migration to Kubernetes StatefulSets on GCP; Sam Obeid and Christopher Vollick present the work at Kafka Summit London&lt;/li&gt;
&lt;li&gt;2020: Platform processes 1.75 trillion Kafka messages per month; CDC pipeline peaks at 100,000 records/sec during BFCM&lt;/li&gt;
&lt;li&gt;2021: Log-based CDC pipeline published; 400TB of CDC data stored in Kafka&lt;/li&gt;
&lt;li&gt;2022: HybridSource pattern for Flink and Kafka archival documented; BFCM live map pipeline using Flink, Kafka, and SSE published&lt;/li&gt;
&lt;li&gt;2024: BFCM data push reaches 12TB per minute; Kafka partition scaling identified as critical for analytics data freshness&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;shopifys-kafka-use-cases&quot;&gt;Shopify’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;event-collection-and-log-aggregation&quot;&gt;Event collection and log aggregation&lt;/h3&gt;
&lt;p&gt;The original use case from 2014. Kafka serves as the company-wide event bus for collecting events and aggregating log data from all internal systems and data centres. The data flows from producers through regional clusters, mirrors to an aggregate cluster, and lands in the data warehouse formatted as Apache Parquet. This pipeline replaced a batch log-file system that could not provide timely data for internal dashboards.&lt;/p&gt;
&lt;h3 id=&quot;change-data-capture&quot;&gt;Change data capture&lt;/h3&gt;
&lt;p&gt;Shopify’s core application is a sharded monolith backed by more than 100 independent MySQL database shards. The CDC pipeline streams binary log mutations from every shard into Kafka, presenting downstream consumers with a single compacted topic per logical table rather than requiring them to subscribe to 100+ shard-specific topics.&lt;/p&gt;
&lt;p&gt;Before 2021, this was handled by Longboat, a query-based extraction tool that polled tables periodically. The limitation was that hard deletes were invisible: once a row was removed, Longboat could not capture the deletion. The replacement system uses Kafka Connect and Debezium to read MySQL binary logs directly, capturing every insert, update, and delete with a P99 latency of under 10 seconds from database write to Kafka availability.&lt;/p&gt;
&lt;h3 id=&quot;real-time-buyer-signal-pipeline-shopify-inbox&quot;&gt;Real-time buyer signal pipeline (Shopify Inbox)&lt;/h3&gt;
&lt;p&gt;Shopify Inbox is the merchant-to-customer messaging product. A real-time pipeline combines two Kafka event types: Monorail events (Shopify’s internal structured event abstraction) and CDC events. These feed into Apache Beam jobs running on Google Cloud Dataflow that score customer intent signals and inform the Inbox response suggestion system. At non-peak hours, the pipeline processes tens of thousands of cart and checkout events per second.&lt;/p&gt;
&lt;h3 id=&quot;bfcm-live-map&quot;&gt;BFCM live map&lt;/h3&gt;
&lt;p&gt;During Black Friday and Cyber Monday, Shopify runs a live map showing real-time global sales. Apache Flink reads from Kafka topics, computes per-region aggregations, and publishes results back to Kafka topics. A Golang SSE server subscribes to those output topics and pushes data to web clients. The 2021 BFCM event ingested 323 billion rows of data with end-to-end visualization latency of 21 seconds and 100% uptime throughout the event.&lt;/p&gt;
&lt;h3 id=&quot;elasticsearch-multi-dc-replication&quot;&gt;Elasticsearch multi-DC replication&lt;/h3&gt;
&lt;p&gt;Kafka is used to replicate Elasticsearch index updates across geographic regions, enabling a transition from active-passive to active-active multi-region search.&lt;/p&gt;
&lt;h3 id=&quot;microservice-messaging&quot;&gt;Microservice messaging&lt;/h3&gt;
&lt;p&gt;Ruby on Rails and Go services produce events through Monorail, Shopify’s internal event abstraction layer. Monorail enforces schemas, supports schema versioning, and defines explicit producer-consumer contracts on top of raw Kafka topics.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;MetricValueSource / Context&lt;/strong&gt;Monthly Kafka messages1.75 trillionShopify Engineering Blog, December 2020Events per day (all clusters)BillionsShopify Engineering Blog, November 2018CDC average throughput65,000 records/secCDC architecture post, March 2021CDC peak throughput (BFCM 2020)100,000 records/secCDC architecture post, March 2021CDC data stored in Kafka400TB+CDC architecture post, March 2021Debezium connectors~150 across 12 Kubernetes podsCDC architecture post, March 2021Cart/checkout events per second (non-peak)Tens of thousandsShopify Inbox pipeline post, December 2021BFCM 2021 rows ingested323 billionSSE data streaming post, November 2022BFCM 2024 data push12TB per minuteBFCM readiness post, November 2025Producer pipeline throughput (2014)Thousands of events/sec; billions per weekRails producer pipeline post, July 2014&lt;/p&gt;
&lt;h2 id=&quot;shopifys-kafka-architecture&quot;&gt;Shopify’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;cluster-topology&quot;&gt;Cluster topology&lt;/h3&gt;
&lt;p&gt;Shopify runs multiple regional Kafka clusters, one per GCP region, and a single aggregate cluster. The aggregate cluster receives all data mirrored from the regional clusters via MirrorMaker and serves as the feed for the data warehouse. Clusters use 30-node configurations managed as Kubernetes StatefulSets with Persistent Volumes.&lt;/p&gt;
&lt;p&gt;Kafka was previously deployed on VMs managed by Chef. The migration to Kubernetes, completed in 2018, was executed as a three-step process to avoid any downtime: deploy new cloud clusters, mirror the existing on-premises clusters to both aggregate clusters simultaneously, then migrate clients from the data centre to the cloud clusters.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Rails and Go applications do not connect directly to Kafka brokers. Instead, they write events to a local SysV message queue. A Go-based producer process reads from that queue and produces to Kafka using the Sarama client library. When Shopify began containerising services with Docker, namespace isolation broke the SysV IPC mechanism, so a TCP-to-SysV proxy layer was added on the host to allow containerised services to continue writing to the queue.&lt;/p&gt;
&lt;p&gt;This indirection fully decouples application code from Kafka availability: the SysV queue is sized to hold two hours of events, so Kafka cluster maintenance or restarts have zero impact on producers.&lt;/p&gt;
&lt;h3 id=&quot;cdc-pipeline-architecture&quot;&gt;CDC pipeline architecture&lt;/h3&gt;
&lt;p&gt;The CDC pipeline for Shopify’s sharded monolith follows a four-stage pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One Debezium MySQL connector per database shard reads from the MySQL binary log.&lt;/li&gt;
&lt;li&gt;A RegexRouter transform consolidates events from all shards into intermediate Kafka topics.&lt;/li&gt;
&lt;li&gt;A custom Kafka Streams application reads from the intermediate topics and demultiplexes records by table.&lt;/li&gt;
&lt;li&gt;Output: one compacted Kafka topic per logical table, partitioned by primary key.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Consumers subscribe to the per-table output topics and see a unified view of each table regardless of how many shards the underlying data spans. Confluent Schema Registry provides data discovery and dependency tracking across CDC topics.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;A large portion of Shopify’s Flink applications use Kafka as both source and sink. The BFCM live map is one example: Flink reads from Kafka, computes aggregations, and publishes results back to Kafka. The Streaming Capabilities team also manages HybridSource pipelines, described in the special techniques section below.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Kafka topics have per-topic retention policies. Expired data is archived to Google Cloud Storage rather than deleted, enabling Flink applications to backfill from historical data using the HybridSource connector before switching to live Kafka consumption.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;sysv-message-queue-producer-decoupling&quot;&gt;SysV message queue producer decoupling&lt;/h3&gt;
&lt;p&gt;Shopify’s producer pipeline places a SysV message queue between application code and the Kafka producer process. This pattern predates widespread use of producer buffering at the client level and gives Shopify a host-local buffer that survives Kafka cluster disruptions. The queue holds up to two hours of events, making cluster maintenance transparent to producers.&lt;/p&gt;
&lt;h3 id=&quot;compacted-per-table-cdc-topics-with-cross-shard-demultiplexing&quot;&gt;Compacted per-table CDC topics with cross-shard demultiplexing&lt;/h3&gt;
&lt;p&gt;Each logical database table is represented as a single compacted Kafka topic partitioned by primary key. Compaction keeps only the most recent record per key, giving consumers a current-state view without replaying full history. The complexity of 100+ underlying MySQL shards is absorbed entirely by the CDC pipeline: a RegexRouter transform consolidates shard-specific events into intermediate topics, and a custom Kafka Streams application repartitions records by table into the final output topics.&lt;/p&gt;
&lt;h3 id=&quot;gcs-serde-for-large-records&quot;&gt;GCS Serde for large records&lt;/h3&gt;
&lt;p&gt;CDC events from large database rows can exceed Kafka’s 1MB per-message limit. Shopify implemented a custom Serde that detects oversized payloads, stores them in Google Cloud Storage, and writes a GCS pointer to the Kafka topic. Consumers use the same Serde to transparently retrieve the payload from GCS. Standard Kafka consumers remain compatible without modification.&lt;/p&gt;
&lt;h3 id=&quot;hybridsource-for-flink-backfill&quot;&gt;HybridSource for Flink backfill&lt;/h3&gt;
&lt;p&gt;Kafka topics at Shopify have configured retention limits. When a topic expires data, that data transitions to GCS archives partitioned across thousands of splits. Flink applications that need to backfill historical data use the HybridSource connector to read from the GCS archive first, then seamlessly transition to the live Kafka topic. This removes the need for manual coordination between historical batch jobs and real-time stream processing.&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-rack-awareness-and-rolling-restart-safety&quot;&gt;Kubernetes rack-awareness and rolling restart safety&lt;/h3&gt;
&lt;p&gt;Kafka broker pods use inter-pod anti-affinity rules to prevent co-located replicas, and Kubernetes zone labels are mapped to Kafka rack configuration to ensure replicas are distributed across availability zones. Readiness probes gate rolling restarts, preventing more than one broker from going offline simultaneously during configuration changes or upgrades. Kafka’s sensitivity to concurrent broker restarts can trigger terabytes of partition rebalancing, and the pod anti-affinity and readiness probe combination is how Shopify avoids this.&lt;/p&gt;
&lt;h3 id=&quot;open-source-contribution-to-debezium&quot;&gt;Open-source contribution to Debezium&lt;/h3&gt;
&lt;p&gt;During initial CDC deployment, full table snapshots held MySQL read locks for hours at a time, blocking writes on production databases. A Shopify engineer implemented a lock-free snapshot mode for Debezium’s MySQL connector and contributed it upstream to the open-source project. This mode has since been available to all Debezium users.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Self-managed on Google Kubernetes Engine. Brokers run as Kubernetes StatefulSets with dedicated Persistent Volumes. Kafka pods have resource-monitoring sidecars, node affinity rules to place brokers on dedicated nodes, and inter-pod anti-affinity to spread replicas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Retention and archival:&lt;/strong&gt; All Kafka topics have per-topic retention policies. Expired data is archived to Google Cloud Storage to support Flink HybridSource backfills rather than being discarded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BFCM capacity planning (Game Days):&lt;/strong&gt; Shopify runs annual chaos-engineering exercises called Game Days starting in spring, simulating 150% of the previous year’s BFCM peak load across three GCP regions. Game Days have identified Kafka-specific capacity gaps: the analytics infrastructure required partition count increases to maintain data freshness during traffic spikes, and these increases are now part of the pre-BFCM preparation checklist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDC connector management:&lt;/strong&gt; Approximately 150 Debezium connectors are managed across 12 Kubernetes pods, with one connector per MySQL shard to isolate failure domains. Schema evolution for CDC consumers was an active area of work as of 2021, with the team implementing governance through Confluent Schema Registry for dependency tracking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloud migration protocol:&lt;/strong&gt; The migration from on-premises VMs to GCP Kubernetes was executed in three phases: deploy cloud clusters, run MirrorMaker to replicate from on-premises to both aggregate clusters simultaneously, then migrate clients. No downtime was required during the transition.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;JVM dependency for the Kafka client&lt;/strong&gt; Kafka’s initial client libraries were Java and Scala only. Deploying a JVM instance on all of Shopify’s Go-native servers was considered impractical. In 2013, engineer Evan Huus built Sarama, a Go client library for Apache Kafka, and Shopify open-sourced it under the MIT license. Sarama is now maintained by IBM and remains a widely used Go Kafka client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docker namespace isolation breaking SysV IPC&lt;/strong&gt; When Shopify containerised Ruby on Rails services, Docker’s process namespace isolation broke the SysV message queue mechanism used to buffer events for Kafka. The producer process ran on the host, but containerised services could no longer write to the host’s IPC namespace. The solution was a TCP-to-SysV proxy layer on the host that accepted messages over a TCP socket and wrote them into the SysV queue. Containerised services connect over TCP; the rest of the pipeline is unchanged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDC table snapshot locking&lt;/strong&gt; Debezium’s initial snapshot mode for MySQL connector held a global read lock for the duration of a full table snapshot. For Shopify’s large tables, this meant hours of blocked writes on production databases. A Shopify engineer contributed a lock-free snapshot mode to the upstream Debezium project, reading table data without holding a lock. This resolved the blocking but introduced a different constraint: the lock-free mode cannot guarantee a consistent point-in-time snapshot while binlog events are also being consumed. Remaining limitations include tables too large to snapshot within practical timeframes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Records exceeding the 1MB Kafka message size limit&lt;/strong&gt; Certain database rows, particularly those with large text or blob fields, produced CDC events that exceeded Kafka’s default message size limit. Rather than raising the broker message size limit globally (which affects all topics), Shopify implemented a custom Serde that externalises oversized payloads to GCS and writes a pointer record to Kafka. Consumers using the same Serde retrieve the payload from GCS transparently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;100+ shard complexity for CDC consumers&lt;/strong&gt; Shopify’s monolith spans more than 100 MySQL shards. A naive CDC design would require consumers to subscribe to shard-specific topics for every table they care about. Shopify’s solution abstracts the sharding entirely: a RegexRouter transform and a custom Kafka Streams demultiplexer consolidate all shard events into unified per-table output topics. Consumers interact only with the per-table topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema evolution in CDC topics&lt;/strong&gt; Breaking changes to internal database schemas propagate to all consumers of the corresponding CDC topics. As of the March 2021 blog post, this was an active challenge with mitigation strategies under development. Confluent Schema Registry is used for dependency tracking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka partition count insufficient for BFCM analytics throughput&lt;/strong&gt; Game Days exercises revealed that analytics Kafka topics needed higher partition counts to sustain data freshness at BFCM scale. Partition count increases are now a standard item in the pre-BFCM preparation checklist.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Central event bus and streaming platform for all internal pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client&lt;/td&gt;
&lt;td&gt;Sarama (originally Shopify/sarama, now IBM/sarama)&lt;/td&gt;
&lt;td&gt;Go client library for all Kafka producers; built by Shopify in 2013&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment / infra&lt;/td&gt;
&lt;td&gt;Kubernetes (GKE, GCP)&lt;/td&gt;
&lt;td&gt;StatefulSet-based Kafka broker deployment and lifecycle management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connector&lt;/td&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;MySQL binary log CDC source connector; one instance per database shard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;Orchestrates Debezium connectors and RegexRouter transforms in the CDC pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Data discovery and dependency tracking for CDC topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;Custom application for CDC cross-shard demultiplexing by table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;Stateful stream processing; reads from and writes to Kafka for real-time analytics and BFCM live map&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream / batch processing&lt;/td&gt;
&lt;td&gt;Apache Beam / Google Cloud Dataflow&lt;/td&gt;
&lt;td&gt;Unified batch and stream processing for the Shopify Inbox buyer signal pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing&lt;/td&gt;
&lt;td&gt;Apache Spark&lt;/td&gt;
&lt;td&gt;Batch processing stage in the data platform pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;Google Cloud Storage (GCS)&lt;/td&gt;
&lt;td&gt;Archive for expired Kafka topic data; external payload store for oversized CDC records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;Google Cloud Bigtable&lt;/td&gt;
&lt;td&gt;Delivery backend for the Reportify query service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data format&lt;/td&gt;
&lt;td&gt;Apache Parquet&lt;/td&gt;
&lt;td&gt;On-disk format for data platform processed output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;MirrorMaker&lt;/td&gt;
&lt;td&gt;Replicates regional Kafka clusters to the aggregate cluster for data warehouse ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal abstraction&lt;/td&gt;
&lt;td&gt;Monorail&lt;/td&gt;
&lt;td&gt;Internal event layer enforcing schemas and version contracts on top of raw Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming server&lt;/td&gt;
&lt;td&gt;Go SSE server&lt;/td&gt;
&lt;td&gt;Subscribes to Kafka topics and pushes processed data to web clients via Server-Sent Events (BFCM live map)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / Team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Evan Huus&lt;/td&gt;
&lt;td&gt;Engineer, Shopify&lt;/td&gt;
&lt;td&gt;Created Sarama, Shopify’s open-source Go Kafka client library (2013)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simon Eskildsen&lt;/td&gt;
&lt;td&gt;Engineer, Shopify&lt;/td&gt;
&lt;td&gt;Designed the Ruby on Rails Kafka producer pipeline using SysV MQ and Go Sarama (2014)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sam Obeid&lt;/td&gt;
&lt;td&gt;Senior Production Engineer, Shopify&lt;/td&gt;
&lt;td&gt;Led Kafka-on-Kubernetes migration; authored the 2018 engineering blog post; speaker at Kafka Summit London 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Christopher Vollick&lt;/td&gt;
&lt;td&gt;Engineer, Shopify&lt;/td&gt;
&lt;td&gt;Speaker at Kafka Summit London 2018, “Kafka in Containers in Docker in Kubernetes in the Cloud”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;John Martin&lt;/td&gt;
&lt;td&gt;Senior Production Engineer, Streaming Platform&lt;/td&gt;
&lt;td&gt;Co-led log-based CDC architecture design and documentation (2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adam Bellemare&lt;/td&gt;
&lt;td&gt;Staff Engineer, Data Platform&lt;/td&gt;
&lt;td&gt;Co-authored CDC architecture blog post (2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashay Pathak&lt;/td&gt;
&lt;td&gt;Data Scientist, Messaging team&lt;/td&gt;
&lt;td&gt;Co-authored Shopify Inbox real-time buyer signal pipeline post (2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selina Li&lt;/td&gt;
&lt;td&gt;Data Scientist, Messaging team&lt;/td&gt;
&lt;td&gt;Co-authored Shopify Inbox real-time buyer signal pipeline post (2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Lam&lt;/td&gt;
&lt;td&gt;Engineer, Streaming Capabilities team&lt;/td&gt;
&lt;td&gt;Co-authored Flink optimisation post covering HybridSource and Kafka retention (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rafael Aguiar&lt;/td&gt;
&lt;td&gt;Engineer, Streaming Capabilities team&lt;/td&gt;
&lt;td&gt;Co-authored Flink optimisation post (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bao Nguyen&lt;/td&gt;
&lt;td&gt;Senior Staff Data Engineer&lt;/td&gt;
&lt;td&gt;Designed and documented the BFCM live map Kafka, Flink, and SSE pipeline (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arbab Ahmed&lt;/td&gt;
&lt;td&gt;Reliability Engineering lead&lt;/td&gt;
&lt;td&gt;Co-authored data platform scaling post covering 1.75 trillion monthly Kafka messages (2020)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bruno Deszczynski&lt;/td&gt;
&lt;td&gt;DPE Technical Program Manager&lt;/td&gt;
&lt;td&gt;Co-authored data platform scaling post (2020)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decouple producers from brokers at the host level.&lt;/strong&gt; Shopify’s SysV message queue layer means application code never holds a direct connection to Kafka. A two-hour local buffer absorbs cluster maintenance windows without producer-side changes. If your producers are tightly coupled to broker availability, consider an intermediary buffer before the producer client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build a CDC abstraction that hides database sharding from consumers.&lt;/strong&gt; A one-connector-per-shard approach with a RegexRouter and a Kafka Streams demultiplexer lets you present unified per-table topics to downstream consumers regardless of how many shards exist. This makes the shard count a deployment detail rather than an API contract.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compacted topics are a viable current-state store for CDC data.&lt;/strong&gt; Shopify uses compacted per-table Kafka topics partitioned by primary key to give consumers the latest record per row without requiring them to replay full history. This is a practical alternative to maintaining a separate read database for certain consumer patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan for large records before they cause incidents.&lt;/strong&gt; If your data includes variable-length text or binary fields, the 1MB Kafka message limit will eventually be reached. Shopify’s GCS Serde externalises oversized payloads before they reach the broker, keeping the solution transparent to standard consumers and avoiding broker-level configuration changes that affect every topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat BFCM-scale capacity planning as a year-round process.&lt;/strong&gt; Shopify’s Game Days exercises start in spring and run through autumn. The discovery that Kafka partition counts needed to increase for analytics freshness came from load testing, not from production incidents. If your traffic has a predictable peak season, model Kafka throughput requirements explicitly before the peak rather than scaling reactively.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/running-apache-kafka-on-kubernetes-at-shopify&quot;&gt;Running Apache Kafka on Kubernetes at Shopify&lt;/a&gt; — Sam Obeid, Shopify Engineering Blog, November 2018&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/kafka-producer-pipeline-for-ruby-on-rails&quot;&gt;Kafka Producer Pipeline for Ruby on Rails&lt;/a&gt; — Simon Eskildsen, Shopify Engineering Blog, July 2014&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/capturing-every-change-shopify-sharded-monolith&quot;&gt;Capturing Every Change From Shopify’s Sharded Monolith&lt;/a&gt; — John Martin and Adam Bellemare, Shopify Engineering Blog, March 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/real-time-buyer-signal-data-pipeline-shopify-inbox&quot;&gt;Building a Real-time Buyer Signal Data Pipeline for Shopify Inbox&lt;/a&gt; — Ashay Pathak and Selina Li, Shopify Engineering Blog, December 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/optimizing-apache-flink-tips-part-two&quot;&gt;3 (More) Tips for Optimizing Apache Flink Applications&lt;/a&gt; — Kevin Lam and Rafael Aguiar, Shopify Engineering Blog, December 2022&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/reliably-scale-data-platform&quot;&gt;How to Reliably Scale Your Data Platform for High Volumes&lt;/a&gt; — Arbab Ahmed and Bruno Deszczynski, Shopify Engineering Blog, December 2020&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/bfcm-readiness-2025&quot;&gt;How we prepare Shopify for BFCM (2025)&lt;/a&gt; — Kyle Petroski and Matthew Frail, Shopify Engineering Blog, November 2025&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.engineering/server-sent-events-data-streaming&quot;&gt;Using Server Sent Events to Simplify Real-time Streaming at Scale&lt;/a&gt; — Bao Nguyen, Shopify Engineering Blog, November 2022&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://test-blog-theme.myshopify.com/blogs/technology/15989853-shopify-open-sources-sarama-a-client-for-kafka-0-8-written-in-go&quot;&gt;Shopify open-sources Sarama, a client for Kafka 0.8 written in Go&lt;/a&gt; — Evan Huus, Shopify Technology Blog, August 2013&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/kafka-summit-sf17/shopify-flash-sales-and-apache-kafka/&quot;&gt;Shopify Flash-Sales and Apache Kafka&lt;/a&gt; — Kafka Summit San Francisco 2017&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/press-release/kafka-summit-london-keynotes-speakers-announced/&quot;&gt;Kafka Summit London 2018 speaker announcement&lt;/a&gt; — Christopher Vollick and Sam Obeid, Confluent, 2018&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want visibility into your own Kafka estate, including consumer lag, partition health, and topic throughput, give &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; a try with a free 30-day trial. You can connect it to any Kafka cluster in minutes and deploy it via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Tencent uses Apache Kafka in production</title><link>https://factorhouse.io/articles/tencent-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/tencent-kafka-architecture/</guid><description>A deep-dive into Tencent&apos;s Kafka architecture — covering their federated cluster design, 20 trillion messages per day, KIP contributions, and tiered storage at Tencent Cloud.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tencent’s platform group processes 20 trillion messages per day across 700 &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; clusters, with individual applications generating burst traffic of up to 100 Gb/s. To operate at that volume, Tencent’s engineers built a federated cluster architecture that presents hundreds of physical Kafka clusters as a single logical deployment to producers and consumers, and contributed two Kafka improvement proposals back to the open-source community in the process.&lt;/p&gt;
&lt;p&gt;The core engineering problem Kafka solves for Tencent is scale beyond what any single cluster can deliver, combined with the need to absorb overnight traffic spikes of up to 20x without requiring application-side changes.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Tencent is a Chinese technology conglomerate operating social platforms (WeChat, QQ), digital media (Tencent Video, QQ Music), gaming, cloud infrastructure, and financial services. Its Platform and Content Group (PCG) alone spans internet, social, and content products serving hundreds of millions of daily active users.&lt;/p&gt;
&lt;p&gt;The scale of data movement across these products is difficult to overstate. WeChat logs interactions across messaging, payments, and mini-programs. Tencent Video streams content and telemetry simultaneously. Real-time recommendation engines and trend detection systems depend on sub-second feature delivery across the same infrastructure.&lt;/p&gt;
&lt;p&gt;Tencent began developing its own messaging infrastructure as early as 2013, when the Big Data team built TubeMQ, a Kafka-inspired internal system designed for high-throughput log collection. TubeMQ was open-sourced in 2019 and donated to the Apache Incubator, eventually becoming part of the Apache InLong project. For real-time data pipelines requiring broad ecosystem compatibility, Tencent PCG adopted Apache Kafka directly, and by 2020 had grown that deployment to 10 trillion messages per day. A year later, the figure had doubled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2013:&lt;/strong&gt; Tencent Big Data team develops TubeMQ for internal messaging&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2019:&lt;/strong&gt; TubeMQ open-sourced and donated to the Apache Incubator&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-2020:&lt;/strong&gt; Tencent PCG builds pproxy/cproxy proxy-layer federation for Kafka&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020-08:&lt;/strong&gt; Kenway Chen, Kahn Chen, and George Shu publish a description of PCG’s federated architecture on the Confluent blog, disclosing 10 trillion messages/day across hundreds of clusters&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2021-03 / 2021-06:&lt;/strong&gt; George Shu submits KIP-694 (partition reduction) and KIP-693 (client-side circuit breaker) to the Apache Kafka community&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2021-07:&lt;/strong&gt; Kahn Chen and Liang Wang present at Kafka Summit APAC 2021, disclosing growth to 20 trillion messages/day and a move to a broker-layer master cluster federation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2023-12:&lt;/strong&gt; Lu Shilin (CKafka kernel lead) publishes a detailed account of tiered storage at Tencent Cloud, covering local disk and COS hybrid retention&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tencents-kafka-use-cases&quot;&gt;Tencent’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Tencent PCG uses Kafka across several distinct pipeline categories, each owned by different product teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-region log ingestion:&lt;/strong&gt; WeChat, QQ, QZone, Tencent Video, and news products route interaction and operational logs through Kafka for centralised ingestion and processing. The breadth of products means the platform must handle heterogeneous write patterns without per-team customisation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Machine learning feature pipelines:&lt;/strong&gt; Real-time recommendation engines and trend detection systems depend on Kafka for sub-second feature delivery. Latency requirements here are strict: stale features degrade recommendation quality directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous microservice communication:&lt;/strong&gt; Tencent PCG runs its internet, social, and content platforms as microservices communicating through Kafka topics. This decouples services that otherwise would require synchronous calls across product boundaries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time analytics:&lt;/strong&gt; Business intelligence pipelines consume Kafka topics for real-time reporting across Tencent product lines, feeding dashboards and alerting systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time data warehouse ingestion:&lt;/strong&gt; Applications including Kandian and WeChat Video collect event data through Kafka into a downstream warehouse built on Hive, HBase, and HDFS, with Apache Flink handling stateful stream processing between the two layers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tiered cold/hot data storage (CKafka):&lt;/strong&gt; Tencent Cloud’s managed Kafka service uses tiered storage to separate hot and cold data, offloading inactive log segments to Tencent Cloud Object Storage (COS) while keeping active data on local disk. This supports long-retention use cases without proportional disk cost growth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log collection and monitoring aggregation:&lt;/strong&gt; CKafka’s product documentation describes compressed log collection and monitoring data aggregation as primary use cases for cloud customers, including integration with Tencent Cloud Log Service (CLS).&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;The scale figures below are drawn from public disclosures by named Tencent engineers at Kafka Summit APAC 2021 and the Confluent blog.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Messages per day:&lt;/strong&gt; 20 trillion (July 2021)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka clusters:&lt;/strong&gt; 700 (July 2021)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brokers (physical):&lt;/strong&gt; ~500 (2020 figure, before master cluster design) (August 2020)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum brokers per physical cluster:&lt;/strong&gt; Up to 1,000 in the evolved federation design (July 2021)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Peak throughput (single product):&lt;/strong&gt; 4 million messages/sec (64 GB/s) (August 2020)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Burst traffic (single application):&lt;/strong&gt; 100 Gb/s (July 2021)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maximum cluster bandwidth:&lt;/strong&gt; 240 Gb/s at ~40% CPU utilisation (August 2020)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target message loss rate:&lt;/strong&gt; 0.01% end-to-end (August 2020)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overnight spike magnitude:&lt;/strong&gt; Up to 20x from product promotions and experiments (August 2020)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The jump from 10 trillion to 20 trillion messages per day between August 2020 and July 2021 gives a sense of the growth rate Tencent’s infrastructure team was tracking against. Static cluster sizing was not viable at this trajectory.&lt;/p&gt;
&lt;h2 id=&quot;tencents-kafka-architecture&quot;&gt;Tencent’s Kafka architecture&lt;/h2&gt;
&lt;p&gt;Tencent PCG’s Kafka architecture evolved through two distinct federation phases, both motivated by the need to exceed the scaling limits of individual clusters without requiring application changes.&lt;/p&gt;
&lt;h3 id=&quot;phase-1-proxy-layer-federation-pproxycproxy&quot;&gt;Phase 1: proxy-layer federation (pproxy/cproxy)&lt;/h3&gt;
&lt;p&gt;The proxy-layer design presents multiple physical Kafka clusters as a single logical cluster to producers and consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Producer proxy (pproxy)&lt;/strong&gt; and &lt;strong&gt;consumer proxy (cproxy)&lt;/strong&gt; each implement the Kafka broker protocol, so existing Kafka clients connect to them without modification. A lightweight name service maps client IDs to proxy broker collections. A custom controller service manages federation metadata: topic state across physical clusters, proxy node lifecycle, and partition composition for logical topics.&lt;/p&gt;
&lt;p&gt;A logical topic with 8 partitions distributes those partitions across two physical clusters (4 partitions each). Clients interact only with logical topic metadata. When a physical cluster reaches capacity, a new cluster can be added to the federation in approximately 2 minutes. New clusters can be fully provisioned in 10 minutes. Metadata refresh latency runs at approximately 1 second.&lt;/p&gt;
&lt;p&gt;The design supported up to 60 physical clusters per logical cluster, with 10 brokers per physical cluster.&lt;/p&gt;
&lt;h3 id=&quot;phase-2-broker-layer-master-cluster-federation&quot;&gt;Phase 2: broker-layer master cluster federation&lt;/h3&gt;
&lt;p&gt;The proxy layer imposed roughly 30% operational overhead. Every new Kafka API capability required interface modifications in both the pproxy and cproxy layers, creating code duplication and maintenance burden.&lt;/p&gt;
&lt;p&gt;The replacement design moves federation logic into the broker layer. A &lt;strong&gt;master cluster&lt;/strong&gt; aggregates metadata from multiple sub-clusters without running brokers of its own. Client SDKs fetch merged metadata directly from the master cluster, eliminating the proxy hop. Consumer groups and transactions are managed centrally in the master cluster. Redis stores configuration and offset data for disaster recovery.&lt;/p&gt;
&lt;p&gt;This design used the KIP-500 role-splitting architecture (which removed ZooKeeper as a dependency) and reduced cluster failure recovery time from 30-60 minutes under standard Kafka to under 1 minute.&lt;/p&gt;
&lt;h3 id=&quot;ckafka-tencent-clouds-managed-kafka-service&quot;&gt;CKafka: Tencent Cloud’s managed Kafka service&lt;/h3&gt;
&lt;p&gt;CKafka is Tencent Cloud’s managed Kafka offering. Its control plane is isolated from the data plane: control operations go through the TencentCloud API SDK, while data operations use the standard Apache Kafka SDK. The service is 100% compatible with the Kafka API from version 0.9 through 2.8, supporting Kafka versions 2.4, 2.8, and 3.2.&lt;/p&gt;
&lt;p&gt;CKafka deploys across availability zones with automated fault recovery and integrates with 15+ Tencent Cloud products, including TencentDB, Elasticsearch Service, COS, Tencent Kubernetes Engine (TKE), Oceanus (the managed Flink service), and CLS.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Tencent’s pproxy layer handled producer-side batching transparently. In the master cluster design, producers connect directly to sub-cluster brokers using merged metadata from the master cluster. The 0.01% end-to-end message loss target shaped producer reliability configuration across the PCG platform.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer group management moved to the master cluster in Phase 2, centralising offset tracking and group coordination. CKafka exposes offset reset and message querying by offset or time through the console, which covers the most common operational needs for cloud customers.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink sits downstream of Kafka for stateful processing in Tencent’s real-time data warehouse pipelines. Kafka topics feed Flink jobs that produce output to Hive, HBase, and HDFS for downstream analytics.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;No public disclosure from Tencent covers their use of Kafka Connect connectors specifically. CKafka’s integration with TencentDB, CLS, and Oceanus is documented at the product level, but connector implementation details have not been published by named contributors.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;logical-to-physical-partition-mapping&quot;&gt;Logical-to-physical partition mapping&lt;/h3&gt;
&lt;p&gt;The pproxy/cproxy layer remaps logical partition numbers to physical cluster partitions transparently to clients. Capacity can be expanded without data rebalancing or application-side changes, and zero-downtime cluster decommission is supported: the target cluster is marked read-only, producer metadata is updated first to halt writes, and consumer metadata is updated after topics expire.&lt;/p&gt;
&lt;h3 id=&quot;kip-693-client-side-circuit-breaker-for-partition-write-errors&quot;&gt;KIP-693: client-side circuit breaker for partition write errors&lt;/h3&gt;
&lt;p&gt;George Shu (General Manager, PCG Data Infrastructure and Platforms) submitted KIP-693 to the Apache Kafka community in 2021. The problem it addresses: disk failures and high disk utilisation in some partitions cause long write latency, which fills the shared producer buffer and degrades throughput for all other partitions on the same broker. KIP-693 proposes a configuration-driven circuit-breaking mechanism to mute failing partitions at the client level, isolating the degradation.&lt;/p&gt;
&lt;h3 id=&quot;kip-694-support-for-reducing-topic-partitions&quot;&gt;KIP-694: support for reducing topic partitions&lt;/h3&gt;
&lt;p&gt;KIP-694, also submitted by George Shu, addresses the inverse problem: Kafka supports adding partitions to a topic but not removing them. At Tencent’s scale, traffic spikes require rapidly expanding partition counts. After the spike, those partitions cannot be reclaimed, which steadily increases cluster metadata overhead. KIP-694 proposes lossless partition reduction as a native Kafka capability.&lt;/p&gt;
&lt;h3 id=&quot;tiered-storage-local-disk-and-cos-hybrid-ckafka&quot;&gt;Tiered storage: local disk and COS hybrid (CKafka)&lt;/h3&gt;
&lt;p&gt;Lu Shilin (CKafka kernel lead) documented this architecture in December 2023. Hot data is retained on local cloud disks. Inactive log segments are uploaded asynchronously to Tencent COS (object storage). A “Local Retention” parameter controls how long uploaded segments persist locally; a separate “Retention” parameter governs the full data window.&lt;/p&gt;
&lt;p&gt;To avoid I/O contention during upload and download, Tencent implemented parallel transfer with rate limiting. Preloading and prefetching load anticipated hot data into memory pools ahead of consumer reads. Memory management uses heap-off-heap ByteBuffer reuse to reduce GC pressure. Per-topic and per-cluster rollback capability is built in.&lt;/p&gt;
&lt;p&gt;The motivation was straightforward: historical data access patterns were polluting the page cache, evicting hot data and degrading SLAs for active consumers.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Tencent PCG runs a self-managed Kafka deployment across its private data centres. Tencent Cloud operates CKafka as a managed service for external cloud customers using a separate control plane.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and observability:&lt;/strong&gt; CKafka exposes multi-dimensional metrics at instance, topic, and consumer group granularity. A top-10 topic ranking by traffic is available in the console out of the box. One-click diagnostic inspection identifies cluster issues and risks. Tencent PCG built its own monitoring and automated management tooling internally to operate hundreds of clusters; the specific tooling stack has not been disclosed publicly by named contributors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster lifecycle management:&lt;/strong&gt; Clusters can be initialised in 10 minutes and scaled by adding one physical cluster in 2 minutes (proxy-layer design). In the master cluster design, sub-clusters can be added without modifying the federation’s logical topology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition rebalancing:&lt;/strong&gt; CKafka performs automatic partition balancing during off-peak hours and supports automatic disk watermark adjustment to prevent business disruption during high-growth periods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; ACL, SASL, and SSL authentication are console-configurable in CKafka. Integration with Tencent Cloud Access Management (CAM) and CloudAudit provides audit traceability for enterprise customers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; CKafka’s console supports offset reset and message querying by offset or time. Tiered storage provides granular rollback at topic or cluster level, which supports operational recovery scenarios without requiring full topic replay.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;20x-overnight-volume-spikes&quot;&gt;20x overnight volume spikes&lt;/h3&gt;
&lt;p&gt;Product promotions and large-scale experiments routinely caused overnight traffic to spike 20x. Static cluster sizing was either wasteful during normal operation or insufficient during peaks.&lt;/p&gt;
&lt;p&gt;Solution: the federated cluster design allows rapid provisioning of additional physical clusters (2 minutes to add one cluster) and decommissioning after peaks, without application-side changes. Clusters are treated as fungible capacity units rather than permanent infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;zookeeper-bounded-partition-count&quot;&gt;ZooKeeper-bounded partition count&lt;/h3&gt;
&lt;p&gt;Before Kafka 2.8, ZooKeeper imposed a practical ceiling well below one million partitions. For Tencent’s real-time data warehouse, which required a large number of topics with many partitions, this was a hard constraint.&lt;/p&gt;
&lt;p&gt;Solution: the broker-layer federation design used the KIP-500 architecture (removing ZooKeeper) to scale beyond this ceiling. KIP-694 was submitted to the community to address the complementary problem of partition reduction after peaks.&lt;/p&gt;
&lt;h3 id=&quot;5-annual-disk-failure-rate-causing-30-60-minute-recovery&quot;&gt;5% annual disk failure rate causing 30-60 minute recovery&lt;/h3&gt;
&lt;p&gt;Standard Kafka’s disaster tolerance operates at the broker level. A 30-60 minute recovery window from cluster failure was incompatible with Tencent PCG’s SLA requirements.&lt;/p&gt;
&lt;p&gt;Solution: the master cluster federation model reduced cluster failure recovery to under 1 minute. Recovery is handled at the federation coordination layer rather than requiring full cluster restart and leader election across hundreds of brokers.&lt;/p&gt;
&lt;h3 id=&quot;30-operational-overhead-from-the-proxy-layer&quot;&gt;30% operational overhead from the proxy layer&lt;/h3&gt;
&lt;p&gt;The pproxy/cproxy approach required continuous interface modifications to expose new Kafka API capabilities, creating code duplication across two proxy implementations.&lt;/p&gt;
&lt;p&gt;Solution: the broker-layer master cluster model eliminated the proxy layer. SDK-level metadata merging replaced the proxy hop, and new Kafka API features became available without proxy modifications.&lt;/p&gt;
&lt;h3 id=&quot;keyed-message-ordering-across-federated-clusters&quot;&gt;Keyed message ordering across federated clusters&lt;/h3&gt;
&lt;p&gt;Adding physical clusters to a federation could route messages with identical keys to different partitions, breaking ordering guarantees for key-partitioned topics.&lt;/p&gt;
&lt;p&gt;Tencent’s approach here was pragmatic rather than architectural: they noted that keyed messages are used only occasionally in their pipelines, and they accepted the trade-off in favour of scalability. A complete technical solution for ordering across federated partitions was not described in their public disclosures.&lt;/p&gt;
&lt;h3 id=&quot;historical-data-access-polluting-page-cache-ckafka&quot;&gt;Historical data access polluting page cache (CKafka)&lt;/h3&gt;
&lt;p&gt;Long-retention topics generated cold data reads that evicted hot data from cache, degrading SLAs for active consumers.&lt;/p&gt;
&lt;p&gt;Solution: tiered storage offloads cold segments to COS asynchronously, keeping the page cache available for hot data. Prefetching loads anticipated consumer reads into memory pools before they are requested.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Versions 2.4, 2.8, 3.2 supported in CKafka; PCG internal version not disclosed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster federation (Phase 1)&lt;/td&gt;
&lt;td&gt;pproxy / cproxy (custom)&lt;/td&gt;
&lt;td&gt;Producer and consumer proxy implementing the Kafka broker protocol; routes to physical clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster federation (Phase 2)&lt;/td&gt;
&lt;td&gt;Master cluster (custom broker-layer design)&lt;/td&gt;
&lt;td&gt;Aggregates sub-cluster metadata; central consumer group and transaction management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration and offset storage&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;Used in master cluster federation for disaster recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;Stateful processing; consumes from Kafka for real-time data warehouse and lake ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote storage (tiered)&lt;/td&gt;
&lt;td&gt;Tencent Cloud Object Storage (COS)&lt;/td&gt;
&lt;td&gt;Cold segment target in CKafka tiered storage architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data warehouse&lt;/td&gt;
&lt;td&gt;Hive / HBase / HDFS&lt;/td&gt;
&lt;td&gt;Downstream storage receiving data via Kafka and Flink pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed Kafka service&lt;/td&gt;
&lt;td&gt;CKafka (TDMQ for CKafka)&lt;/td&gt;
&lt;td&gt;Tencent Cloud’s managed offering; Kafka API-compatible (0.9-2.8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternative messaging (non-Kafka)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kenway Chen&lt;/strong&gt; (Tech Lead and Engineering Manager, Tencent PCG): Co-author of the &lt;a href=&quot;https://www.confluent.io/blog/tencent-kafka-process-10-trillion-messages-per-day/&quot;&gt;Confluent blog post&lt;/a&gt; describing PCG’s federated Kafka architecture (2020)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kahn Chen&lt;/strong&gt; (Software Architect, Tencent PCG): Co-author of the Confluent blog post; presented &lt;a href=&quot;https://www.kafka-summit.org/sessions/enhancing-apache-kafka-for-large-scale-real-time-data-pipeline-at-tencent/&quot;&gt;“Enhancing Apache Kafka for large-scale real-time data pipeline at Tencent”&lt;/a&gt; at Kafka Summit APAC 2021&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;George Shu&lt;/strong&gt; (General Manager, PCG Data Infrastructure and Platforms): Co-author of the Confluent blog post; proposer of &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-694:+Support+Reducing+Partitions+for+Topics&quot;&gt;KIP-694&lt;/a&gt; and &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-693:+Client-side+Circuit+Breaker+for+Partition+Write+Errors&quot;&gt;KIP-693&lt;/a&gt; to the Apache Kafka community&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Liang Wang (Thirteen Wang)&lt;/strong&gt; (Senior Engineer, Tencent): Co-presenter with Kahn Chen at &lt;a href=&quot;https://www.slideshare.net/slideshow/enhancing-apache-kafka-for-large-scale-realtime-data-pipeline-at-tencent-kahn-chen-and-thirteen-wang-tencent/249913050&quot;&gt;Kafka Summit APAC 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lu Shilin (鲁仕林)&lt;/strong&gt; (Kernel Lead, Tencent Cloud CKafka): Author of &lt;a href=&quot;https://juejin.cn/post/7313911560442249216&quot;&gt;“Kafka tiered storage practice and evolution at Tencent Cloud”&lt;/a&gt; (Juejin, December 2023)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Treat individual clusters as bounded capacity units, not infinite resources.&lt;/strong&gt; Tencent PCG found that growing individual clusters indefinitely was impractical. Their federation design treats each physical cluster as a bounded unit and routes logical topics across multiple clusters. If you are approaching partition or throughput ceilings, a federated or tiered architecture separates scaling concerns more cleanly than tuning a single cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate the control plane from the data plane early.&lt;/strong&gt; CKafka’s isolation of control operations (via the cloud API) from data operations (via the Kafka SDK) allowed Tencent Cloud to manage the managed service without exposing control-plane complexity to customers. For internal platforms, the same principle applies: provisioning, ACL management, and monitoring should not go through the same path as message production and consumption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model your traffic distribution before choosing partition counts.&lt;/strong&gt; Tencent’s experience with keyed messages across federated partitions highlights that key-based ordering and federation do not compose cleanly. If your pipelines depend on key-partitioned ordering, design for that constraint at the federation boundary before scaling out clusters, rather than discovering the incompatibility under load.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribute upstream when you need features that standard Kafka does not provide.&lt;/strong&gt; KIP-693 and KIP-694 both address real operational problems Tencent encountered at scale: partition write isolation and lossless partition reduction. Submitting KIPs rather than maintaining private forks keeps the internal codebase closer to upstream and benefits the broader community with solutions that have been tested against one of the world’s largest Kafka deployments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design tiered storage for access pattern isolation, not just cost reduction.&lt;/strong&gt; Tencent Cloud’s CKafka tiered storage work shows that the primary motivation was cache isolation: cold data reads were evicting hot data and degrading SLAs. If you are evaluating Kafka tiered storage, measure its impact on page cache hit rates for active consumers as the primary success metric, not just storage cost savings.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kenway Chen, Kahn Chen, George Shu. “&lt;a href=&quot;https://www.confluent.io/blog/tencent-kafka-process-10-trillion-messages-per-day/&quot;&gt;How Tencent PCG scales massive data pipelines with Apache Kafka&lt;/a&gt;.” Confluent Blog, 2020-08-03.&lt;/li&gt;
&lt;li&gt;Kahn Chen, Liang Wang. “&lt;a href=&quot;https://www.kafka-summit.org/sessions/enhancing-apache-kafka-for-large-scale-real-time-data-pipeline-at-tencent/&quot;&gt;Enhancing Apache Kafka for large-scale real-time data pipeline at Tencent&lt;/a&gt;.” Kafka Summit APAC 2021, 2021-07-28.&lt;/li&gt;
&lt;li&gt;Kahn Chen, Liang Wang. &lt;a href=&quot;https://www.slideshare.net/slideshow/enhancing-apache-kafka-for-large-scale-realtime-data-pipeline-at-tencent-kahn-chen-and-thirteen-wang-tencent/249913050&quot;&gt;Kafka Summit APAC 2021 slide deck (SlideShare)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;George Shu. &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-694:+Support+Reducing+Partitions+for+Topics&quot;&gt;KIP-694: Support Reducing Partitions for Topics&lt;/a&gt;. Apache Kafka KIP wiki, 2021-03.&lt;/li&gt;
&lt;li&gt;George Shu. &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-693:+Client-side+Circuit+Breaker+for+Partition+Write+Errors&quot;&gt;KIP-693: Client-side Circuit Breaker for Partition Write Errors&lt;/a&gt;. Apache Kafka KIP wiki, 2021-06.&lt;/li&gt;
&lt;li&gt;Lu Shilin. “&lt;a href=&quot;https://juejin.cn/post/7313911560442249216&quot;&gt;Kafka tiered storage practice and evolution at Tencent Cloud&lt;/a&gt;.” Juejin, 2023-12-19.&lt;/li&gt;
&lt;li&gt;Tencent Cloud. “&lt;a href=&quot;https://www.tencentcloud.com/document/product/597/11751&quot;&gt;Comparison with Apache Kafka&lt;/a&gt;.” Tencent Cloud documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want visibility into your own Kafka clusters, consumer lag, and topic throughput without building custom dashboards from scratch, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a 30-day free trial. You can connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Wix uses Apache Kafka in production</title><link>https://factorhouse.io/articles/wix-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/wix-kafka-architecture/</guid><description>A deep-dive into Wix&apos;s Kafka architecture: 66 billion daily messages, 2,200+ microservices, the Greyhound SDK, Confluent Cloud migration, and operating 500,000+ partitions across 4 regions.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wix runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; as the event backbone for more than 2,200 microservices, processing 66 billion messages every day across 50,000 topics and 500,000 partitions. The scale is notable, but the more interesting engineering story is how Wix achieved it: by building Greyhound, an open-source Kafka SDK that abstracts the native client for every service in the fleet, and by migrating the entire platform to Confluent Cloud in 2021 without taking a single service offline.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Wix is a cloud-based website creation and hosting platform used by more than 200 million registered users worldwide. At its core, the platform is a collection of loosely coupled product surfaces: site builder, e-commerce, bookings, blog, CRM, and several hundred third-party app integrations. Serving those surfaces requires Wix’s engineering organisation to coordinate over 2,200 microservices, deployed across 18 data centres and points of presence, spanning two cloud providers (Google Cloud and AWS) and four geographic regions.&lt;/p&gt;
&lt;p&gt;Kafka became the communication layer for those services as the microservices architecture matured. By 2020, 1,500 services were exchanging events through Kafka. By the time Natan Silnitsky presented at Kafka Summit London in April 2022, that number had grown to 2,200+ services, 50,000 topics, and 500,000+ partitions, handling 15 billion business events per day. The 66 billion total daily message count includes the internal infrastructure traffic that flows through Kafka alongside business events.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greyhound open-sourced on GitHub (v0.0.5)&lt;/td&gt;
&lt;td&gt;June 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering post: Kafka as event-driven system for 1,500 microservices&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration of 2,000 microservices to multi-cluster Confluent Cloud, zero downtime&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Summit Americas: “5 lessons learned migrating to Confluent Cloud”&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Summit London keynote + “Kafka-based global data mesh at Wix” talk&lt;/td&gt;
&lt;td&gt;April 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale reaches 2,200+ microservices, 50K topics, 500K+ partitions, 15B daily business events&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online ML feature store rebuilt on Kafka and Flink, supporting 3,000+ features&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;wixs-kafka-use-cases&quot;&gt;Wix’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;microservices-event-driven-communication&quot;&gt;Microservices event-driven communication&lt;/h3&gt;
&lt;p&gt;The primary use of Kafka at Wix is replacing synchronous RPC between services with asynchronous events. Rather than service A calling service B directly, A publishes a domain event to a Kafka topic and any interested downstream service subscribes. This decoupling allows Wix to evolve services independently and absorb traffic spikes without cascading failures across the dependency graph.&lt;/p&gt;
&lt;h3 id=&quot;materialized-views-for-query-offloading&quot;&gt;Materialized views for query offloading&lt;/h3&gt;
&lt;p&gt;One concrete problem Kafka solved was query volume on Wix’s MetaSite service, which holds metadata for every site on the platform: owner, version, and installed applications. Before Kafka-based projections, this single service was receiving over 1 million requests per minute from other services that needed a slice of that data.&lt;/p&gt;
&lt;p&gt;The solution was to stream all MetaSite change events to a Kafka topic and let each downstream consumer build its own purpose-built read model. Services that previously queried MetaSite directly now maintain a local projection that is updated via Kafka, reducing their dependency to an eventual-consistency relationship rather than a real-time RPC call.&lt;/p&gt;
&lt;h3 id=&quot;change-data-capture&quot;&gt;Change data capture&lt;/h3&gt;
&lt;p&gt;Wix uses Debezium connectors to capture row-level database changes and publish them as Kafka events. Services downstream can consume the CDC topic and produce their own clean domain event contracts rather than exposing raw database schemas as the inter-service API.&lt;/p&gt;
&lt;h3 id=&quot;protobuf-backed-api-contracts&quot;&gt;Protobuf-backed API contracts&lt;/h3&gt;
&lt;p&gt;At Wix, a Kafka topic schema is an API contract. All Kafka messages are serialised with Protobuf, and the &lt;code&gt;.proto&lt;/code&gt; file is the canonical definition shared across Kafka, gRPC, and external-facing APIs. This means a new domain can be onboarded and integrated with surrounding services within a few days.&lt;/p&gt;
&lt;h3 id=&quot;real-time-ml-feature-store&quot;&gt;Real-time ML feature store&lt;/h3&gt;
&lt;p&gt;Wix rebuilt its online feature store around Kafka and Apache Flink. The system processes billions of events daily and maintains over 3,000 ML features with near-real-time update latency, feeding personalisation and recommendation models across the platform.&lt;/p&gt;
&lt;h3 id=&quot;global-data-mesh&quot;&gt;Global data mesh&lt;/h3&gt;
&lt;p&gt;Wix’s platform spans four regions and two cloud providers. Rather than each service knowing where to find its upstreams, Kafka provides the transport layer for a global data mesh. Services publish to a logical topic namespace and Kafka handles the cross-region replication, meaning producers and consumers remain unaware of the underlying geographic topology.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total daily Kafka messages&lt;/td&gt;
&lt;td&gt;66 billion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily business events (data mesh)&lt;/td&gt;
&lt;td&gt;15 billion across 4 regions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microservices using Kafka&lt;/td&gt;
&lt;td&gt;2,200+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;~50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitions&lt;/td&gt;
&lt;td&gt;500,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geographic regions&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data centres and POPs&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud providers&lt;/td&gt;
&lt;td&gt;2 (Google Cloud, AWS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developers interacting with Kafka daily&lt;/td&gt;
&lt;td&gt;900+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;wixs-kafka-architecture&quot;&gt;Wix’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;the-greyhound-sdk&quot;&gt;The Greyhound SDK&lt;/h3&gt;
&lt;p&gt;No Wix service talks to Kafka through the native client directly. Every JVM service goes through Greyhound, the open-source Kafka SDK that Wix’s data streaming team built and maintains. Greyhound is written in Scala using the ZIO functional library and adds a higher-level interface on top of the native Kafka client, providing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Concurrent message processing:&lt;/strong&gt; configurable parallelism per consumer handler, not limited by partition count&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retry policies:&lt;/strong&gt; first-class retry configuration on both consumers and producers, using dedicated retry topics with back-off rather than blocking partition processing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch processing:&lt;/strong&gt; optional batch consumption for throughput-sensitive pipelines&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context propagation and metrics:&lt;/strong&gt; distributed tracing context forwarded through events; Prometheus gauges emitted per topic-consumer-handler&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For non-JVM services, Greyhound ships as a sidecar proxy. Services that are not on the JVM send and receive via the Greyhound proxy over gRPC, which then handles the Kafka interaction on their behalf. This is also what made the Confluent Cloud migration possible at scale: routing logic and cluster addressing only had to be updated in Greyhound, not in each of the 2,200 services.&lt;/p&gt;
&lt;p&gt;The Greyhound repository is available at &lt;a href=&quot;https://github.com/wix/greyhound&quot;&gt;github.com/wix/greyhound&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;Wix does not use Confluent Schema Registry. When Wix evaluated it, the registry did not fit their requirement to unify Kafka event schemas, gRPC service contracts, and external API definitions in a single consistent system. Instead, Wix’s infrastructure and developer experience teams built a custom schema management platform backed by Protobuf. The platform provides automatic schema discovery, serialisation, validation, and compatibility enforcement across all three communication paradigms. The &lt;code&gt;.proto&lt;/code&gt; file is the single source of truth for any data contract, regardless of whether the transport is Kafka, gRPC, or an HTTP API.&lt;/p&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;Wix runs on multi-cluster Confluent Cloud. Before 2021 the platform ran self-hosted Kafka clusters; the migration to managed Confluent is covered in the Challenges section below.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink is used for stateful stream processing over Kafka topics. Wix runs Flink on Confluent Cloud in serverless mode, using FlinkSQL for complex aggregations and feature transformations for the ML feature store. Flink reads from and writes to Kafka topics directly.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Kafka messages are Protobuf-serialised, with Greyhound handling serialisation through its default serialiser, which accepts Protobuf-derived Scala case classes. Producer configuration is managed centrally through Greyhound rather than in individual services.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer group configuration, offset management, and retry topic routing are all managed through Greyhound. Lag monitoring is handled through the internal Kafka Control Plane, described in the Operations section. Greyhound emits a Prometheus gauge per topic-consumer-handler showing the currently longest-running handler in each pod, which feeds directly into lag dashboards.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Wix uses Debezium as a Kafka Connect source connector for CDC, capturing database change events and publishing them as Kafka topics that downstream services can consume.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;grpc-fan-out-proxy&quot;&gt;gRPC fan-out proxy&lt;/h3&gt;
&lt;p&gt;High-volume topics consumed by many services were a meaningful driver of Confluent Cloud costs. Wix addressed this by building a push-based gRPC fan-out proxy. Instead of each subscribing service maintaining its own Kafka consumer for a shared topic, the proxy consumes each topic once and fans out delivery to N subscribers over gRPC streaming connections. This removed redundant consumer groups from Confluent and reduced Wix’s Kafka infrastructure bill by 30%.&lt;/p&gt;
&lt;h3 id=&quot;chunked-message-delivery&quot;&gt;Chunked message delivery&lt;/h3&gt;
&lt;p&gt;Kafka’s default maximum message size is 1 MB. Some Wix payloads, such as large site content objects, occasionally exceeded this limit. Rather than raising &lt;code&gt;message.max.bytes&lt;/code&gt; cluster-wide, Wix’s engineer Amit Pe’er built a Chunks Producer/Consumer pattern. The producer splits an oversized message into fragments, persists each fragment to a local H2 database on disk, and sends only the chunk IDs over Kafka. The consumer retrieves chunks by ID, with retry logic that handles out-of-order arrival, and reassembles the original message before passing it to the handler. The Kafka topic carries only small identifier messages; the bulk data moves through the local disk store.&lt;/p&gt;
&lt;h3 id=&quot;zero-downtime-migration-to-confluent-cloud&quot;&gt;Zero-downtime migration to Confluent Cloud&lt;/h3&gt;
&lt;p&gt;The 2021 migration from self-hosted Kafka to Confluent Cloud moved 10,000 topics and 100,000+ partitions serving 2,000 microservices without any service downtime. The approach relied on three decisions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Centralised routing in Greyhound:&lt;/strong&gt; because all Kafka interaction goes through the SDK or proxy, cluster addressing only needed to change in one place&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replication bridge:&lt;/strong&gt; a dedicated replication service mirrored messages from the self-hosted cluster to Confluent Cloud while consumers were being cut over, so no messages were lost during the transition window&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gradual per-topic cutover:&lt;/strong&gt; topics were migrated in small batches rather than all at once, limiting blast radius and making per-topic rollback straightforward&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Individual service owners made no code changes during the migration.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;consumer-lag-tooling-tllsr&quot;&gt;Consumer lag tooling: TLLSR&lt;/h3&gt;
&lt;p&gt;Wix organises its Kafka operational tooling into five capabilities, described internally as TLLSR:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trace:&lt;/strong&gt; follow a specific message through the processing pipeline to diagnose where it was delayed or dropped&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lookup:&lt;/strong&gt; retrieve a specific event by key from a topic, useful for investigating individual record issues&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Longest-Running:&lt;/strong&gt; identify the slowest currently active consumer handler across the fleet, surfaced via the Prometheus gauge Greyhound emits per pod&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip:&lt;/strong&gt; advance the consumer offset past a stuck message, used when a specific record is causing repeated processing failures and the data loss is acceptable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redistribute:&lt;/strong&gt; rebalance events from a lagging partition across all partitions, resolving single-partition lag caused by hot key distribution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These capabilities are available through Wix’s internal Kafka Control Plane UI, which includes a dedicated Consumers Lag View that developers use to investigate root causes across all 2,200 services from a single interface.&lt;/p&gt;
&lt;h3 id=&quot;monitoring&quot;&gt;Monitoring&lt;/h3&gt;
&lt;p&gt;Greyhound emits Prometheus metrics for each topic-consumer-handler pair, including the longest-running handler currently active in each pod. These feed into the lag dashboards in the Control Plane. The combination of per-pod handler metrics and the TLLSR tooling means developers can diagnose most consumer lag issues without needing to inspect broker-side metrics directly.&lt;/p&gt;
&lt;h3 id=&quot;schema-governance&quot;&gt;Schema governance&lt;/h3&gt;
&lt;p&gt;Schema compatibility is enforced automatically through Wix’s custom schema platform. New schemas are reviewed before publishing, and breaking changes are caught at the tooling layer rather than at runtime. Because the same &lt;code&gt;.proto&lt;/code&gt; files serve both Kafka and gRPC, schema governance covers the full inter-service communication surface in one process.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;single-partition-consumer-lag&quot;&gt;Single-partition consumer lag&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Events with poorly distributed keys concentrated all traffic on one partition, while adjacent partitions sat idle. The lagging partition accumulated a backlog that the consumer could not drain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Key design that did not distribute cardinality evenly across the partition space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Wix built the Redistribute tool inside Greyhound, which takes events from the lagging partition and spreads them across all partitions. This restores the parallel processing that the consumer handler was configured for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Operators can resolve single-partition lag incidents through the Control Plane UI without code changes or consumer restarts.&lt;/p&gt;
&lt;h3 id=&quot;slow-consumer-handlers-blocking-a-partition&quot;&gt;Slow consumer handlers blocking a partition&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; One slow or stuck consumer handler blocked all subsequent messages in the same partition. Because Kafka delivers messages in order within a partition, a handler that does not complete its work holds up everything behind it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; An unexpected downstream slowdown, typically a slow database call or a downstream service timeout, that caused handler processing time to spike.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Longest-Running monitor in the Control Plane surfaces the offending handler and pod. For cases where the stuck message cannot be re-processed successfully, the Skip tool advances the offset past it. For higher-value flows where skipping is not acceptable, Greyhound’s retry policy routes the message to a retry topic, unblocking the main partition while the retry works in the background.&lt;/p&gt;
&lt;h3 id=&quot;migrating-2000-services-to-confluent-cloud-without-downtime&quot;&gt;Migrating 2,000 services to Confluent Cloud without downtime&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Wix’s self-hosted Kafka clusters had grown to 10,000 topics and 100,000+ partitions. Moving to managed Kafka without involving the owners of 2,000 individual services required a migration approach that was transparent to producers and consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Self-hosted operational overhead had grown to the point where the engineering investment in cluster management outweighed its benefits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Greyhound abstraction was the enabling factor. Because routing and cluster addressing were centralised in the SDK and proxy, the migration team updated one layer rather than 2,000 services. A replication bridge kept data flowing from old to new clusters during the cutover window, and per-topic gradual migration kept blast radius small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Zero downtime, no service code changes required.&lt;/p&gt;
&lt;h3 id=&quot;schema-management-across-kafka-and-grpc&quot;&gt;Schema management across Kafka and gRPC&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Wix needed to govern event schemas for Kafka topics, gRPC service contracts, and external APIs in a unified way. Confluent Schema Registry did not support this multi-paradigm requirement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Wix’s architecture uses Kafka, gRPC, and HTTP as first-class transport protocols, and each had historically been managed separately, creating drift and duplication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A custom schema management platform, backed by Protobuf, that treats the &lt;code&gt;.proto&lt;/code&gt; file as the single authoritative schema for all three transports. Automatic discovery, compatibility checking, and developer tooling are built on top of this layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; A new domain can be onboarded and fully integrated with surrounding services in a few days, down from weeks when schemas were managed separately per protocol.&lt;/p&gt;
&lt;h3 id=&quot;kafka-bill-from-fan-out-consumption&quot;&gt;Kafka bill from fan-out consumption&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; High-volume topics consumed by many services created redundant consumer groups on Confluent Cloud, each paying for the same data repeatedly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Each subscribing service maintained its own Kafka consumer, which is the natural pattern for Kafka but expensive when the same high-volume topic has many subscribers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A push-based gRPC fan-out proxy that consumes each high-volume topic once and delivers to all subscribers over gRPC streaming, eliminating the redundant consumer groups.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; 30% reduction in Kafka infrastructure costs.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (Confluent Cloud, multi-cluster)&lt;/td&gt;
&lt;td&gt;Core event streaming platform; multi-region deployment across Google Cloud and AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client SDK&lt;/td&gt;
&lt;td&gt;Greyhound (Wix open source, Scala/ZIO)&lt;/td&gt;
&lt;td&gt;High-level Kafka SDK for all JVM services; sidecar proxy for non-JVM services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink (Confluent Cloud serverless)&lt;/td&gt;
&lt;td&gt;Stateful stream processing for the online ML feature store; FlinkSQL for aggregations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Protobuf&lt;/td&gt;
&lt;td&gt;Universal message format across Kafka, gRPC, and external APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema management&lt;/td&gt;
&lt;td&gt;Custom internal platform&lt;/td&gt;
&lt;td&gt;Unified Protobuf schema governance for Kafka, gRPC, and HTTP API contracts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connector&lt;/td&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;Captures database change events and publishes them to Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large-message storage&lt;/td&gt;
&lt;td&gt;H2 (embedded)&lt;/td&gt;
&lt;td&gt;On-disk chunk store for the Chunks Producer/Consumer pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;Consumer handler lag metrics emitted by Greyhound, per topic-consumer-handler per pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-service delivery&lt;/td&gt;
&lt;td&gt;gRPC&lt;/td&gt;
&lt;td&gt;Fan-out delivery from the Kafka proxy to downstream subscriber services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;Google Cloud + AWS&lt;/td&gt;
&lt;td&gt;Multi-cloud hosting for Kafka clusters and compute across 4 regions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Natan Silnitsky&lt;/td&gt;
&lt;td&gt;Backend-Infra Engineer, Data Streaming Team&lt;/td&gt;
&lt;td&gt;Primary architect of Wix’s Kafka infrastructure; creator and lead of Greyhound; presenter at Kafka Summit Americas 2021 and Kafka Summit London 2022. Author of most of the Wix Kafka engineering blog series.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avi Perez&lt;/td&gt;
&lt;td&gt;Engineering leader, Wix&lt;/td&gt;
&lt;td&gt;Featured in the Kafka Summit London 2022 keynote presenting Wix’s approach to modern data flow and data pipelines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amit Pe’er&lt;/td&gt;
&lt;td&gt;Engineer, Wix&lt;/td&gt;
&lt;td&gt;Author of the Chunks Producer/Consumer pattern for handling oversized Kafka messages.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralise your Kafka client layer early.&lt;/strong&gt; Wix’s ability to migrate 2,000 services to Confluent Cloud without touching individual service code came directly from having a shared SDK that all services used. If each service had configured Kafka independently, the migration would have required coordinating changes across the entire engineering organisation. A shared client layer is worth the investment before you need to do something like this, not after.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat Kafka topic schemas as API contracts, not implementation details.&lt;/strong&gt; Wix uses the same Protobuf &lt;code&gt;.proto&lt;/code&gt; file as the contract for Kafka events, gRPC calls, and external APIs. This eliminates schema drift between communication protocols and makes onboarding new domains faster because the tooling and governance are the same regardless of transport.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer lag tooling needs both visibility and remediation.&lt;/strong&gt; Wix found that monitoring consumer lag is not enough on its own. Their TLLSR tooling pairs each monitoring capability (Trace, Lookup, Longest-Running) with a corresponding action (Skip, Redistribute). Without the remediation side, engineers can diagnose problems but still need to write ad-hoc scripts or perform manual offset management to resolve them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fan-out patterns can meaningfully reduce managed Kafka costs.&lt;/strong&gt; If multiple services consume the same high-volume topic, maintaining separate consumer groups for each scales your Confluent Cloud costs linearly with the number of subscribers. A push-based fan-out proxy, consuming each topic once, can reduce that cost significantly as Wix found with their 30% bill reduction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration to managed Kafka is feasible at large scale with the right abstraction.&lt;/strong&gt; The routing of 10,000 topics and 100,000+ partitions from self-hosted clusters to Confluent Cloud, with zero downtime, was made tractable by having a single layer where cluster addressing lived. Any organisation considering a similar move should audit how many places in their codebase hold Kafka connection configuration before starting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/listen-using-apache-kafka-as-the-event-driven-system-for-1-500-microservices-at-wix-fe9bd5bce7a7&quot;&gt;Using Apache Kafka as the event-driven system for 1,500 microservices at Wix&lt;/a&gt; (Wix Engineering / Medium, 2020)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/migrating-to-a-multi-cluster-managed-kafka-with-0-downtime-b936655f888e&quot;&gt;Migrating to a multi-cluster managed Kafka with 0 downtime&lt;/a&gt; (Wix Engineering / Medium, 2021)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/how-wix-manages-schemas-for-kafka-and-grpc-used-by-2000-microservices-2117416ea17b&quot;&gt;How Wix manages schemas for Kafka (and gRPC) used by 2,000 microservices&lt;/a&gt; (Wix Engineering / Medium, 2021)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://www.confluent.io/en-gb/events/kafka-summit-americas-2021/5-lessons-learned-for-successful-migration-to-confluent-cloud-ksam21/&quot;&gt;5 lessons learned for successful migration to Confluent Cloud&lt;/a&gt; (Kafka Summit Americas 2021)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-london-2022/kafka-based-global-data-mesh-at-wix/&quot;&gt;Kafka-based global data mesh at Wix&lt;/a&gt; (Kafka Summit London 2022)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/troubleshooting-kafka-for-2000-microservices-at-wix-986ee382fd1e&quot;&gt;Troubleshooting Kafka for 2,000 microservices at Wix&lt;/a&gt; (Wix Engineering / Medium, 2022)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/6-event-driven-architecture-patterns-part-1-93758b253f47&quot;&gt;6 event-driven architecture patterns, part 1&lt;/a&gt; (Wix Engineering / Medium, 2021)&lt;/li&gt;
&lt;li&gt;Natan Silnitsky — &lt;a href=&quot;https://medium.com/wix-engineering/6-event-driven-architecture-patterns-part-2-455cc73b22e1&quot;&gt;6 event-driven architecture patterns, part 2&lt;/a&gt; (Wix Engineering / Medium, 2021)&lt;/li&gt;
&lt;li&gt;Amit Pe’er — &lt;a href=&quot;https://medium.com/wix-engineering/chunks-producer-consumer-f97a834df00d&quot;&gt;Chunks producer/consumer: handling large messages within Kafka&lt;/a&gt; (Wix Engineering / Medium, 2021)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/wix/greyhound&quot;&gt;Greyhound — rich Kafka client library&lt;/a&gt; (Wix GitHub, 2020–present)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want visibility into your own Kafka consumers, lag, and message throughput, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a single interface for monitoring and managing Kafka clusters. You can connect it to any cluster, including Confluent Cloud, and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>Conduktor: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/conduktor/</link><guid isPermaLink="true">https://factorhouse.io/articles/conduktor/</guid><description>Conduktor review for 2026: pricing, strengths, deployment trade-offs, and how it compares to alternatives for enterprise Kafka governance teams.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Conduktor is a commercial Kafka governance platform built around RBAC, self-service topic workflows, and a proxy (Gateway) that enforces encryption and policy at the wire level without modifying producer or consumer code.&lt;/li&gt;
&lt;li&gt;Its strongest use cases are multi-team enterprise environments where governance, compliance (PCI DSS, HIPAA, GDPR), and self-service are priorities.&lt;/li&gt;
&lt;li&gt;Per-seat pricing ($1,200/seat/year on the Team Edition) becomes a significant line item for teams above 20-30 users; at 100 users across three clusters, estimated list price reaches $80,000-$150,000 per year.&lt;/li&gt;
&lt;li&gt;The free Community tier has been incrementally restricted: version 1.43 reduced the number of allowed servers and users, and connecting to any SSL-enabled or multi-broker cluster requires a paid plan. Community sentiment on Reddit consistently describes the free tier as no longer viable for startup or staging environments. [r/apachekafka, multiple threads, 2023-2026]&lt;/li&gt;
&lt;li&gt;Known gaps include the absence of distributed tracing, no integration with Azure’s native Schema Registry, and an operational overhead introduced by the Gateway proxy (2-10ms added latency, a required PostgreSQL 13+ dependency, and a single point of failure to manage for high availability).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is worth evaluating if per-cluster pricing, a stateless deployment model, proven stability on large partitioned clusters, or WCAG-compliant UI matters to your team.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-conduktor&quot;&gt;What is Conduktor?&lt;/h2&gt;
&lt;p&gt;Conduktor is a commercial Kafka management and governance platform. It has two main components: Console, a React-based web UI for managing topics, schemas, connectors, consumer groups, and access controls across multiple clusters; and Gateway, a Kafka proxy that sits between clients and brokers to enforce encryption, data masking, quota policies, and multi-tenancy rules at the wire level without requiring application changes.&lt;/p&gt;
&lt;p&gt;The product was originally distributed as a JavaFX desktop application. In 2023, Conduktor shifted its focus to Console (its centrally deployed web platform) and deprecated both the desktop application (end of life at end of 2025) and its Testing product. Conduktor achieved SOC2 Type II certification in 2023 and has grown its enterprise feature set — LDAP, RBAC, audit logging, and schema registry integrations — in response to requirements from regulated-industry customers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722b1f17be485095adbee_conduktor-blog-screenshot.avif&quot; alt=&quot;Conduktor&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conduktor-review&quot;&gt;Conduktor review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Conduktor’s core functionality covers the expected surface area for enterprise Kafka management: topic creation and management, consumer group inspection, schema registry integration (Confluent-compatible and AWS Glue), Kafka Connect management with a UI wizard for connector deployment, ksqlDB integration, and multi-cluster support.&lt;/p&gt;
&lt;p&gt;The Gateway is the more differentiated component. It enables field-level encryption, data masking, and policy enforcement at the wire level. Key management integrates with AWS KMS, Azure Key Vault, GCP Cloud KMS, HashiCorp Vault, and Fortanix. The Topic as a Service feature — which lets teams self-serve topic creation within defined policy guardrails — received what Conduktor’s CTO described as “overwhelming praise” at Kafka Summit London 2024. [Stéphane Derosiaux, Medium/Conduktor, April 2024]&lt;/p&gt;
&lt;p&gt;The Gateway also provides capabilities that extend beyond basic governance. In disaster recovery scenarios, the proxy architecture allows platform teams to failover backend cluster connections dynamically without requiring any client-side reconfiguration or application restarts — a meaningful operational advantage for multi-region deployments. [r/apachekafka, “DR for Kafka Cluster,” January 2025] The Gateway additionally supports virtual topic filtering, which executes server-side filtering logic without spinning up dedicated stream processors or creating physical duplicate topics. Practitioners evaluating this approach on Reddit noted it reduces network bandwidth and client-side processing load compared to a Kafka Streams or Flink implementation for the same problem. [r/apachekafka, “Is there any way to perform server-side filtering?”, August 2024]&lt;/p&gt;
&lt;p&gt;There are two documented functional gaps. First, Conduktor has no native distributed tracing capability, which prevents it from being considered a complete observability platform. [Damaso Sanoja, Redpanda blog, March 2023] Second, while Azure Event Hubs is accessible via Kafka protocol compatibility, Azure’s native Schema Registry is not integrated — teams using Azure Event Hubs cannot access schema features within Conduktor Console. [Farbod Ahmadian, DataChef blog, November 2024]&lt;/p&gt;
&lt;p&gt;One operational nuance: when Console’s internal index is incomplete or stale, it falls back to querying the Kafka cluster directly, which can result in slow page loads. [Conduktor support documentation, undated] The monitoring graph also averages brief metric spikes across large time windows, making short-lived spikes invisible at wider zoom levels. [Conduktor documentation, undated]&lt;/p&gt;
&lt;p&gt;The Conduktor Testing product was deprecated in 2023 despite customer adoption, and the Terraform provider’s experimental generic resource is explicitly not recommended for production use.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Conduktor Console requires PostgreSQL 13+ as an external dependency — this is not optional. Minimum resource requirements are 2 CPU and 3 GB RAM for Console, plus 2 CPU and 4 GB RAM for Gateway if you are running the proxy component. A Kubernetes/Helm path is documented and functional. Docker deployment is supported; the image was reduced from 1.66 GB to 800 MB in 2023 as part of a documented effort to simplify onboarding. [Conduktor retrospective, 2023]&lt;/p&gt;
&lt;p&gt;The retirement of the native desktop client has been a consistent source of friction for solo developers and small teams. Under the desktop model, installation required a standard ZIP extraction or an MSI installer. The Console model requires installing WSL2, configuring Docker on a Linux subsystem, downloading a specific compose file, placing it in a precisely named folder to avoid container configuration errors, and running container operations from the command line. Community commentary characterises this as an unreasonable setup burden for a tool whose primary use case — inspecting a local topic — does not require a centrally deployed web application. [r/apachekafka, “Open source clone of Conduktor Desktop,” May 2026] The consensus among practitioners on Reddit is that the Console architecture is well-suited to multi-tenant teams deploying to a shared server with Okta and RBAC, but it represents a meaningful barrier to entry for individual developers. This friction has driven some users toward native desktop alternatives and lightweight open-source web UIs that carry no containerisation dependency. [r/apachekafka, “I built a free, open-source desktop Kafka client,” 2025]&lt;/p&gt;
&lt;p&gt;The Gateway introduces latency overhead: typically 2-10ms per message (1-5ms network hop plus 0.5-5ms message processing). For sub-10ms latency requirements, this overhead may be prohibitive. The proxy also becomes a potential single point of failure and requires HA deployment planning. [Kai Waehner, personal blog, October 2025]&lt;/p&gt;
&lt;p&gt;Conduktor Cloud (their SaaS offering) failed to gain meaningful traction. In their 2023 retrospective, they attributed this to customers preferring on-premise deployments for security and privacy reasons, and to the absence of VPC peering and local agent support. Active Directory integration requires a specific workaround — setting the LDAP search filter to &lt;code&gt;(sAMAccountName={0})&lt;/code&gt; — when the default configuration returns an “invalid user” error.&lt;/p&gt;
&lt;p&gt;Connecting Conduktor to a Strimzi-managed Kafka Connect cluster requires exposing the Connect REST service and disabling KafkaConnector resources. Practitioners have reported HTTP 500 errors and permission denied errors when attempting this integration without the workaround. [fathimaSheikh and mozarik, GitHub/Strimzi Discussions #8543, May-June 2023]&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;RBAC is Conduktor’s most consistently cited strength. The Console logs every user action — including produce, consume, and admin requests — across 70+ event types with user identity, IP address, timestamp, topic, and partition. [Conduktor documentation, undated]&lt;/p&gt;
&lt;p&gt;SSO via OIDC and LDAP is available on all tiers including Community. SAML 2.0 requires the Enterprise plan. One independent comparison notes LDAP integration as “limited” relative to OIDC. [Hayato Shimizu, AxonOps blog, December 2025]&lt;/p&gt;
&lt;p&gt;The Gateway’s field-level encryption and data masking operate at the wire level, meaning producers and consumers do not need to be modified. Key management integrates with AWS KMS, Azure Key Vault, GCP Cloud KMS, HashiCorp Vault, and Fortanix. The Gateway can additionally function as a secure intermediary for managed services like Confluent Cloud, allowing organisations to enforce custom security policies and access controls on top of a cloud-hosted cluster without modifying client applications. [r/apachekafka, “Is anyone using Confluent Cloud on a private dedicated network?”, 2023] [Conduktor documentation and product pages, undated]&lt;/p&gt;
&lt;p&gt;The Gateway’s proxy architecture has drawn architectural commentary from competitors: a Lenses employee raised concerns that placing a component between clients and brokers “may add complexity and risk.” [Marios, Lenses Community Forum, January 2026] This is a trade-off worth evaluating.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;Console uses a React-based interface. Practitioners describing it on Product Hunt have called the search function fast and praised the overall UX. [Mark Shannon, Johan Netzler, Product Hunt, ~2023].&lt;/p&gt;
&lt;p&gt;Community feedback on Reddit is consistent with this assessment on the UX itself: a senior engineer described their personal experience as “highly positive due to its excellent user experience.” The same thread noted that their organisation abandoned the tool because of licensing fees rather than any functional dissatisfaction — a pattern that appears repeatedly across practitioner forums. [r/apachekafka, “The best Kafka Management tool,” 2025] The UI is widely regarded as Conduktor’s strongest attribute; the points of contention are almost entirely commercial and architectural rather than functional.&lt;/p&gt;
&lt;p&gt;One independent reviewer notes that the “desktop app heritage shows in architecture,” though this appears to be a reference to product lineage rather than a specific UI deficiency. [Hayato Shimizu, AxonOps blog, December 2025]&lt;/p&gt;
&lt;p&gt;The one documented UI limitation is the monitoring graph behaviour noted above: brief spikes averaged across a wide time window render as zero, making them invisible without narrowing the time range. [Conduktor documentation, undated]&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Conduktor integrates with AWS MSK (including IAM authentication, eliminating long-lived API key exposure), Confluent Platform and Cloud, Redpanda, Aiven, and Strimzi. Confluent-compatible and AWS Glue Schema Registries are both supported. ksqlDB and Kafka Connect management are included in the Console UI. A formal partnership integration with Redpanda positions Conduktor as a governance and management layer on top of Redpanda clusters. [Stéphane Derosiaux, Medium/Conduktor, undated]&lt;/p&gt;
&lt;p&gt;The Azure gap is documented and specific: Azure Event Hubs is reachable via Kafka protocol compatibility, but Azure’s native Schema Registry is not integrated. [Farbod Ahmadian, DataChef blog, November 2024] Apache Flink support was not documented in any source reviewed for this article. [UNVERIFIED — needs source]&lt;/p&gt;
&lt;p&gt;A Terraform provider is available for GitOps workflows. The experimental generic resource within that provider is not recommended for production. [Conduktor documentation, undated]&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Enterprise tier customers receive a dedicated Solutions Engineering team.&lt;/p&gt;
&lt;p&gt;Community tier users receive public documentation, a community Slack, and best-effort email support. Conduktor switched from Intercom to Zendesk for customer support tooling in 2023, which they described as resulting in a “significant improvement in Quality of Service.” [Conduktor retrospective, 2023]&lt;/p&gt;
&lt;p&gt;No named negative reviews of Conduktor’s support quality were found on any third-party review platform at time of research. G2, Capterra, PeerSpot, GetApp, and SourceForge each had zero verified user reviews at the time of writing.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Conduktor suits organisations running Kafka at multi-team scale where governance, compliance, and self-service are primary requirements. The documented customer base skews toward regulated industries — financial services, logistics, and healthcare — where field-level encryption, audit logging across 70+ event types, and compliance certifications (SOC2 Type II) are prerequisites rather than nice-to-haves.&lt;/p&gt;
&lt;p&gt;Teams using AWS MSK who want IAM-native authentication and a governed self-service layer without building it in-house are a strong fit. Platform engineering teams managing Kafka access requests across 20 or more teams will find the Topic as a Service and ownership model design specifically relevant.&lt;/p&gt;
&lt;p&gt;It is less well-suited to small teams or startups. Community feedback on Reddit is consistent on this point: the free tier restrictions added in version 1.43 (fewer allowed servers and users) and the requirement for a paid plan to connect to any SSL-enabled or multi-broker cluster make it impractical for staging environment access during early development. [r/apachekafka, “The best Kafka Management tool,” 2025; r/apachekafka, “Free tools to connect to multi-broker/SSL-enabled clusters,” 2022] Other scenarios where alternatives deserve a closer look: teams primarily on Azure Event Hubs who need schema registry integration, organisations requiring a stateless deployment model, or use cases that require distributed tracing.&lt;/p&gt;
&lt;h2 id=&quot;conduktor-pricing&quot;&gt;Conduktor pricing&lt;/h2&gt;
&lt;p&gt;Conduktor operates a per-seat pricing model for its commercial tiers. A Community tier is available at no cost for teams getting started or running smaller deployments.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;The Team Edition is priced at approximately $1,200 per seat per year (around $32/user/month). For an organisation with 100 users across three clusters, estimated list price ranges from $80,000 to $150,000 per year. Enterprise pricing is negotiated directly and includes the dedicated Solutions Engineering team, SAML 2.0 SSO, and additional compliance features. Exact Enterprise pricing is not published.&lt;/p&gt;
&lt;p&gt;Per-seat pricing means that cost scales with headcount rather than with infrastructure. If your teams are growing faster than your cluster count, this is worth modelling explicitly before committing.&lt;/p&gt;
&lt;h3 id=&quot;free-tier-limitations&quot;&gt;Free tier limitations&lt;/h3&gt;
&lt;p&gt;The Community tier has become more restrictive over successive releases. Version 1.43 reduced the number of allowed servers and users on the free plan. More significantly, connecting to any cluster configured with SSL/TLS or consisting of more than one broker node requires a paid subscription — which means the free tier cannot connect to most staging or production-equivalent environments. Community practitioners have described this as the point at which the free tier became unviable for startup use. [r/apachekafka, “The best Kafka Management tool,” 2025; r/apachekafka, “Free tools to connect to multi-broker/SSL-enabled clusters,” 2022]&lt;/p&gt;
&lt;p&gt;In a public Reddit thread, Conduktor representatives characterised their licensing as “highly affordable” and attributed hesitation to confusion around volume discounts and floating licence options. [r/apachekafka, “Suggestions for UI for AWS managed Kafka?”, 2023] That framing is worth noting, but it does not address the specific free tier restrictions that prevent evaluation against secured clusters.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;Conduktor offers free sandboxes that allow evaluation without local Docker setup. A Community tier is available for teams that want to self-host without a commercial licence, subject to the tier restrictions noted above.&lt;/p&gt;
&lt;h2 id=&quot;conduktor-competitors-and-alternatives&quot;&gt;Conduktor competitors and alternatives&lt;/h2&gt;
&lt;p&gt;Conduktor occupies a reasonably well-defined segment — enterprise Kafka governance with a proxy component — but it competes across several dimensions with both open-source tools and commercial alternatives. Open-source tools like &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; and &lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat&lt;/a&gt; are viable for teams where developer experience is the primary requirement and governance is handled separately. Commercial tools like &lt;a href=&quot;/articles/lenses&quot;&gt;Lenses&lt;/a&gt; and &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; offer different trade-offs on pricing model, deployment architecture, and feature emphasis.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multi-team enterprise governance and compliance&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Conduktor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;RBAC, Topic as a Service, Gateway proxy (encryption, masking, virtual topic filtering, DR failover), schema registry, Kafka Connect, ksqlDB&lt;/td&gt;
&lt;td&gt;Self-hosted (requires PostgreSQL 13+); Kubernetes/Helm supported; SaaS limited&lt;/td&gt;
&lt;td&gt;RBAC; OIDC/LDAP (all tiers); SAML 2.0 (Enterprise only)&lt;/td&gt;
&lt;td&gt;React-based Console; NPS 80 at Kafka Summit 2024; widely praised by community&lt;/td&gt;
&lt;td&gt;Per-seat (~$1,200/seat/year); Community tier free but restricted (no SSL, single-broker only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka operations and developer experience at cluster scale&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic, consumer group, schema, connector management; advanced RBAC; audit log; WCAG-compliant UI; stable on clusters with up to 200k partitions&lt;/td&gt;
&lt;td&gt;Stateless; no external database dependency; Docker, Helm, JAR&lt;/td&gt;
&lt;td&gt;Advanced RBAC&lt;/td&gt;
&lt;td&gt;Fully WCAG-compliant&lt;/td&gt;
&lt;td&gt;Per-cluster pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer-focused data exploration and stream processing&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lenses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Data exploration, SQL on streams, topology view, connector management&lt;/td&gt;
&lt;td&gt;Self-hosted and SaaS&lt;/td&gt;
&lt;td&gt;RBAC&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Per-licence (contact for pricing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightweight developer UI for single-team or small-scale use&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AKHQ / Kafbat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic, consumer group, schema management; basic ACL management; free SSL and multi-broker access&lt;/td&gt;
&lt;td&gt;Self-hosted; low resource overhead; no external dependencies&lt;/td&gt;
&lt;td&gt;ACL-based; limited RBAC&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Free (open-source)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full Confluent Platform users&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial (bundled)&lt;/td&gt;
&lt;td&gt;Full Confluent Platform integration; ksqlDB, Kafka Connect, Schema Registry&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform; requires Confluent deployment&lt;/td&gt;
&lt;td&gt;Confluent RBAC&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Included with Confluent Platform licence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka operations visibility and JMX-based monitoring&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AxonOps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial (with OSS tier)&lt;/td&gt;
&lt;td&gt;Monitoring, alerting, backup and restore, topic management&lt;/td&gt;
&lt;td&gt;Self-hosted; Cassandra backend&lt;/td&gt;
&lt;td&gt;Role-based&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Free tier; commercial tiers available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a full comparison of Kafka UI and management tools in 2026, see &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Top Kafka UI tools in 2026: a practical comparison for engineering teams&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-conduktor&quot;&gt;Frequently asked questions about Conduktor&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-conduktor-cost-and-is-there-a-free-tier&quot;&gt;How much does Conduktor cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;The Team Edition is approximately $1,200 per seat per year. A free Community tier is available for self-hosted deployments, but it restricts the number of allowed servers and users and does not support connections to SSL-enabled or multi-broker clusters. Enterprise pricing is negotiated directly. For 100 users across three clusters, estimated list price is $80,000-$150,000 per year.&lt;/p&gt;
&lt;h3 id=&quot;when-is-conduktor-a-better-choice-than-the-alternatives&quot;&gt;When is Conduktor a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;Conduktor is a strong fit when multi-team Kafka governance, compliance certification (SOC2 Type II), field-level encryption, and self-service topic workflows are the primary requirements — particularly in regulated industries with PCI DSS, HIPAA, or GDPR obligations. The Gateway’s disaster recovery failover and virtual topic filtering capabilities are also relevant for platform teams managing high-availability or bandwidth-sensitive deployments.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-conduktor&quot;&gt;When are the alternatives a better choice than Conduktor?&lt;/h3&gt;
&lt;p&gt;If your team is small, if the free tier’s SSL and multi-broker restrictions prevent you from evaluating the tool against your actual staging environment, if per-cluster pricing fits your scaling model better than per-seat, if you need a stateless deployment without a PostgreSQL dependency, or if Azure Schema Registry integration is required, alternatives are likely worth a closer look.&lt;/p&gt;
&lt;h3 id=&quot;does-conduktor-support-azure-event-hubs&quot;&gt;Does Conduktor support Azure Event Hubs?&lt;/h3&gt;
&lt;p&gt;Azure Event Hubs is accessible via Kafka protocol compatibility, but Azure’s native Schema Registry is not integrated. Teams using Event Hubs cannot access Conduktor’s schema features against the Azure native registry.&lt;/p&gt;
&lt;h3 id=&quot;is-conduktor-desktop-still-supported&quot;&gt;Is Conduktor Desktop still supported?&lt;/h3&gt;
&lt;p&gt;Conduktor Desktop was retired at end of 2025. Conduktor’s current product is Console, a centrally deployed web platform. The last available version of the desktop application no longer supports modern Kafka versions, rendering it obsolete for teams that have not frozen their infrastructure. Teams migrating from Desktop to Console should expect a change in deployment model — from a personal application to a shared platform — rather than a like-for-like upgrade. Community feedback notes that the Docker-based Console setup is better suited to centralised team deployments than to individual developer machines.&lt;/p&gt;
&lt;h3 id=&quot;can-i-use-conduktors-free-tier-with-a-secured-staging-cluster&quot;&gt;Can I use Conduktor’s free tier with a secured staging cluster?&lt;/h3&gt;
&lt;p&gt;No. Connecting to any cluster configured with SSL/TLS or consisting of more than one broker node requires a paid subscription. This makes the free tier unsuitable for evaluating Conduktor against a typical staging environment and is the most commonly cited reason developers on Reddit move to open-source alternatives like Kafbat or AKHQ for local and staging use.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>How Airbnb uses Apache Kafka in production</title><link>https://factorhouse.io/articles/airbnb-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/airbnb-kafka-architecture/</guid><description>A deep-dive into Airbnb&apos;s Kafka architecture — covering six production systems, 35+ billion daily events, SpinalTap CDC, Flink-based personalisation, and Kafka as a write-ahead log.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Airbnb runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; across at least six production systems simultaneously, from the analytics pipeline that ingests more than 35 billion events per day to the merge queue that serialises thousands of code changes across its engineering monorepos. Few companies have found this many distinct architectural roles for a single piece of infrastructure, which makes Airbnb’s Kafka story worth examining in detail.&lt;/p&gt;
&lt;p&gt;The engineering problem Kafka solves at Airbnb is not a single one. It provides the event backbone for real-time personalisation, change data capture, distributed materialised views, and durable write-ahead logging, each with different throughput, ordering, and latency requirements handled through separate cluster and pipeline designs.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Airbnb operates a two-sided marketplace connecting hosts and guests across more than 220 countries. At the scale Airbnb operates, the platform generates a continuous stream of user activity events: searches, listing views, booking requests, messages, and availability updates, each of which needs to reach multiple downstream systems in near real time.&lt;/p&gt;
&lt;p&gt;Airbnb began building Kafka-backed streaming infrastructure around 2016-2017, when Youssef Francis and Jun He described their streaming logging pipeline at Kafka Summit New York 2017. The early architecture used Kafka as an event bus feeding Airstream, an internal Spark Streaming framework, into HBase and Hive. Over the following years the team expanded Kafka’s role substantially: adding a CDC system (SpinalTap, open-sourced in 2019), migrating real-time personalisation from Spark to Flink, building a Kafka Streams-based merge queue (Evergreen, presented at Kafka Summit London 2022), and adopting Kafka as the write-ahead log in their distributed key-value store (Mussel).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2016-2017&lt;/td&gt;
&lt;td&gt;Airstream (Spark Streaming on Kafka) deployed for logging event ingestion; Kafka Summit NYC 2017 talk on reliable logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;SpinalTap CDC open-sourced on GitHub; Kafka Summit NYC 2019 talk on streaming ingestion by Hao Wang&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~2021&lt;/td&gt;
&lt;td&gt;User Signals Platform launched with Apache Flink, replacing Spark Streaming for real-time personalisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;Evergreen merge queue built on Kafka Streams; presented at Kafka Summit London 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Riverbed framework in production processing 2.4 billion events/day across 50+ materialised views&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-2025&lt;/td&gt;
&lt;td&gt;Mussel V2 re-architecture: Kafka as WAL sustaining 100,000+ streaming writes/second on 100 TB+ tables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;airbnbs-kafka-use-cases&quot;&gt;Airbnb’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;analytics-event-ingestion&quot;&gt;Analytics event ingestion&lt;/h3&gt;
&lt;p&gt;The foundational use case is also the highest-volume one. Clients (mobile apps, web browsers) and online services publish logging events directly to Kafka. A Spark Streaming job built on Airstream, Airbnb’s internal streaming framework, reads from Kafka continuously, writes events to HBase for deduplication, then dumps them hourly into Hive partitions for downstream ETL and analytics jobs. The Hive-based data ingestion framework processes more than 35 billion Kafka event messages and more than 1,000 tables per day.&lt;/p&gt;
&lt;p&gt;Kafka brokers were migrated from on-premises infrastructure to VPC-hosted infrastructure, producing a 4x throughput improvement. To support capacity planning, the team tracks QPS per Kafka partition and adds partitions proactively as event volumes grow.&lt;/p&gt;
&lt;h3 id=&quot;real-time-personalisation-via-the-user-signals-platform&quot;&gt;Real-time personalisation via the User Signals Platform&lt;/h3&gt;
&lt;p&gt;Every user interaction on the Airbnb platform, a search query, a listing view, a booking, generates an event that flows into the User Signals Platform (USP). More than 100 Apache Flink jobs consume these Kafka events, transform them into structured “User Signals,” write the results to a key-value store, and re-emit signals as Kafka events for downstream consumer jobs handling session engagement and user segmentation. The USP service layer handles 70,000 queries per second.&lt;/p&gt;
&lt;p&gt;The decision to use Flink rather than Spark Streaming was deliberate and driven by a specific latency constraint: Spark’s micro-batch model processed events in small batches rather than one at a time, which introduced delays that were incompatible with real-time personalisation requirements. Flink’s event-by-event processing model met those latency requirements.&lt;/p&gt;
&lt;h3 id=&quot;change-data-capture-via-spinaltap&quot;&gt;Change data capture via SpinalTap&lt;/h3&gt;
&lt;p&gt;SpinalTap is Airbnb’s open-source CDC service, available on GitHub at &lt;a href=&quot;https://github.com/airbnb/SpinalTap&quot;&gt;airbnb/SpinalTap&lt;/a&gt;. It detects data mutations in MySQL, DynamoDB, and proprietary in-house storage systems, then propagates them as standardised events to Kafka using Apache Thrift serialisation, which supports both the Ruby and Java consumers that read from those topics.&lt;/p&gt;
&lt;p&gt;SpinalTap supplies several downstream systems from a single Kafka stream of mutations. Search indexing: mutations flow from Kafka into Elasticsearch for products like review search and inbox search. Cache invalidation: downstream services subscribe to mutation events and evict or update entries in Memcached and Redis without blocking the request path. Service signalling: dependent services subscribe to data changes in near real time, with the Availability service monitoring Reservation changes as a concrete example.&lt;/p&gt;
&lt;p&gt;SpinalTap provides at-least-once delivery with zero data-loss tolerance, per-record ordering (commit-order preservation), and epoch-based split-brain mitigation for high-availability deployments. A continuous validation pipeline runs in both pre-production and production environments.&lt;/p&gt;
&lt;h3 id=&quot;distributed-materialised-views-via-riverbed&quot;&gt;Distributed materialised views via Riverbed&lt;/h3&gt;
&lt;p&gt;Some of Airbnb’s highest-traffic features require joining data across multiple distinct databases. Fetching that data at query time produces latency that is incompatible with user-facing response time requirements. Riverbed is Airbnb’s Lambda-like framework that solves this by pre-computing and maintaining distributed materialised views.&lt;/p&gt;
&lt;p&gt;The streaming component of Riverbed consumes CDC events via Kafka. Rather than partitioning those events by their source record ID, Riverbed repartitions them in Kafka by the materialised view document ID. This ensures that all CDC events affecting a given materialised view document are routed to the same consumer and processed serially, eliminating concurrent-write race conditions without requiring distributed locking.&lt;/p&gt;
&lt;p&gt;Riverbed currently powers 50+ materialised views, processes 2.4 billion events per day, and writes 350 million documents per day.&lt;/p&gt;
&lt;h3 id=&quot;write-ahead-log-in-the-mussel-key-value-store&quot;&gt;Write-ahead log in the Mussel key-value store&lt;/h3&gt;
&lt;p&gt;Mussel is Airbnb’s distributed key-value store for derived data. Rather than writing directly to the backend database, every write is first persisted to Kafka, which acts as the durable write-ahead log. Downstream Replayer and Write Dispatcher components consume from Kafka and apply writes to the backend database in order. In Mussel V1, the Kafka topic used 1,024 partitions aligned with the shard structure, so each shard’s data was processed by a single consumer in commit order.&lt;/p&gt;
&lt;p&gt;Mussel V2, re-architected in 2024-2025, pairs a NewSQL backend with Kubernetes-native control and a stateless horizontally-scalable Dispatcher layer. Kafka continues to serve as the common durable log. The system sustains more than 100,000 streaming writes per second and supports tables exceeding 100 terabytes, with p99 read latencies under 25 milliseconds. During the V1-to-V2 migration, Kafka’s role as a shared log allowed both versions to run simultaneously and enabled data replay for validation.&lt;/p&gt;
&lt;h3 id=&quot;developer-infrastructure-the-evergreen-merge-queue&quot;&gt;Developer infrastructure: the Evergreen merge queue&lt;/h3&gt;
&lt;p&gt;Most of Airbnb’s engineering work happens inside large monolithic repositories. With thousands of changes being merged every day, there is a meaningful probability that changes passing automated checks independently will fail when integrated with concurrent changes on the mainline. Evergreen is Airbnb’s merge queue system that guarantees serializability of changes.&lt;/p&gt;
&lt;p&gt;At its core, Evergreen uses an actor model built on Kafka Streams. A state machine applies a pure function on events and transforms them into actions executed by workers; workers may in turn produce additional events. The team selected Kafka Streams for its exactly-once processing guarantees (critical for a system where duplicate or missing merges would be costly), its built-in load balancing, and its minimal external dependencies. Janusz Kudelka and Joel Snyder from Airbnb’s Developer Infrastructure team presented the architecture and its production learnings at Kafka Summit London 2022.&lt;/p&gt;
&lt;h2 id=&quot;scale--throughput&quot;&gt;Scale &amp;amp; throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Daily events (analytics ingestion pipeline)&lt;/td&gt;
&lt;td&gt;&amp;gt;35 billion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily events (Riverbed)&lt;/td&gt;
&lt;td&gt;2.4 billion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily documents written (Riverbed)&lt;/td&gt;
&lt;td&gt;350 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Events per second (User Signals Platform)&lt;/td&gt;
&lt;td&gt;&amp;gt;1 million&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flink jobs consuming Kafka (USP)&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USP service query rate&lt;/td&gt;
&lt;td&gt;70,000 QPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mussel streaming write throughput&lt;/td&gt;
&lt;td&gt;&amp;gt;100,000 writes/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mussel largest table size&lt;/td&gt;
&lt;td&gt;&amp;gt;100 terabytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mussel p99 read latency&lt;/td&gt;
&lt;td&gt;&amp;lt;25 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka partitions in Mussel V1&lt;/td&gt;
&lt;td&gt;1,024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables ingested daily (data warehouse)&lt;/td&gt;
&lt;td&gt;1,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Materialised views in Riverbed&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;Airbnb operates multiple Kafka clusters segmented by function. Youssef Francis and Jun He referenced separate clusters for analytics, change data capture, and inter-service communication as early as their 2017 Kafka Summit talk, a pattern consistent with the distinct pipeline architectures described above.&lt;/p&gt;
&lt;h2 id=&quot;airbnbs-kafka-architecture&quot;&gt;Airbnb’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Producers span a wide range of application types. Mobile clients and web browsers publish logging events directly. Online services publish both logging events and mutation events (via SpinalTap’s binlog-parsing source layer). SpinalTap’s destination layer buffers outbound events in bounded in-memory queues and uses destination pools with thread-based partitioning to absorb traffic spikes without applying back-pressure upstream.&lt;/p&gt;
&lt;p&gt;Serialisation varies by pipeline. SpinalTap uses Apache Thrift for cross-language support across Ruby and Java producers and consumers. The USP and Riverbed pipelines are Java-based.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer group design reflects the isolation requirements of each system. The USP runs 100+ independent Flink jobs, each consuming from specific Kafka topics and maintaining its own consumer group state. Hot-standby Task Managers are provisioned to take over Kafka partition assignments immediately on failure, avoiding rebalancing delays.&lt;/p&gt;
&lt;p&gt;For idempotency in the USP, the team made a deliberate storage-layer decision: rather than deduplicating events in the stream processor (which adds state complexity under at-least-once delivery), computed signals are written to an append-only key-value store where event timestamps serve as version keys. A later write for the same key with a higher timestamp wins, making the storage layer naturally idempotent.&lt;/p&gt;
&lt;p&gt;Offset management in the logging ingestion pipeline is coupled to partition count monitoring. QPS per partition is tracked as the primary metric for determining when to add partitions.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Two stream processing systems run on top of Kafka.&lt;/p&gt;
&lt;p&gt;Apache Flink powers the User Signals Platform. The Flink-based architecture was chosen specifically because Spark Streaming’s micro-batch model introduced event delays that made it unsuitable for real-time personalisation. To lower the barrier to authoring Flink jobs, the USP team built a config-driven layer: engineers define signal transformations declaratively, and a setup script generates the corresponding Flink job configuration, batch backfill files, and monitoring alerts automatically.&lt;/p&gt;
&lt;p&gt;Kafka Streams powers Evergreen. The stateful actor model is implemented as a Kafka Streams topology, where the state machine and its transitions are expressed as stream-processing operations. Exactly-once processing semantics are a hard requirement: the merge queue must not execute a merge twice or skip one.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;SpinalTap functions as a custom producer framework rather than a standard Kafka Connect source connector: it parses MySQL binary logs and DynamoDB streams directly and writes standardised Thrift-encoded mutation events to Kafka. The downstream consumers (Elasticsearch indexers, cache invalidators, Hive exporters) are custom applications rather than Connect sink connectors. No use of standard Kafka Connect connectors is referenced in Airbnb’s public engineering writing.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques--engineering-innovations&quot;&gt;Special techniques &amp;amp; engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;partition-alignment-in-mussel-ordering-without-coordination&quot;&gt;Partition alignment in Mussel (ordering without coordination)&lt;/h3&gt;
&lt;p&gt;Mussel V1 used 1,024 Kafka partitions aligned with the 1,024-shard structure of the backend store. Because each partition maps to exactly one shard, all writes to a shard flow through a single Kafka partition and are applied by a single consumer in order. This gives per-shard write ordering without needing a separate coordination layer or distributed lock.&lt;/p&gt;
&lt;h3 id=&quot;partitioning-by-document-id-in-riverbed-race-condition-prevention&quot;&gt;Partitioning by document ID in Riverbed (race condition prevention)&lt;/h3&gt;
&lt;p&gt;CDC events in Riverbed are repartitioned in Kafka by the materialised-view document ID rather than the source database record ID. Because a materialised view document can be affected by mutations across multiple upstream tables, naive partitioning by source record would route concurrent updates to the same document to different consumers, creating write races. Routing by document ID ensures serial processing per document, which eliminates the race without requiring distributed locking or explicit conflict resolution.&lt;/p&gt;
&lt;h3 id=&quot;balanced-kafka-reader-decoupling-spark-parallelism-from-partition-count&quot;&gt;Balanced Kafka reader (decoupling Spark parallelism from partition count)&lt;/h3&gt;
&lt;p&gt;In Airbnb’s early Spark Streaming logging pipeline, the degree of Spark task parallelism was directly tied to the number of Kafka partitions. Adding throughput capacity required repartitioning Kafka topics, which is an operationally disruptive change. Hao Wang’s team built a balanced Kafka reader that allows Spark parallelism to be configured independently of partition count, enabling flexible capacity increases without forced repartitioning.&lt;/p&gt;
&lt;h3 id=&quot;kafka-as-a-migration-backbone-in-mussel-v2&quot;&gt;Kafka as a migration backbone in Mussel V2&lt;/h3&gt;
&lt;p&gt;During the multi-version migration from Mussel V1 to V2, Kafka’s WAL role made it possible for both versions to operate simultaneously against the same event stream. The new Replayer and Write Dispatcher components could consume from the same Kafka log as V1, allowing engineers to validate V2 correctness by replaying production events against the new backend before cutting over. This reduced the risk of a hard migration cutover.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Kafka is self-managed at Airbnb. The migration of broker infrastructure to VPC provided a 4x throughput improvement. No use of Amazon MSK or Confluent Cloud appears in any primary engineering sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; Partition-level QPS is the primary capacity signal for the analytics ingestion pipeline. The USP generates per-job monitoring alerts automatically through the config-driven job setup. Mussel V2 provides namespace-level quotas and dashboards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upgrade and migration strategy:&lt;/strong&gt; The Mussel V1-to-V2 migration used Kafka as the shared log to run both versions in parallel, supporting incremental validation and rollback. SpinalTap’s continuous validation pipeline runs in pre-production alongside production, allowing schema and routing changes to be tested against real mutation streams before deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; SpinalTap’s continuous validation pipeline, the USP’s config-driven job generator, and Evergreen’s Kafka Streams actor model all reflect a deliberate effort to abstract Kafka’s operational complexity away from application engineers. The goal in each case is to expose a declarative or event-driven interface while the platform team owns the Kafka infrastructure underneath.&lt;/p&gt;
&lt;h2 id=&quot;challenges--how-they-solved-them&quot;&gt;Challenges &amp;amp; how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;latency-incompatibility-between-spark-streaming-and-real-time-personalisation&quot;&gt;Latency incompatibility between Spark Streaming and real-time personalisation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The User Signals Platform was initially built on Spark Streaming, which processes events in micro-batches rather than one at a time. This introduced processing delays that could not be reduced below a threshold determined by the micro-batch interval, making the system unsuitable for real-time personalisation features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; A fundamental processing model constraint in Spark Streaming, not a configuration or scaling issue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Migrated to Apache Flink, which processes events one-by-one. The USP team also built a config-driven layer to absorb the additional complexity Flink introduces for new engineers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The USP now processes more than 1 million events per second across 100+ Flink jobs, serving the personalisation service layer at 70,000 QPS.&lt;/p&gt;
&lt;h3 id=&quot;concurrent-write-race-conditions-in-riverbed&quot;&gt;Concurrent-write race conditions in Riverbed&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Materialised view documents in Riverbed are computed from data spread across multiple upstream tables. When mutations in different upstream tables triggered concurrent updates to the same materialised view document, writes from two consumers could interleave and produce a corrupted result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The partition key used for Kafka routing did not correspond to the unit of write exclusion (the materialised view document ID).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Repartitioned CDC events in Kafka by materialised view document ID. All updates to a given document are now routed to a single consumer and processed serially.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Race conditions eliminated without distributed locking or explicit conflict detection.&lt;/p&gt;
&lt;h3 id=&quot;kafka-partition-count-coupling-in-spark-streaming&quot;&gt;Kafka partition count coupling in Spark Streaming&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Airbnb’s Spark Streaming logging pipeline tied Spark task parallelism directly to Kafka partition count. Scaling throughput meant repartitioning Kafka topics, an operationally costly change that required coordinating producer and consumer restarts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The default Spark Kafka integration assigned one Spark task per Kafka partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Developed a custom balanced Kafka reader that maps Kafka partitions to Spark tasks independently, allowing Spark parallelism to be increased without changing partition count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The logging pipeline could grow event throughput without mandatory Kafka repartitioning operations.&lt;/p&gt;
&lt;h3 id=&quot;one-record-at-a-time-throughput-in-evergreen&quot;&gt;One-record-at-a-time throughput in Evergreen&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Evergreen’s Kafka Streams state machine processed one record at a time, which became a throughput bottleneck as the volume of daily merge requests grew. Replaying records for debugging purposes was also difficult.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Kafka Streams’ processing model, combined with the actor pattern’s sequential state transitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; Identified as a known production challenge. Janusz Kudelka and Joel Snyder described it as a learning from operating Evergreen at Kafka Summit London 2022. No specific resolution is described in available public sources.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (self-managed, multiple clusters)&lt;/td&gt;
&lt;td&gt;Separate clusters for analytics, CDC, and inter-service messaging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;User Signals Platform; 100+ jobs; config-driven job generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams&lt;/td&gt;
&lt;td&gt;Evergreen merge queue; exactly-once processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing (historical)&lt;/td&gt;
&lt;td&gt;Apache Spark Streaming / Airstream&lt;/td&gt;
&lt;td&gt;Logging event ingestion pipeline; replaced by Flink for USP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC system&lt;/td&gt;
&lt;td&gt;SpinalTap (open-source)&lt;/td&gt;
&lt;td&gt;MySQL/DynamoDB mutation capture to Kafka; open-sourced 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Apache Thrift&lt;/td&gt;
&lt;td&gt;SpinalTap event format; cross-language Ruby and Java support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (operational)&lt;/td&gt;
&lt;td&gt;Apache HBase&lt;/td&gt;
&lt;td&gt;Event deduplication in logging ingestion pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (analytical)&lt;/td&gt;
&lt;td&gt;Apache Hive&lt;/td&gt;
&lt;td&gt;Data warehouse; hourly partition dumps from Kafka pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query engine&lt;/td&gt;
&lt;td&gt;Apache Presto&lt;/td&gt;
&lt;td&gt;Interactive querying on Hive tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;Apache Airflow&lt;/td&gt;
&lt;td&gt;Batch job orchestration; Mussel V2 bulk ingestion via Airflow and S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KV store&lt;/td&gt;
&lt;td&gt;Mussel (internal, V1 and V2)&lt;/td&gt;
&lt;td&gt;Derived data store; Kafka-backed write-ahead log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KV store backend (V2)&lt;/td&gt;
&lt;td&gt;NewSQL (unspecified)&lt;/td&gt;
&lt;td&gt;Mussel V2 backend; Kubernetes-native, horizontally scalable Dispatcher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search indexing&lt;/td&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;CDC consumer via SpinalTap (review search, inbox search)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Memcached, Redis&lt;/td&gt;
&lt;td&gt;Cache invalidation via SpinalTap CDC events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC sources&lt;/td&gt;
&lt;td&gt;MySQL, DynamoDB&lt;/td&gt;
&lt;td&gt;Primary data stores feeding SpinalTap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business logic layer&lt;/td&gt;
&lt;td&gt;GraphQL&lt;/td&gt;
&lt;td&gt;Riverbed: aggregation logic for materialised view computation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Mussel V2 deployment model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object storage&lt;/td&gt;
&lt;td&gt;Amazon S3&lt;/td&gt;
&lt;td&gt;Mussel V2 bulk ingestion staging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hao Wang&lt;/td&gt;
&lt;td&gt;Led Spark Streaming logging ingestion; presenter at Kafka Summit NYC 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Youssef Francis, Jun He&lt;/td&gt;
&lt;td&gt;Presenters at Kafka Summit NYC 2017 on reliable logging with Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jad Abi-Samra, Litao Deng, Zuofei Wang&lt;/td&gt;
&lt;td&gt;Authors of the SpinalTap engineering blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kidai Kwon&lt;/td&gt;
&lt;td&gt;Author of the User Signals Platform blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amre Shakim&lt;/td&gt;
&lt;td&gt;Lead author of the Riverbed blog posts; team also included Krish Chainani, Victor Chen, Yanxi Chen, Xiangmin Liang, Anton Panasenko, Sonia Stan, Peggy Zheng&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Janusz Kudelka, Joel Snyder&lt;/td&gt;
&lt;td&gt;Developer Infrastructure engineers; designed Evergreen and presented at Kafka Summit London 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cong Zhu, Pala Muthiah, Jinyang Li, Ronnie Zhu, Gabe Lyons, Xu Zhang&lt;/td&gt;
&lt;td&gt;Contributors to Spark Streaming logging ingestion pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use partition key selection as an ordering contract, not just a routing decision.&lt;/strong&gt; Mussel aligns partition count to shard count to guarantee per-shard ordering without coordination. Riverbed partitions by document ID rather than source record ID to make CDC processing race-free. In both cases, the partition key was chosen to match the unit of consistency required by the consuming system, not just to distribute load evenly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Micro-batch stream processing is incompatible with some latency targets.&lt;/strong&gt; Airbnb’s USP found that Spark Streaming’s micro-batch model had a latency floor that could not be reduced to meet real-time personalisation requirements. If your use case has a latency constraint below what a micro-batch interval can provide, Flink’s event-by-event processing model is worth the additional operational complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka as a write-ahead log simplifies distributed migrations.&lt;/strong&gt; Mussel V2’s migration succeeded in part because Kafka already held a complete ordered log of writes. The new backend could consume the same stream independently, enabling parallel validation before cutover. If you are migrating a stateful system, having a durable Kafka WAL in place before the migration substantially reduces cutover risk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decouple stream processor parallelism from Kafka partition count.&lt;/strong&gt; Airbnb’s balanced Kafka reader broke the coupling between Spark task count and partition count, allowing throughput to be increased without repartitioning. This is worth considering in any system where repartitioning is operationally costly, particularly where partition count affects ordering guarantees downstream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abstract Kafka complexity with a declarative layer if your engineering organisation is large.&lt;/strong&gt; The USP config-driven Flink job generator, SpinalTap’s structured pipeline model, and Evergreen’s actor abstraction all serve the same goal: letting application engineers define what they need from a streaming pipeline without needing to understand Kafka consumer groups, offset management, or Flink topology design. At Airbnb’s scale, this separation of concerns is what allows Kafka to serve six distinct systems without six separate platform teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hao Wang, “Scaling Spark Streaming for Logging Event Ingestion,” Airbnb Engineering Blog: &lt;a href=&quot;https://medium.com/airbnb-engineering/scaling-spark-streaming-for-logging-event-ingestion-4a03141d135d&quot;&gt;https://medium.com/airbnb-engineering/scaling-spark-streaming-for-logging-event-ingestion-4a03141d135d&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Hao Wang, Kafka Summit NYC 2019 talk: &lt;a href=&quot;https://videos.confluent.io/watch/b94DNHsNfzTt8apLDmDLHP&quot;&gt;https://videos.confluent.io/watch/b94DNHsNfzTt8apLDmDLHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Youssef Francis, Jun He, Kafka Summit NYC 2017: &lt;a href=&quot;https://www.confluent.io/kafka-summit-nyc17/every-message-counts-kafka-foundation-highly-reliable-logging-airbnb/&quot;&gt;https://www.confluent.io/kafka-summit-nyc17/every-message-counts-kafka-foundation-highly-reliable-logging-airbnb/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kidai Kwon, “Building a User Signals Platform at Airbnb,” Airbnb Engineering Blog: &lt;a href=&quot;https://medium.com/airbnb-engineering/building-a-user-signals-platform-at-airbnb-b236078ec82b&quot;&gt;https://medium.com/airbnb-engineering/building-a-user-signals-platform-at-airbnb-b236078ec82b&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Jad Abi-Samra, Litao Deng, Zuofei Wang, “Capturing Data Evolution in a Service-Oriented Architecture,” Airbnb Engineering Blog: &lt;a href=&quot;https://medium.com/airbnb-engineering/capturing-data-evolution-in-a-service-oriented-architecture-72f7c643ee6f&quot;&gt;https://medium.com/airbnb-engineering/capturing-data-evolution-in-a-service-oriented-architecture-72f7c643ee6f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SpinalTap on GitHub: &lt;a href=&quot;https://github.com/airbnb/SpinalTap&quot;&gt;https://github.com/airbnb/SpinalTap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amre Shakim, “Riverbed: Optimizing Data Access at Airbnb’s Scale,” Airbnb Engineering Blog: &lt;a href=&quot;https://medium.com/airbnb-engineering/riverbed-optimizing-data-access-at-airbnbs-scale-c37ecf6456d9&quot;&gt;https://medium.com/airbnb-engineering/riverbed-optimizing-data-access-at-airbnbs-scale-c37ecf6456d9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Xiangmin Liang, Sivakumar Bhavanari, Amre Shakim, “Riverbed data hydration — Part 1,” Airbnb Engineering Blog: &lt;a href=&quot;https://medium.com/airbnb-engineering/riverbed-data-hydration-part-1-e7011d62d946&quot;&gt;https://medium.com/airbnb-engineering/riverbed-data-hydration-part-1-e7011d62d946&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;InfoQ, “Distributed Materialized Views: How Airbnb’s Riverbed Processes 2.4 Billion Daily Events”: &lt;a href=&quot;https://www.infoq.com/news/2023/10/airbnb-riverbed-introduction/&quot;&gt;https://www.infoq.com/news/2023/10/airbnb-riverbed-introduction/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Airbnb Engineering Blog, “Mussel — Airbnb’s Key-Value Store for Derived Data”: &lt;a href=&quot;https://airbnb.tech/data/mussel-airbnbs-key-value-store-for-derived-data/&quot;&gt;https://airbnb.tech/data/mussel-airbnbs-key-value-store-for-derived-data/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;InfoQ, “Airbnb’s Mussel V2: Next-Gen Key Value Storage to Unify Streaming and Bulk Ingestion”: &lt;a href=&quot;https://www.infoq.com/news/2025/10/airbnb-nextgen-kv-storage-mussel/&quot;&gt;https://www.infoq.com/news/2025/10/airbnb-nextgen-kv-storage-mussel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Janusz Kudelka, Joel Snyder, Kafka Summit London 2022: &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-london-2022/evergreen-building-airbnbs-merge-queue-with-kafka-streams/&quot;&gt;https://www.confluent.io/events/kafka-summit-london-2022/evergreen-building-airbnbs-merge-queue-with-kafka-streams/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Try Kpow with your own Kafka cluster:&lt;/strong&gt; If you are running Kafka in production and want visibility into consumer lag, partition throughput, and topic health across multiple clusters, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; offers a free 30-day trial. You can connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Bytedance uses Apache Kafka in production</title><link>https://factorhouse.io/articles/bytedance-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/bytedance-kafka-architecture/</guid><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ByteDance ran &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; at a scale that eventually made rebuilding it the more practical option. At peak, its streaming data layer handles tens of TB/s of consume throughput across applications that span short-video recommendation, real-time ML training, and cross-team data integration. Rather than continue tuning a standard Kafka deployment, ByteDance built ByteMQ (BMQ): a Kafka-compatible, cloud-native replacement that separates storage from compute and now runs 99.76% of what were formerly Kafka workloads, at roughly 70% lower resource cost. Understanding how the company got there, and what the architecture looks like today, is instructive for any team planning a streaming platform at similar scale.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;ByteDance operates the portfolio of applications behind TikTok, Douyin, Toutiao, and several other consumer products. The company’s platforms generate continuous high-volume user-interaction data: every view, like, share, and comment on TikTok becomes a signal that feeds recommendation models, content ranking, and advertising systems in near real time. That combination of volume and latency sensitivity pushed ByteDance toward event streaming infrastructure early in the company’s growth.&lt;/p&gt;
&lt;p&gt;Kafka was adopted as the primary message bus for event and log collection. It served as the backbone for stream processing pipelines, online model training, and data integration across ByteDance’s business units. Over time, the throughput requirements outgrew what a standard Kafka deployment could serve cost-effectively, and the engineering team began work on a replacement. The timeline below covers the key milestones from the documented record.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-2022:&lt;/strong&gt; Kafka operating as ByteDance’s primary event and log collection bus&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;September 2022:&lt;/strong&gt; Monolith paper published, documenting Kafka’s role in TikTok’s real-time recommendation training pipeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2023:&lt;/strong&gt; StreamOps published at VLDB, describing runtime management of tens of thousands of Flink jobs against Kafka-compatible queues&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;November 2024:&lt;/strong&gt; ByteMQ paper published at ACM SoCC; 99.76% Kafka-to-BMQ migration reported complete; 70% resource cost reduction achieved&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;February 2026:&lt;/strong&gt; StreamShield paper published; Flink cluster now sustains 70,000+ concurrent streaming jobs across 11 million+ resource slots&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;bytedances-kafka-use-cases&quot;&gt;ByteDance’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Event and log collection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka served as ByteDance’s central data hub for collecting events and user-activity logs across its applications, covering online model training inputs, stream data processing, and real-time analytics. This is the foundational use case that drove initial adoption and still underpins the workloads now running on ByteMQ.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time recommendation training&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TikTok’s recommendation system is built on Monolith, ByteDance’s distributed real-time ML framework. Kafka plays a structural role in the training pipeline: one queue carries raw user-action events (views, likes, shares), and a second carries feature data. A Flink streaming job reads from both queues simultaneously, joining each user action with its corresponding features to produce labelled training examples. Those examples flow directly into Monolith’s training parameter server, which updates model weights continuously throughout the day. This architecture allows the recommendation model to adapt to shifting user behaviour without waiting for periodic batch retraining cycles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stream processing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ByteDance runs one of the largest documented Apache Flink deployments in production. As of early 2026, the cluster sustains over 70,000 concurrent streaming jobs backed by more than 11 million resource slots. The message queues underlying those jobs are now ByteMQ, but the workload pattern is a direct continuation of the Kafka-based streaming infrastructure that preceded it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data integration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;BitSail, ByteDance’s open-source data integration engine, provides both Kafka source and Kafka sink connectors, enabling data synchronisation between Kafka topics and downstream stores. The system processes hundreds of trillions of records per day across ByteDance’s data estate, supporting batch, streaming, and incremental sync patterns.&lt;/p&gt;
&lt;h2 id=&quot;scale--throughput&quot;&gt;Scale &amp;amp; throughput&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Peak consume rate:&lt;/strong&gt; Tens of TB/s (ByteMQ paper, ACM SoCC 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Peak produce rate:&lt;/strong&gt; Approximately one-fifth of the consume rate (ByteMQ paper, ACM SoCC 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka clusters migrated to BMQ:&lt;/strong&gt; 99.76% (ByteMQ paper, ACM SoCC 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource cost reduction post-migration:&lt;/strong&gt; ~70% (ByteMQ paper, ACM SoCC 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent Flink streaming jobs:&lt;/strong&gt; 70,000+ (StreamShield paper, arXiv Feb 2026)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flink resource slots managed:&lt;/strong&gt; 11 million+ (StreamShield paper, arXiv Feb 2026)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data synchronised per day (BitSail):&lt;/strong&gt; Hundreds of trillions of records (BitSail GitHub repo)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The consume-to-produce ratio of roughly five-to-one reflects ByteDance’s fan-out consumption patterns, where multiple downstream consumers (recommendation, analytics, logging, model training) each read from the same upstream event streams.&lt;/p&gt;
&lt;h2 id=&quot;bytedances-kafka-architecture&quot;&gt;ByteDance’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;from-kafka-to-bytemq&quot;&gt;From Kafka to ByteMQ&lt;/h3&gt;
&lt;p&gt;ByteDance’s streaming architecture is best understood in two phases: Kafka as adopted, and ByteMQ as the current system. The transition happened because standard Kafka couples storage and compute: adding storage capacity requires adding brokers, and adding brokers for throughput also adds storage. At ByteDance’s scale, this relationship became expensive. The company’s response was to build ByteMQ, a Kafka-compatible system that breaks that coupling.&lt;/p&gt;
&lt;p&gt;ByteMQ is Kafka API-compatible by design. Producers and consumers continue to use standard Kafka SDKs, and the Pub/Sub Engine within BMQ presents the familiar topic and consumer-group interface. This compatibility requirement was a deliberate engineering constraint, chosen to de-risk the migration. Switching infrastructure at the scale of tens of TB/s while keeping application code unchanged is a significant operational achievement.&lt;/p&gt;
&lt;h3 id=&quot;bytemq-architecture&quot;&gt;ByteMQ architecture&lt;/h3&gt;
&lt;p&gt;Three architectural decisions define BMQ:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storage-compute separation.&lt;/strong&gt; Rather than writing message data to local broker disks, BMQ persists all data to ByteDance’s internal Federated Distributed File System (DFS). Brokers handle routing and API serving, while the DFS handles durability. This means brokers can be scaled independently of storage, and vice versa. In ByteDance’s own comparative benchmarks, a scale-out operation on clusters provisioned at approximately 1,000 TB of storage and 200 CPU cores required adding roughly 50 new BMQ brokers versus approximately 5 new brokers for an equivalent Kafka cluster: a reflection of how much more efficiently BMQ can redistribute load when brokers are not tied to their local disk state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adaptive resource scheduling.&lt;/strong&gt; BMQ redistributes workloads across brokers and across multiple availability zones dynamically, balancing throughput and isolating failures without manual intervention. This addresses a common operational burden in large Kafka deployments, where partition reassignment is a heavyweight and often risky operation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Historical data restructuring.&lt;/strong&gt; BMQ restructures older streaming data into formats that are efficient for offline batch consumption. This allows both real-time stream processors and batch analytics jobs to consume from the same underlying data layer, without requiring separate pipelines or dedicated archival infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;recommendation-pipeline-monolith&quot;&gt;Recommendation pipeline (Monolith)&lt;/h3&gt;
&lt;p&gt;The Monolith recommendation training pipeline uses Kafka as its inter-component transport with a specific two-queue architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action queue:&lt;/strong&gt; carries user-action events from TikTok (views, likes, shares, follows)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature queue:&lt;/strong&gt; carries pre-computed feature data from the feature store&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Flink online joiner&lt;/strong&gt; reads from both queues simultaneously and joins each action event with its corresponding features to produce labelled training examples&lt;/li&gt;
&lt;li&gt;Training examples are written to Monolith’s training parameter server (training-PS), which updates model weights in real time&lt;/li&gt;
&lt;li&gt;The inference parameter server (inference-PS) serves live traffic and syncs periodically from the training-PS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This design keeps the training loop continuously fed from live user-interaction data, rather than waiting for a batch export cycle.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;ByteDance’s documented architecture does not describe specific Kafka producer configuration parameters (acks, batching, compression) in public sources. The Monolith pipeline uses Kafka producers to write action events and feature data into their respective queues as part of the online training loop.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer groups in BMQ follow the same semantics as Kafka consumer groups: consumers are organised into groups, with each partition assigned to one consumer in the group at a time. The Kafka API compatibility layer in BMQ’s Pub/Sub Engine means existing consumer group code runs unchanged against BMQ.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink is ByteDance’s primary stream processing engine. The Flink cluster is managed by two internal systems: StreamOps handles runtime lifecycle management (auto-scaling, straggler detection, automated recovery), and StreamShield provides resiliency mechanisms at the engine and cluster levels. Both systems interact with BMQ as the message source.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques--engineering-innovations&quot;&gt;Special techniques &amp;amp; engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kafka-compatible replacement at scale (ByteMQ)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The ByteMQ migration is itself a notable engineering decision. Rather than adopting a third-party Kafka-compatible system or moving to a different messaging model, ByteDance built and operated its own replacement. The design choice to maintain full Kafka SDK compatibility meant that migration could proceed incrementally across 99.76% of clusters without requiring application changes. The 70% resource cost reduction suggests the storage-compute coupling in standard Kafka was contributing significantly to infrastructure spend at ByteDance’s scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Online continuous training via Kafka (Monolith)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The two-queue Kafka architecture in Monolith enables a training loop that runs continuously from live user interactions. The Flink join step is critical: by joining action events with features in a streaming job rather than storing features alongside events, the system keeps feature data up to date without having to re-embed stale feature values into the event stream at write time. This produces training examples that reflect the feature state at the time the action occurred rather than a fixed snapshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Collisionless embedding table&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Monolith uses a hash-map-based embedding table rather than a fixed-size embedding table. Embeddings expire when unused (avoiding accumulation of long-tail entities), and frequency filtering prevents low-signal features from consuming memory. This keeps the embedding memory footprint bounded without sacrificing model quality from hash collisions, which are common in the fixed-size approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Region checkpointing (StreamShield)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Standard Flink checkpointing operates at the job level: a checkpoint failure triggers recovery for the entire job. StreamShield introduces region-level checkpointing, which narrows the recovery scope to the portion of the job that actually failed. This change improved checkpoint success rates in ByteDance’s production Flink cluster from 53.9% to 93.5%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adaptive shuffle (StreamShield)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;StreamShield’s Adaptive Shuffle enables dynamic, load-aware data redistribution across Flink task managers. When a task manager becomes a bottleneck, the shuffle layer redistributes partitions to underutilised nodes without a full job restart. This reduces the frequency of hot-spot-induced failures under uneven workloads.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;StreamOps: runtime lifecycle management:&lt;/strong&gt; ByteDance built StreamOps, published at VLDB 2023, as a cloud-native control plane for the Flink cluster. StreamOps runs three control policies concurrently across all streaming jobs: an auto-scaler that adjusts task manager allocation in response to throughput changes, a straggler detector that identifies slow-running tasks within jobs, and a job doctor that diagnoses and remediates common failure patterns automatically. StreamOps manages tens of thousands of concurrent jobs, many of which run continuously for days or longer against BMQ queues. Operating at that concurrency level without a centralised control plane would require proportionally more manual intervention per incident.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;StreamShield: production resiliency:&lt;/strong&gt; StreamShield, published February 2026, extends StreamOps with a resiliency layer built around four mechanisms: runtime optimisation (adaptive shuffle, autoscaling), fine-grained fault tolerance (region checkpointing, single-task recovery), hybrid replication (combining passive and active strategies for cluster-level fault tolerance), and a testing and release pipeline that includes chaos testing, micro-benchmarking, macro-benchmarking, and online probe tasks before each production deployment. As of the paper’s publication, StreamShield sustains over 70,000 concurrent streaming jobs and manages more than 11 million resource slots, with thousands of recovery events per day handled automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Job startup overhead:&lt;/strong&gt; StreamShield reduced job startup overhead from approximately 500 seconds to approximately 200 seconds at scale, measured in test configurations with thousands of task managers. Faster restarts reduce the window during which a failed job is not processing data, which matters for latency-sensitive pipelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data integration with BitSail:&lt;/strong&gt; ByteDance open-sourced BitSail, a distributed data integration engine that supports Kafka as both a source and a sink. It is deployed across cloud-native and on-premises environments within ByteDance, and the repository documents support for hundreds of trillions of daily records. For teams looking to evaluate it, the project is available at the BitSail GitHub repository.&lt;/p&gt;
&lt;h2 id=&quot;challenges--how-they-solved-them&quot;&gt;Challenges &amp;amp; how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;kafka-could-not-scale-cost-effectively-at-bytedances-volume&quot;&gt;Kafka could not scale cost-effectively at ByteDance’s volume&lt;/h3&gt;
&lt;p&gt;Storage and compute are coupled in standard Kafka; adding storage requires adding brokers, and vice versa. At ByteDance’s volume, this relationship meant resource costs scaled proportionally with throughput in a way that became unsustainable. ByteDance built ByteMQ with storage-compute separation, using its internal Federated DFS as the storage layer, and maintained full Kafka API compatibility to allow migration without application changes. The migration reached 99.76% of Kafka clusters with approximately 70% reduction in resource costs.&lt;/p&gt;
&lt;h3 id=&quot;flink-job-failures-at-tens-of-thousands-of-concurrent-jobs-required-extensive-manual-intervention&quot;&gt;Flink job failures at tens of thousands of concurrent jobs required extensive manual intervention&lt;/h3&gt;
&lt;p&gt;Job-level recovery scope meant any failure required restarting the full job, and without a centralised control plane, intervention at that concurrency level was proportionally costly. ByteDance built StreamOps for automated lifecycle management and StreamShield for region-level checkpointing and adaptive shuffle. Checkpoint success rate improved from 53.9% to 93.5%, job startup overhead fell from approximately 500 seconds to approximately 200 seconds, and thousands of daily recovery events are now handled automatically.&lt;/p&gt;
&lt;h3 id=&quot;recommendation-model-freshness-lagged-user-behaviour&quot;&gt;Recommendation model freshness lagged user behaviour&lt;/h3&gt;
&lt;p&gt;Batch retraining cycles introduced hours of delay between a user action and the corresponding model weight update. Monolith’s two-queue Kafka architecture feeds a Flink online joining step that produces training examples directly from live event streams, allowing model weights to update continuously throughout the day.&lt;/p&gt;
&lt;h3 id=&quot;fixed-size-embedding-tables-caused-hash-collisions-and-degraded-recommendation-quality&quot;&gt;Fixed-size embedding tables caused hash collisions and degraded recommendation quality&lt;/h3&gt;
&lt;p&gt;Multiple distinct feature entities mapped to the same embedding slot when the fixed-size table was full. Monolith’s collisionless hash-map embedding table assigns each entity its own slot, with expirable embeddings and frequency filtering to keep the memory footprint bounded. This eliminates hash collisions without trading off model quality for sparse features.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CategoryToolsNotes&lt;/strong&gt;Message broker (original)Apache KafkaEvent and log collection; inter-service messaging; ML pipeline transportMessage broker (current)ByteMQ (BMQ) — internalKafka API-compatible; storage-compute separated; backed by ByteDance’s Federated DFSPersistent storage (BMQ)ByteDance Federated DFS — internalDistributed file system backing BMQ’s storage layerStream processingApache Flink70,000+ concurrent jobs; real-time feature joining, analytics, online model trainingOnline ML trainingMonolith — internal (open source)Flink-based online joiner reads two Kafka queues to produce training examples for continuous recommendation model trainingData integrationBitSail — open sourceKafka source and sink; hundreds of trillions of records/dayStreaming runtime managementStreamOps — internalAuto-scaler, straggler detector, job doctor for Flink jobs at scaleStreaming resiliencyStreamShield — internalRegion checkpointing, adaptive shuffle, hybrid replication, chaos-tested release pipeline&lt;/p&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Yancan Mao&lt;/strong&gt;: Lead author on both ByteMQ (ACM SoCC 2024) and StreamOps (VLDB 2023). &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3698038.3698536&quot;&gt;ByteMQ paper&lt;/a&gt;, &lt;a href=&quot;https://dl.acm.org/doi/abs/10.14778/3611540.3611543&quot;&gt;StreamOps paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zhanghao Chen&lt;/strong&gt;: Co-author, StreamOps. &lt;a href=&quot;https://dl.acm.org/doi/abs/10.14778/3611540.3611543&quot;&gt;StreamOps paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ruohang Yin, Liyuan Lei, Peng Ye, Shengfu Zou, Shizheng Tang, Yunzhe Guo, Ye Yuan, Xiaochen Yu, Bo Wan, Yunfei Gong, Changli Gao, Guanghui Zhang, Jian Shen, Rui Shi&lt;/strong&gt;: Co-authors, ByteMQ (ByteDance Inc.). &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3698038.3698536&quot;&gt;ByteMQ paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Yong Fang, Yuxing Han, Meng Wang, Yifan Zhang, Yue Ma, Chi Zhang&lt;/strong&gt;: Authors, StreamShield. &lt;a href=&quot;https://arxiv.org/abs/2602.03189&quot;&gt;StreamShield paper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage-compute coupling becomes expensive at high throughput.&lt;/strong&gt; ByteDance’s decision to build a Kafka-compatible replacement rather than continue scaling standard Kafka was driven by the proportional growth in resource cost. If your Kafka storage and compute are scaling together but you primarily need one or the other, evaluating storage-offload options (tiered storage, object storage backends) earlier may avoid a more significant architectural change later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka API compatibility is a practical migration constraint.&lt;/strong&gt; ByteMQ’s decision to maintain full Kafka SDK compatibility allowed ByteDance to migrate 99.76% of clusters without touching application code. If you are designing internal messaging infrastructure, preserving the Kafka protocol interface keeps your migration options open.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-time training pipelines benefit from separated queues with a streaming joiner.&lt;/strong&gt; Monolith’s two-queue approach keeps raw events and feature data on separate Kafka topics, joined in real time by a Flink job. This pattern avoids embedding feature state into the event at write time, which would require re-ingesting events whenever features change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job-level recovery scope does not work at tens of thousands of concurrent jobs.&lt;/strong&gt; ByteDance’s region checkpointing approach in StreamShield reduced the blast radius of individual failures and improved checkpoint success rates from 53.9% to 93.5%. If you are scaling a Flink deployment significantly, the granularity of your checkpointing and recovery scope is worth reviewing before it becomes a bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A centralised control plane for streaming jobs reduces per-incident manual work.&lt;/strong&gt; StreamOps manages auto-scaling, straggler detection, and automated recovery across ByteDance’s Flink cluster. At smaller concurrency levels, per-job monitoring is manageable; at tens of thousands of jobs, automation at the control-plane level is necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources--further-reading&quot;&gt;Sources &amp;amp; further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Yancan Mao et al. (ByteDance) — ByteMQ: A Cloud-native Streaming Data Layer in ByteDance — ACM SoCC 2024: &lt;a href=&quot;https://dl.acm.org/doi/10.1145/3698038.3698536&quot;&gt;https://dl.acm.org/doi/10.1145/3698038.3698536&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yancan Mao, Zhanghao Chen et al. (ByteDance) — StreamOps: Cloud-Native Runtime Management for Streaming Services in ByteDance — VLDB 2023: &lt;a href=&quot;https://dl.acm.org/doi/abs/10.14778/3611540.3611543&quot;&gt;https://dl.acm.org/doi/abs/10.14778/3611540.3611543&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yong Fang et al. (ByteDance) — StreamShield: A Production-Proven Resiliency Solution for Apache Flink at ByteDance — arXiv, February 2026: &lt;a href=&quot;https://arxiv.org/abs/2602.03189&quot;&gt;https://arxiv.org/abs/2602.03189&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ByteDance AI Lab — Monolith: Real Time Recommendation System With Collisionless Embedding Table — arXiv, September 2022: &lt;a href=&quot;https://arxiv.org/abs/2209.07663&quot;&gt;https://arxiv.org/abs/2209.07663&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ByteDance — BitSail: Distributed high-performance data integration engine — GitHub: &lt;a href=&quot;https://github.com/bytedance/bitsail&quot;&gt;https://github.com/bytedance/bitsail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache Software Foundation — Powered By Apache Kafka — ByteDance entry: &lt;a href=&quot;https://kafka.apache.org/powered-by/&quot;&gt;https://kafka.apache.org/powered-by/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you want visibility into the consumer groups, lag metrics, and topic throughput in your own Kafka cluster, give &lt;a href=&quot;https://kpow.io/&quot;&gt;Kpow&lt;/a&gt; a try with a free 30-day trial. It connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Cloudflare uses Apache Kafka in production</title><link>https://factorhouse.io/articles/cloudflare-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/cloudflare-kafka-architecture/</guid><description>A deep-dive into Cloudflare&apos;s Kafka architecture: use cases at trillion-message scale, 14 clusters, internal tooling decisions, and the engineering lessons behind a decade of Kafka operations.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Cloudflare has been running &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; in production since 2014. By July 2022, the company had processed over one trillion messages on its primary inter-service message bus alone, across 14 distinct Kafka clusters and approximately 330 nodes. At peak, the HTTP analytics pipeline alone was ingesting 100 Gbps and 7.5 million requests per second through Kafka. Those numbers reflect more than raw traffic volume: they reflect how deeply Kafka is woven into Cloudflare’s control plane, analytics stack, logging infrastructure, and DNS pipeline.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Cloudflare operates one of the largest networks in the world, providing content delivery, DDoS protection, DNS resolution, zero-trust security, and developer platform services to millions of customers. Its network spans hundreds of data centers globally and handles a significant share of internet traffic.&lt;/p&gt;
&lt;p&gt;Kafka was first adopted in 2014, initially to power analytics, DDoS mitigation, logging, and metrics pipelines. As the company scaled and its microservice footprint grew, Kafka took on a broader role as the backbone for inter-service communication across the control plane.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;Kafka first adopted for analytics, DDoS mitigation, logging, and metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018-03&lt;/td&gt;
&lt;td&gt;Zstandard (zstd) compression adopted on the HTTP requests topic; HTTP analytics pipeline migrated from PostgreSQL to ClickHouse; analytics cluster at 106 brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-05&lt;/td&gt;
&lt;td&gt;DNS per-record build pipeline launched; handling 250 DNS record changes per second, a 25x increase from initial deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-07&lt;/td&gt;
&lt;td&gt;1 trillion inter-service messages milestone published; 14 clusters, approximately 330 nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-01&lt;/td&gt;
&lt;td&gt;Intelligent offset-based consumer restart mechanism published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-03&lt;/td&gt;
&lt;td&gt;Matt Boyle and Andrea Medda present “Tales of Kafka @Cloudflare” at QCon London&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-01&lt;/td&gt;
&lt;td&gt;Logging pipeline overview published; approximately 1 million log lines per second; OpenTelemetry Logs migration planned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;cloudflares-kafka-use-cases&quot;&gt;Cloudflare’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Kafka at Cloudflare serves several distinct workloads, each with its own cluster or pipeline configuration.&lt;/p&gt;
&lt;h3 id=&quot;inter-service-messaging-via-the-messagebus-cluster&quot;&gt;Inter-service messaging via the Messagebus cluster&lt;/h3&gt;
&lt;p&gt;The largest and most visible use is inter-service communication across the control plane. Cloudflare’s Application Services team built a general-purpose cluster called “Messagebus” to decouple microservices by propagating resource lifecycle events: the creation, modification, or deletion of any resource is published as a Protobuf-encoded message and consumed by any interested downstream service.&lt;/p&gt;
&lt;p&gt;The alert notification system (ANS) is one example built on this pattern. Teams produce to an alert topic with a YAML configuration and a single client import; the system handles delivery to Slack, Google Chat, webhooks, PagerDuty, and email automatically. Communication preferences management across multiple systems also runs on the same bus.&lt;/p&gt;
&lt;h3 id=&quot;http-and-dns-analytics&quot;&gt;HTTP and DNS analytics&lt;/h3&gt;
&lt;p&gt;From 2014 onwards, Kafka has been the ingestion layer for HTTP request logs and DNS query logs from Cloudflare’s edge. In 2018, the HTTP analytics pipeline was processing an average of 6 million requests per second (peaking at 8 million), with the Kafka topic receiving up to 100 Gbps ingress and 7.5 million messages per second at peak.&lt;/p&gt;
&lt;p&gt;Messages in this pipeline were encoded in Cap’n Proto format. Downstream consumers initially wrote aggregated results to PostgreSQL; the pipeline was later migrated to ClickHouse, where 106 Go consumers extract more than 100 fields per message for ingestion.&lt;/p&gt;
&lt;h3 id=&quot;ddos-mitigation&quot;&gt;DDoS mitigation&lt;/h3&gt;
&lt;p&gt;Traffic telemetry from the edge is distributed to DDoS detection consumers via Kafka, giving the mitigation system a real-time feed of traffic patterns without tight coupling to the log collection infrastructure.&lt;/p&gt;
&lt;h3 id=&quot;internal-logging-pipeline&quot;&gt;Internal logging pipeline&lt;/h3&gt;
&lt;p&gt;Service logs across Cloudflare’s infrastructure flow through a multi-stage pipeline into Kafka before reaching consumers. As of 2024, this pipeline handles approximately 1 million log lines per second. Kafka buffers logs at two core data centers (“log-a” and “log-b”), providing decoupling, fault tolerance, and up to eight hours of consumer outage tolerance before data loss risk arises.&lt;/p&gt;
&lt;h3 id=&quot;dns-zone-builds&quot;&gt;DNS zone builds&lt;/h3&gt;
&lt;p&gt;DNS record changes submitted via the API trigger PostgreSQL events that are converted into Kafka messages consumed by the Zone Builder. The pipeline supports two consumer types: a full zone build scheduler and a per-record build scheduler. As of 2022, it handles an average of 250 DNS record changes per second, representing 25x growth from initial deployment.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Pipeline / date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative inter-service messages&lt;/td&gt;
&lt;td&gt;Over 1 trillion&lt;/td&gt;
&lt;td&gt;Messagebus cluster, ~8 years to July 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total clusters&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Across multiple data centers, July 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total broker nodes&lt;/td&gt;
&lt;td&gt;~330&lt;/td&gt;
&lt;td&gt;All clusters, July 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP requests topic peak ingress&lt;/td&gt;
&lt;td&gt;100 Gbps / 7.5M req/sec&lt;/td&gt;
&lt;td&gt;Analytics cluster, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP analytics cluster brokers&lt;/td&gt;
&lt;td&gt;106 (replication factor: 3)&lt;/td&gt;
&lt;td&gt;Analytics cluster, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log lines per second (logging pipeline)&lt;/td&gt;
&lt;td&gt;~1 million&lt;/td&gt;
&lt;td&gt;Logging cluster, 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS record changes per second&lt;/td&gt;
&lt;td&gt;250 (25x growth from launch)&lt;/td&gt;
&lt;td&gt;DNS pipeline, 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum replication factor&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;All clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;cloudflares-kafka-architecture&quot;&gt;Cloudflare’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;cluster-topology&quot;&gt;Cluster topology&lt;/h3&gt;
&lt;p&gt;Cloudflare runs 14 Kafka clusters across multiple data centers, deployed on bare metal alongside Kubernetes and databases in its control plane. The clusters are purpose-built: the general-purpose Messagebus cluster handles inter-service communication, while separate clusters serve analytics, logging, DNS, and other high-throughput workloads. Infrastructure is managed with Salt under a GitOps model.&lt;/p&gt;
&lt;h3 id=&quot;message-format-and-topic-design&quot;&gt;Message format and topic design&lt;/h3&gt;
&lt;p&gt;The Messagebus cluster standardised on Protocol Buffers (Protobuf) as the message serialization format, chosen over JSON and Apache Avro for its strict typing, forward and backward compatibility guarantees, and multi-language code generation. The team enforces a single Protobuf message type per topic to prevent format incompatibility across consumers.&lt;/p&gt;
&lt;p&gt;Schema governance runs through an internal “Messagebus Schema” service that maintains a central repository of all Protobuf schemas, maps team ownership to topics, and uses prototool to detect and flag breaking schema changes before they reach production. Teams are notified via chat integration when schema changes are proposed.&lt;/p&gt;
&lt;h3 id=&quot;messagebus-client-library&quot;&gt;Messagebus-Client library&lt;/h3&gt;
&lt;p&gt;The Application Services team built an internal Go library called Messagebus-Client that wraps the Shopify Sarama Kafka client. It provides opinionated configuration defaults, automatic mTLS certificate rotation, and built-in Prometheus metrics exposure for both producers and consumers. Default producer metrics include message delivery success and failure rates; default consumer metrics include consumption success and error rates. This library is the standard entry point for Cloudflare’s internal teams producing and consuming on the Messagebus cluster.&lt;/p&gt;
&lt;h3 id=&quot;connector-framework&quot;&gt;Connector Framework&lt;/h3&gt;
&lt;p&gt;For workloads that require moving data from a source system into Kafka, or from Kafka into a sink, Cloudflare built a Connector Framework on top of Kafka connectors. New connector services are generated from Cookiecutter templates and configured entirely via environment variables: readers, writers, and transformations are declared without writing custom integration code. Each generated connector includes built-in metrics and alerting. The framework supports Kafka and Cloudflare’s internal Quicksilver key-value store as both source and sink targets.&lt;/p&gt;
&lt;h3 id=&quot;logging-pipeline-architecture&quot;&gt;Logging pipeline architecture&lt;/h3&gt;
&lt;p&gt;The internal logging pipeline follows a defined path: services emit logs via stdout/stderr, which are captured by systemd-journald and forwarded through syslog-ng. syslog-ng adds metadata (hostname, data center) and forwards logs to two core data centers. At those core data centers, logs are buffered in Kafka before downstream consumers pull from them.&lt;/p&gt;
&lt;p&gt;Kafka partitioning in this pipeline uses a composite key of host name and service name. This guarantees message ordering within a partition, but creates uneven partition sizes because different machines generate different log volumes. As of 2024, the team was working on improving partition balancing at scale.&lt;/p&gt;
&lt;h3 id=&quot;dns-pipeline-architecture&quot;&gt;DNS pipeline architecture&lt;/h3&gt;
&lt;p&gt;The DNS zone build pipeline connects PostgreSQL to the Zone Builder via Kafka. When a DNS record is changed via the API, a PostgreSQL trigger emits an event that is converted to a Kafka message. The Zone Builder runs two consumer types: a full zone build scheduler for complete zone rebuilds, and a per-record build scheduler for incremental changes. The per-record approach reduced build times on large zones (1 million records) from approximately 34 seconds to 6-8 milliseconds, a 4,250x improvement.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;offset-based-consumer-health-checks&quot;&gt;Offset-based consumer health checks&lt;/h3&gt;
&lt;p&gt;One of the more notable techniques Cloudflare published is their approach to detecting unhealthy Kafka consumers. A standard connectivity check confirms that a consumer is connected to the broker and can read from the topic, but does not confirm that the consumer is actively processing messages. Consumers can deadlock after a rebalancing event and remain connected while making no progress.&lt;/p&gt;
&lt;p&gt;Cloudflare solved this by implementing a Kubernetes liveness probe that tracks committed offset advancement rather than connection state. The probe fails if a consumer’s committed offset has not changed since the previous check interval, indicating a stalled consumer. An in-memory map tracks offsets per partition. When a rebalance occurs, the system rebuilds the map using Sarama’s rebalance signal to ensure each replica only monitors its currently assigned partitions, preventing false failures during reassignment.&lt;/p&gt;
&lt;h3 id=&quot;compression-with-zstandard&quot;&gt;Compression with Zstandard&lt;/h3&gt;
&lt;p&gt;Cloudflare’s first compression choice was Snappy, which achieved a 2.25x reduction on the HTTP log topic and 2.6x on DNS logs. After benchmarking multiple algorithms, the team switched to Zstandard (zstd), which reached a 4.5x compression ratio on the HTTP requests topic. The switch saved hundreds of gigabits of network bandwidth and reduced storage footprint substantially.&lt;/p&gt;
&lt;p&gt;During the compression work, the team discovered that Kafka was recompressing already-compressed message batches due to offset validation logic in the Sarama Go client library. They diagnosed the issue and contributed a fix upstream to the Sarama project.&lt;/p&gt;
&lt;h3 id=&quot;batch-consumption-for-throughput-spikes&quot;&gt;Batch consumption for throughput spikes&lt;/h3&gt;
&lt;p&gt;For high-throughput consumers, particularly those that hit SLO breaches during traffic spikes, Cloudflare implemented batch consumption: consumers process multiple Kafka messages simultaneously before transformation and dispatch, rather than one message at a time. This approach absorbs production spikes without accumulating consumer lag.&lt;/p&gt;
&lt;h3 id=&quot;opentelemetry-integration-in-the-sdk&quot;&gt;OpenTelemetry integration in the SDK&lt;/h3&gt;
&lt;p&gt;After pandemic-era traffic growth caused a critical consumer to breach its SLA, the team added OpenTelemetry tracing directly into the Messagebus-Client SDK. This gave them visibility across the full processing chain and allowed them to pinpoint the specific bottlenecks contributing to the SLO breach: bucket writes and Kafka reads.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;Cloudflare runs its Kafka infrastructure on self-managed bare metal. Key operational practices across its 14 clusters include:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; Every producer and consumer gets automatically generated Prometheus dashboards covering production rate, consumption rate, and partition skew. The primary alert for all consumers is high consumer lag, generated without manual configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observability:&lt;/strong&gt; OpenTelemetry tracing is embedded in the Messagebus-Client SDK, enabling distributed traces that span from message production through broker delivery to consumer processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fault tolerance targets:&lt;/strong&gt; The logging pipeline Kafka buffer is sized to tolerate up to eight hours of total consumer outage before any data loss risk arises.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; Internal wikis document adoption patterns. A ChatOps integration surfaces schema change notifications in team channels. The Connector Framework’s Cookiecutter templates generate production-ready connector services with minimal input. A newer tool called Gaia enables push-button provisioning of new Kafka-integrated services aligned with Cloudflare’s internal best practices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema governance:&lt;/strong&gt; The Messagebus Schema service acts as the schema registry for the Messagebus cluster. prototool checks for breaking changes before any schema reaches production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure provisioning:&lt;/strong&gt; Salt with a GitOps model manages broker configuration and cluster provisioning.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Schema coupling despite using a message bus&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Early Kafka adoption at Cloudflare used JSON. Despite using Kafka as a decoupling layer, teams remained tightly coupled at the schema level because there was no enforcement of message contracts. The fix was a migration to Protobuf with a strict one-type-per-topic policy, combined with a client-side validation library that validates messages before they are published. This shifted schema errors from runtime consumer failures to producer-side build failures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Silent consumer failures after rebalancing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Consumers that deadlocked after partition reassignment appeared healthy under basic connectivity checks while making no progress on their partitions. The team addressed this with the offset-comparison liveness probe described above, using Kubernetes to restart stalled consumers automatically. The approach reduced false positives and caught real deadlock conditions that had previously gone undetected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disk I/O contention from multiple lagging consumers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Multiple lagging consumers generating random read patterns on spinning disks caused significant I/O contention. The resolution was to migrate those clusters to SSDs. This was an infrastructure cost decision driven by consumer access patterns, and it was preceded by the compression work, which reduced the storage and bandwidth footprint that would otherwise have made the SSD migration more expensive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SLO breach during pandemic-era traffic spike&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A critical consumer fell behind its SLA when traffic surged during the COVID-19 pandemic. The team used OpenTelemetry tracing to identify the specific bottlenecks (bucket writes and Kafka reads) and resolved the lag by implementing batch consumption and adding observability into the SDK to prevent similar blind spots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client library over-configuration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Early versions of Messagebus-Client exposed too many configuration knobs. Teams that modified defaults occasionally introduced unintended side effects, including configuration changes that affected other parts of the pipeline. The lesson was to make opinionated defaults the path of least resistance and require explicit justification for deviations. Matt Boyle and Andrea Medda presented this as one of the core tradeoffs at QCon London 2023: the right balance between a highly configurable SDK and a standardised one depends on how much you are willing to invest in documentation and support.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Central message broker across 14 clusters: inter-service bus, analytics, logging, DNS, DDoS telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message serialization&lt;/td&gt;
&lt;td&gt;Protocol Buffers (Protobuf)&lt;/td&gt;
&lt;td&gt;Standard message format on the Messagebus cluster; strict typing and cross-language code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message serialization (analytics)&lt;/td&gt;
&lt;td&gt;Cap’n Proto&lt;/td&gt;
&lt;td&gt;HTTP log encoding in the analytics Kafka pipeline (2018)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client library&lt;/td&gt;
&lt;td&gt;Shopify Sarama&lt;/td&gt;
&lt;td&gt;Go Kafka client wrapped by Messagebus-Client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application language&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Primary language for all Kafka services, connectors, and the Messagebus-Client SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application language&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Protobuf schema code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Runs Kafka consumers; liveness probes power the offset-based consumer health check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;Auto-generated metrics for every Kafka producer and consumer via Messagebus-Client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboards&lt;/td&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;Visualisation of production rate, consumption rate, and partition skew per topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed tracing&lt;/td&gt;
&lt;td&gt;OpenTelemetry / OpenTracing&lt;/td&gt;
&lt;td&gt;Embedded in Messagebus-Client SDK for end-to-end trace visibility across producers and consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics sink&lt;/td&gt;
&lt;td&gt;ClickHouse&lt;/td&gt;
&lt;td&gt;Receives HTTP log data extracted by Kafka consumers; replaced PostgreSQL for analytics aggregation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;Zstandard (zstd)&lt;/td&gt;
&lt;td&gt;Message compression on high-throughput topics; 4.5x ratio on the HTTP requests topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema governance&lt;/td&gt;
&lt;td&gt;prototool&lt;/td&gt;
&lt;td&gt;Breaking change detection in the Messagebus Schema registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure provisioning&lt;/td&gt;
&lt;td&gt;Salt&lt;/td&gt;
&lt;td&gt;Broker configuration management under a GitOps model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service scaffolding&lt;/td&gt;
&lt;td&gt;Cookiecutter&lt;/td&gt;
&lt;td&gt;Template engine for generating new Connector Framework service scaffolding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal service provisioning&lt;/td&gt;
&lt;td&gt;Gaia&lt;/td&gt;
&lt;td&gt;Push-button creation of new Kafka-integrated services according to Cloudflare’s best practices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log forwarding&lt;/td&gt;
&lt;td&gt;syslog-ng&lt;/td&gt;
&lt;td&gt;Log collection stage feeding the Kafka logging pipeline; adds hostname and data center metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log capture&lt;/td&gt;
&lt;td&gt;systemd-journald&lt;/td&gt;
&lt;td&gt;Captures service stdout/stderr and feeds syslog-ng&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS source&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;Emits change triggers that are converted into Kafka messages for the DNS Zone Builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal key-value store (connector sink)&lt;/td&gt;
&lt;td&gt;Quicksilver&lt;/td&gt;
&lt;td&gt;Cloudflare’s internal distributed key-value store; used as a Connector Framework sink target&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Matt Boyle&lt;/td&gt;
&lt;td&gt;Engineering Manager, Application Services&lt;/td&gt;
&lt;td&gt;Authored the 1 trillion messages post; co-presenter at QCon London 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Andrea Medda&lt;/td&gt;
&lt;td&gt;Senior Systems Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Co-authored the intelligent consumer restart post; co-presenter at QCon London 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chris Shepherd&lt;/td&gt;
&lt;td&gt;Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Co-authored the intelligent consumer restart post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ivan Babrou&lt;/td&gt;
&lt;td&gt;Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Authored the Kafka compression post; led the zstd evaluation and Sarama upstream patch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alex Bocharov&lt;/td&gt;
&lt;td&gt;Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Authored the HTTP analytics pipeline migration post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alex Fattouche&lt;/td&gt;
&lt;td&gt;Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Authored the DNS build speed improvement post describing the Kafka-backed zone build pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Colin Douch&lt;/td&gt;
&lt;td&gt;Engineer, Cloudflare&lt;/td&gt;
&lt;td&gt;Authored the logging pipeline overview post&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Invest in schema governance before you scale.&lt;/strong&gt; Cloudflare ran into tight coupling between consumers despite using Kafka as a decoupling layer, because JSON offered no enforcement. Migrating to Protobuf with a strict one-type-per-topic rule and a central schema registry resolved it, but required a migration effort that would have been cheaper to do earlier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection health is not the same as processing health.&lt;/strong&gt; A Kubernetes liveness probe that checks TCP connectivity to the broker tells you very little about whether a consumer is making progress. Cloudflare’s offset-comparison approach, which checks whether committed offsets advance between intervals, catches deadlocked consumers that a simple connection check would miss entirely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measure compression algorithm choice at your actual message shape.&lt;/strong&gt; Snappy was a reasonable starting point, but benchmarking on Cloudflare’s actual HTTP log messages showed that zstd delivered twice the compression ratio. The specific encoding format (Cap’n Proto, Protobuf, JSON) and message content distribution matter more than general benchmarks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opinionated client libraries reduce operational surface area, but documentation is the cost.&lt;/strong&gt; The Messagebus-Client library made Kafka accessible to teams without deep Kafka expertise, and the default metrics meant every consumer was observable from day one. The tradeoff was that over-configurability in early versions caused unintended side effects. Cloudflare’s recommendation at QCon 2023 was to provide opinionated defaults and invest heavily in documentation so teams don’t deviate without understanding the implications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate clusters by access pattern and criticality.&lt;/strong&gt; Cloudflare’s 14 clusters are not all running the same workload, and the separation matters operationally. The analytics cluster at 106 brokers (2018) was optimised for throughput; the Messagebus cluster was optimised for reliability and schema governance. Running all workloads on a single cluster would have created both operational and performance coupling.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Author&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Using Apache Kafka to process 1 trillion inter-service messages&lt;/td&gt;
&lt;td&gt;Matt Boyle, Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2022-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Squeezing the firehose: getting the most from Kafka compression&lt;/td&gt;
&lt;td&gt;Ivan Babrou, Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2018-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;HTTP analytics for 6M requests per second using ClickHouse&lt;/td&gt;
&lt;td&gt;Alex Bocharov, Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2018-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Tales of Kafka at Cloudflare: lessons learnt on the way to 1 trillion messages&lt;/td&gt;
&lt;td&gt;Matt Boyle, Andrea Medda — InfoQ&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Intelligent, automatic restarts for unhealthy Kafka consumers&lt;/td&gt;
&lt;td&gt;Chris Shepherd, Andrea Medda — Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2023-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;An overview of Cloudflare’s logging pipeline&lt;/td&gt;
&lt;td&gt;Colin Douch, Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2024-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;How we improved DNS record build speed by more than 4,000x&lt;/td&gt;
&lt;td&gt;Alex Fattouche, Cloudflare Blog&lt;/td&gt;
&lt;td&gt;2022-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Tales of Kafka at Cloudflare: Andrea Medda and Matt Boyle at QCon London 2023&lt;/td&gt;
&lt;td&gt;InfoQ editorial, QCon London 2023&lt;/td&gt;
&lt;td&gt;2023-04&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;If you are running Kafka in production and want better visibility into your cluster, consumer lag, and topic health, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a real-time control plane for Apache Kafka. You can connect it to any Kafka cluster in minutes and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How DoorDash uses Apache Kafka in production</title><link>https://factorhouse.io/articles/doordash-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/doordash-kafka-architecture/</guid><description>A deep-dive into DoorDash&apos;s Kafka architecture — covering the Iguazu event platform, Flink-based ML feature pipelines, self-serve topic governance, and the engineering decisions behind hundreds of billions of daily events.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;DoorDash operates one of the largest food delivery platforms in the United States, coordinating millions of orders, restaurant partnerships, and Dasher assignments every day. Underpinning that coordination is an &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; deployment that spans five clusters, more than 2,500 topics, and around six billion messages per day on average — with peaks reaching twice that rate.&lt;/p&gt;
&lt;p&gt;The company reached that scale through two distinct adoption waves: an initial migration from RabbitMQ in mid-2019 to stop task-processing outages, then a ground-up rebuild of its real-time data infrastructure under a platform called Iguazu that replaced Amazon Kinesis and SQS and brought data warehouse latency down from one day to a few minutes.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;DoorDash connects consumers, restaurants, and delivery drivers (Dashers) across the United States, Canada, Australia, and Japan. The platform processes a high volume of time-sensitive transactions — order placement, restaurant confirmation, Dasher assignment, and delivery completion — all of which depend on low-latency event propagation between microservices.&lt;/p&gt;
&lt;p&gt;DoorDash first adopted Kafka in mid-2019 after repeated RabbitMQ failures began causing order checkout and Dasher dispatch outages under peak load. That initial migration, led by engineer Ashwin Kachhara, addressed the immediate reliability problem. A separate initiative, beginning around 2020 and led by Allen Wang (who had previously built Netflix’s Keystone Kafka pipeline), took a broader view: replacing multiple fragmented cloud-native queuing systems with a unified, Kafka-centred streaming platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka milestones at DoorDash&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mid-2019&lt;/td&gt;
&lt;td&gt;RabbitMQ and Celery outages drive adoption of Kafka for order checkout and Dasher assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Late 2019&lt;/td&gt;
&lt;td&gt;Allen Wang joins from Netflix; begins building the Real-Time Streaming Platform (RTSP) team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~2020&lt;/td&gt;
&lt;td&gt;Construction of Iguazu begins; Kafka replaces Amazon SQS and Kinesis as the central ingestion hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Riviera declarative ML feature framework goes into production (Kafka + Flink SQL + Redis)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;Iguazu reaches hundreds of billions of events per day; Allen Wang publishes the primary architecture post and presents at QCon San Francisco&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;API-first Kafka topic governance replaces Terraform/Atlantis GitOps; 2,500+ topics, five clusters, six billion messages per day documented publicly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;Kafka multi-tenancy via OpenTelemetry context propagation; full self-serve CRUD for topics, users, and ACLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Fabricator (successor to Riviera) launched: 100+ pipelines, 500 ML features, 100 billion daily feature values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;doordashs-kafka-use-cases&quot;&gt;DoorDash’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;asynchronous-task-processing&quot;&gt;Asynchronous task processing&lt;/h3&gt;
&lt;p&gt;The original use case. Before mid-2019, DoorDash routed order checkout and Dasher assignment work through RabbitMQ and Celery. Repeated broker failures under peak load caused customer-facing outages with direct revenue impact; RabbitMQ offered limited observability and required slow, manual recovery steps.&lt;/p&gt;
&lt;p&gt;Ashwin Kachhara and his team replaced RabbitMQ with Kafka for asynchronous task processing. Kafka’s durable log, at-least-once delivery guarantees, and horizontal scalability eliminated the outage pattern. The migration was completed without downtime using a gradual traffic cutover approach.&lt;/p&gt;
&lt;h3 id=&quot;real-time-event-pipeline-to-snowflake-iguazu&quot;&gt;Real-time event pipeline to Snowflake (Iguazu)&lt;/h3&gt;
&lt;p&gt;The more architecturally significant initiative. Before Iguazu, DoorDash maintained separate pipelines using Amazon SQS and Amazon Kinesis to feed its data warehouse, ML platform, and time-series metrics backend. Each pipeline used different technology and communication patterns, producing data warehouse latency of up to one day and significant operational overhead.&lt;/p&gt;
&lt;p&gt;Iguazu replaced those fragmented pipelines with a single Kafka-centred platform. All microservices and mobile clients publish events via an HTTP-based Kafka proxy; Apache Flink jobs consume those topics and fan out to multiple destinations (Snowflake, Redis, Chronosphere) with format transformations applied per sink. End-to-end latency to Snowflake dropped from one day to a few minutes.&lt;/p&gt;
&lt;p&gt;Use cases documented within Iguazu include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dasher assignment monitoring:&lt;/strong&gt; near-real-time warehouse data lets the Dasher assignment team detect algorithm bugs within minutes rather than the next day&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile application health monitoring:&lt;/strong&gt; checkout page load errors are routed to Chronosphere for operational alerting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-time user sessionization and behaviour analysis&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;real-time-ml-feature-generation-riviera-and-fabricator&quot;&gt;Real-time ML feature generation (Riviera and Fabricator)&lt;/h3&gt;
&lt;p&gt;Delivery events are consumed from Kafka topics by Flink jobs to produce real-time ML features — for example, recent average restaurant wait times. The Riviera framework (and its 2025 successor, Fabricator) expresses these pipelines as a SQL query plus a YAML configuration file, routing Kafka topic data through Flink into Redis for low-latency reads by inference services. By 2025, the Fabricator platform ran more than 100 pipelines generating 500 unique features at a rate of over 100 billion feature values per day.&lt;/p&gt;
&lt;h3 id=&quot;search-index-replication&quot;&gt;Search index replication&lt;/h3&gt;
&lt;p&gt;The Search Platform team built a continuous indexing pipeline where database changes propagate through Kafka to Flink applications, which then update Elasticsearch. Before this pipeline, a full reindex of the search catalogue took up to one week; the Kafka and Flink approach made it continuous and near-real-time.&lt;/p&gt;
&lt;h3 id=&quot;advertising-budget-pacing&quot;&gt;Advertising budget pacing&lt;/h3&gt;
&lt;p&gt;The ads platform publishes a record to a Kafka topic when a campaign hits its daily spend limit. A Flink streaming job consumes that topic and writes an expiration flag to CockroachDB, embedding the budget-capped state into DoorDash’s in-house search index at the filtering stage. This change produced a 43% drop in search processor latency and a 45% reduction in discarded candidates compared to the previous approach.&lt;/p&gt;
&lt;h3 id=&quot;production-testing-via-kafka-multi-tenancy&quot;&gt;Production testing via Kafka multi-tenancy&lt;/h3&gt;
&lt;p&gt;In 2024, DoorDash introduced a multi-tenant Kafka architecture to allow test traffic to flow through the same production topics and consumer instances as live traffic. OpenTelemetry context propagation carries a “doortest” tenant identifier on each Kafka record; consumers branch on that metadata without creating separate topics. This approach enables production-fidelity testing without topic proliferation.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Figure&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka clusters&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;2,500+&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily message volume&lt;/td&gt;
&lt;td&gt;~6 billion messages&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average throughput&lt;/td&gt;
&lt;td&gt;~4 million messages per minute&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak throughput&lt;/td&gt;
&lt;td&gt;~8 million messages per minute&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iguazu event volume&lt;/td&gt;
&lt;td&gt;Hundreds of billions of events per day&lt;/td&gt;
&lt;td&gt;Allen Wang, DoorDash Engineering Blog, August 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery guarantee&lt;/td&gt;
&lt;td&gt;99.99% (four nines)&lt;/td&gt;
&lt;td&gt;Allen Wang, DoorDash Engineering Blog, August 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data loss rate (async mode)&lt;/td&gt;
&lt;td&gt;Less than 0.001%&lt;/td&gt;
&lt;td&gt;Allen Wang, InfoQ / QCon Plus, June 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flink daily processing volume&lt;/td&gt;
&lt;td&gt;220 TB per day&lt;/td&gt;
&lt;td&gt;Junaid Effendi, The Sequence (citing DoorDash engineering sources)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New topics provisioned per week&lt;/td&gt;
&lt;td&gt;~100&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, December 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warehouse latency improvement&lt;/td&gt;
&lt;td&gt;~1 day to a few minutes&lt;/td&gt;
&lt;td&gt;Allen Wang, InfoQ / QCon Plus, June 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fabricator daily feature values&lt;/td&gt;
&lt;td&gt;100+ billion&lt;/td&gt;
&lt;td&gt;DoorDash Engineering Blog, April 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;doordashs-kafka-architecture&quot;&gt;DoorDash’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;high-level-overview&quot;&gt;High-level overview&lt;/h3&gt;
&lt;p&gt;DoorDash runs its Kafka infrastructure on AWS. The five clusters are operated by the Real-Time Streaming Platform (RTSP) team. Public sources do not describe the specific role of each cluster (whether they are segmented by team, environment, or criticality), though all five are provisioned and governed through the same internal tooling.&lt;/p&gt;
&lt;p&gt;The Iguazu architecture has three main layers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ingestion:&lt;/strong&gt; An HTTP-based Kafka proxy (built on Confluent’s open-source Kafka REST Proxy) runs on Kubernetes and accepts events from microservices and mobile clients. The proxy provides a REST interface so internal producers do not need native Kafka client configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processing:&lt;/strong&gt; Apache Flink jobs run as standalone Kubernetes services deployed via Helm charts, consuming Kafka topics and applying per-destination fan-out and format transformations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delivery:&lt;/strong&gt; Events are written to S3 in Parquet format, then ingested into Snowflake via Snowpipe (triggered by Amazon SQS notifications). ML features go to Redis. Operational metrics go to Chronosphere.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;event-ingestion-layer-the-iguazu-kafka-proxy&quot;&gt;Event ingestion layer: the Iguazu Kafka proxy&lt;/h3&gt;
&lt;p&gt;DoorDash extended the open-source Confluent Kafka REST Proxy with several additions for the Iguazu use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-cluster routing (a single proxy endpoint routes to the appropriate cluster)&lt;/li&gt;
&lt;li&gt;Asynchronous producing (decoupling publisher latency from Kafka ACK wait)&lt;/li&gt;
&lt;li&gt;Metadata pre-fetching&lt;/li&gt;
&lt;li&gt;HTTP header passthrough for OpenTelemetry context propagation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;All events are defined in Protocol Buffers. Schemas are stored in a central shared Protobuf Git repository and validated at build time in CI/CD pipelines — invalid schemas are caught before deployment, not at runtime. Confluent Schema Registry handles runtime schema lookup.&lt;/p&gt;
&lt;p&gt;Where downstream sinks require Avro (for example, certain legacy connectors), DoorDash’s serialization library converts Protobuf to Avro transparently using Avro’s protobuf library. Producers remain Protobuf-native and are unaware of Avro consumers.&lt;/p&gt;
&lt;p&gt;Every event flowing through Iguazu uses a standard envelope containing: creation time, source service, encoding method reference, and a schema registry pointer.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;For high-volume event topics without natural message keys (random partition assignment), DoorDash’s default producer configuration produced small, frequent batches and excessive broker CPU load. The RTSP team addressed this with two configuration changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Switching to Kafka’s built-in sticky partitioner (batching null-keyed records to the same partition until a batch is ready)&lt;/li&gt;
&lt;li&gt;Increasing &lt;code&gt;linger.ms&lt;/code&gt; to 50-100 ms to accumulate larger batches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result was a 30-40% reduction in Kafka broker CPU utilisation on those topics.&lt;/p&gt;
&lt;p&gt;For topics requiring delivery guarantees, DoorDash uses replication factor 2 with &lt;code&gt;min.insync.replicas&lt;/code&gt; set to 1 — a deliberate choice to favour throughput and cost over maximum durability on non-critical event streams.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer groups are managed centrally by the RTSP team. Topic readiness after provisioning is confirmed by polling Prometheus metrics via Chronosphere; the Minions orchestration service waits until topic metrics appear in Chronosphere before marking an onboarding workflow complete.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink is the primary stream processing engine. DoorDash exposes two interfaces to internal users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flink DataStream API&lt;/strong&gt; for custom stateful processing logic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flink SQL with YAML configuration&lt;/strong&gt; (via the Riviera and Fabricator frameworks) for declarative feature pipelines, where engineers write a SQL query and a YAML file rather than Flink code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;data-delivery-paths&quot;&gt;Data delivery paths&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake (data warehouse)&lt;/td&gt;
&lt;td&gt;Kafka topic → Flink job → S3 (Parquet) → SQS notification → Snowpipe → Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis (ML feature store)&lt;/td&gt;
&lt;td&gt;Kafka topic → Flink SQL job (Riviera / Fabricator) → Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chronosphere (operational metrics)&lt;/td&gt;
&lt;td&gt;Kafka topic → Flink job → Chronosphere time-series backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elasticsearch (search index)&lt;/td&gt;
&lt;td&gt;Database change event → Kafka topic → Flink job → Elasticsearch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CockroachDB (ad budget state)&lt;/td&gt;
&lt;td&gt;Ad spend event → Kafka topic → Flink streaming job → CockroachDB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;S3 acts as a durable buffer independent of Kafka retention. If Snowflake suffers downtime, failed Snowpipe ingestions can be backfilled from S3 without depending on Kafka replayability.&lt;/p&gt;
&lt;p&gt;Per-event isolation is a design principle: each event type has its own dedicated Flink application and its own Snowpipe instance. Failures are contained to a single event type rather than cascading across a shared pipeline.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-decisions&quot;&gt;Special techniques and engineering decisions&lt;/h2&gt;
&lt;h3 id=&quot;declarative-ml-feature-pipelines&quot;&gt;Declarative ML feature pipelines&lt;/h3&gt;
&lt;p&gt;The Riviera framework (2021) and its successor Fabricator (2025) allow data scientists to express Flink stream processing jobs as a SQL query plus a YAML configuration file. The framework generates the Flink job; engineers who would otherwise need to write DataStream API code can instead write a single YAML file. Feature development time was reduced from weeks to hours, and the feature engineering codebase shrank by 70%.&lt;/p&gt;
&lt;h3 id=&quot;s3-as-a-durable-overflow-buffer&quot;&gt;S3 as a durable overflow buffer&lt;/h3&gt;
&lt;p&gt;Finite Kafka retention creates a risk: if Snowflake is unavailable long enough for Kafka logs to roll off, data is lost. DoorDash addressed this by routing all Iguazu Flink jobs through S3 (Parquet) before Snowpipe ingestion. S3 is not subject to Kafka retention windows, so backfills remain possible regardless of Kafka log state.&lt;/p&gt;
&lt;h3 id=&quot;opentelemetry-based-kafka-multi-tenancy&quot;&gt;OpenTelemetry-based Kafka multi-tenancy&lt;/h3&gt;
&lt;p&gt;Rather than creating separate topics per test scenario, DoorDash propagates OpenTelemetry context (tenant ID and route information) on every Kafka record header. Production and test traffic share the same topics and consumer group instances; consumers inspect the OTEL header to branch handling. This avoids topic proliferation while providing production-fidelity test conditions.&lt;/p&gt;
&lt;h3 id=&quot;protobuf-to-avro-transparent-conversion&quot;&gt;Protobuf-to-Avro transparent conversion&lt;/h3&gt;
&lt;p&gt;DoorDash maintains Protobuf as the canonical internal event format. Where downstream sinks require Avro, its serialization library converts on the fly. This allows the company to remain Protobuf-native without modifying producers when adding Avro-dependent sinks.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;
&lt;p&gt;All five Kafka clusters run on AWS, managed by the RTSP team. Flink jobs are deployed as standalone Kubernetes services via Helm charts. Infrastructure is managed with Terraform.&lt;/p&gt;
&lt;h3 id=&quot;topic-governance&quot;&gt;Topic governance&lt;/h3&gt;
&lt;p&gt;DoorDash went through three generations of topic provisioning tooling:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Terraform / Atlantis (GitOps):&lt;/strong&gt; Required infrastructure team review for every topic creation request. As the pace of new topics grew to roughly 100 per week, this created a significant support burden.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infra Service + Minions (2023):&lt;/strong&gt; Replaced GitOps with an HTTP API (Infra Service) and a Cadence-backed orchestration service (Minions) that automates end-to-end Iguazu onboarding — creating Kafka topics, launching Flink jobs, creating Snowflake objects, and opening pull requests, with Slack notifications at each step. Configuration options exposed to users are deliberately restricted to capacity-related settings (retention, partition count) to prevent common misconfigurations. Onboarding time reduced by 95%, from multiple days to under one hour, typically within 15 minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Self-Serve (2024):&lt;/strong&gt; Extended Infra Service to cover the full resource lifecycle — topics, user accounts, and ACLs — with auto-approval rules for routine, non-sensitive changes such as standard ACL grants or topic resizes within pre-set bounds.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;authentication-and-access-control&quot;&gt;Authentication and access control&lt;/h3&gt;
&lt;p&gt;DoorDash uses SASL/SCRAM for per-service Kafka authentication and ACLs for authorisation, controlling which producers and consumer groups can access each topic. The 2024 Kafka Self-Serve platform manages user account creation and ACL assignment through the same API-first workflow as topic creation.&lt;/p&gt;
&lt;h3 id=&quot;monitoring&quot;&gt;Monitoring&lt;/h3&gt;
&lt;p&gt;Chronosphere is DoorDash’s operational metrics backend for Kafka. Topic readiness after provisioning is confirmed by polling Prometheus metrics through Chronosphere’s API — the Minions orchestration workflow waits on this before marking onboarding complete. Application-level operational events (for example, mobile checkout errors) are also routed through Iguazu Flink jobs to Chronosphere for alerting.&lt;/p&gt;
&lt;h3 id=&quot;schema-evolution&quot;&gt;Schema evolution&lt;/h3&gt;
&lt;p&gt;Protobuf’s inherent backward and forward compatibility is the primary schema evolution mechanism. Schemas must pass CI/CD validation before merging; runtime enforcement is handled through Confluent Schema Registry. The Fabricator framework enforces backward- and forward-compatible evolution at the framework level.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-doordash-solved-them&quot;&gt;Challenges and how DoorDash solved them&lt;/h2&gt;
&lt;h3 id=&quot;rabbitmq-reliability-failures-at-peak-load&quot;&gt;RabbitMQ reliability failures at peak load&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; RabbitMQ repeatedly went down under heavy order volume. Because order checkout and Dasher assignment ran through RabbitMQ and Celery, broker failures directly caused customer-facing outages. Recovery was slow and manual; observability was poor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Replaced RabbitMQ with Kafka for asynchronous task processing using a gradual traffic cutover with no downtime. Kafka’s durable log and horizontal scalability eliminated the outage pattern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Task-processing outages from broker failures ceased; the system could scale horizontally to handle peak load.&lt;/p&gt;
&lt;p&gt;Source: Ashwin Kachhara, DoorDash Engineering Blog, September 2020.&lt;/p&gt;
&lt;h3 id=&quot;fragmented-sqs-and-kinesis-pipelines-with-one-day-warehouse-latency&quot;&gt;Fragmented SQS and Kinesis pipelines with one-day warehouse latency&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Separate SQS and Kinesis pipelines for the data warehouse, ML platform, and metrics backend used incompatible technologies and communication paradigms, producing warehouse data latency of up to one day and significant operational overhead from managing disparate systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Consolidated onto a single Kafka + Flink platform (Iguazu). Kafka serves as the unified pub/sub hub; Flink applies per-destination fan-out with data format transformations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Warehouse latency from one day to a few minutes; a single platform team operates the infrastructure.&lt;/p&gt;
&lt;p&gt;Source: Allen Wang, DoorDash Engineering Blog, August 2022.&lt;/p&gt;
&lt;h3 id=&quot;high-broker-cpu-on-null-keyed-event-streams&quot;&gt;High broker CPU on null-keyed event streams&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; For high-volume topics without message keys, the default round-robin partition assignment produced small, frequent batches, generating excessive CPU load on brokers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Configured Kafka’s sticky partitioner and increased &lt;code&gt;linger.ms&lt;/code&gt; to 50-100 ms to accumulate larger batches per partition before flushing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; 30-40% reduction in Kafka broker CPU utilisation on affected topics.&lt;/p&gt;
&lt;p&gt;Source: Allen Wang, August 2022 (via Shen Zhu engineering blog summary).&lt;/p&gt;
&lt;h3 id=&quot;gevent-incompatibility-with-librdkafka-in-python&quot;&gt;Gevent incompatibility with librdkafka in Python&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; A Python point-of-sale service used Gevent for async I/O via monkey-patching. Gevent cannot patch librdkafka (a C library), so native Kafka consumer usage blocked Gevent’s event loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Ran the Kafka consumer loop inside a dedicated Gevent greenlet. Blocking I/O calls inside the consumer were replaced with Gevent-compatible equivalents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The migrated service outperformed the previous Celery/Gevent worker under heavy I/O.&lt;/p&gt;
&lt;p&gt;Source: Jessica Zhao and Boyang Wei, DoorDash Engineering Blog, February 2021.&lt;/p&gt;
&lt;h3 id=&quot;snowflake-downtime-risking-data-loss-within-kafka-retention-windows&quot;&gt;Snowflake downtime risking data loss within Kafka retention windows&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Kafka’s finite log retention creates a data loss risk if Snowflake is unavailable long enough for topic logs to roll off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; All Iguazu Flink jobs write to S3 in Parquet format before Snowpipe ingestion. S3 serves as a durable buffer independent of Kafka retention; backfills are possible from S3 regardless of Kafka log state.&lt;/p&gt;
&lt;p&gt;Source: Allen Wang, InfoQ / QCon Plus, June 2023.&lt;/p&gt;
&lt;h3 id=&quot;manual-topic-provisioning-becoming-a-bottleneck&quot;&gt;Manual topic provisioning becoming a bottleneck&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; GitOps-based topic creation required infrastructure team review for every new topic. At approximately 100 new topics per week, this created a significant support burden and slowed engineering teams waiting for approvals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Replaced GitOps with an HTTP API (Infra Service) backed by Minions/Cadence orchestration, with auto-approval for routine requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Iguazu onboarding time reduced by 95%, from multiple days to under one hour.&lt;/p&gt;
&lt;p&gt;Source: DoorDash Engineering Blog, December 2023.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Central pub/sub and event streaming backbone; 5 clusters, 2,500+ topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;DataStream API for custom logic; Flink SQL for declarative feature pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event ingestion proxy&lt;/td&gt;
&lt;td&gt;Confluent Kafka REST Proxy (self-hosted, open source)&lt;/td&gt;
&lt;td&gt;HTTP-based event ingestion layer (Iguazu Kafka Proxy) for microservices and mobile clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Runtime schema lookup and enforcement for Protobuf and Avro events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema format (primary)&lt;/td&gt;
&lt;td&gt;Protocol Buffers (Protobuf)&lt;/td&gt;
&lt;td&gt;Primary event schema format; defined in a central shared Git repository, validated at CI/CD build time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema format (secondary)&lt;/td&gt;
&lt;td&gt;Avro&lt;/td&gt;
&lt;td&gt;Auto-converted from Protobuf by DoorDash’s serialization library for sinks that require it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate storage&lt;/td&gt;
&lt;td&gt;Amazon S3&lt;/td&gt;
&lt;td&gt;Durable buffer for Parquet-format event data between Flink jobs and Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warehouse ingestion&lt;/td&gt;
&lt;td&gt;Amazon SQS + Snowpipe&lt;/td&gt;
&lt;td&gt;SQS notifications trigger Snowpipe to ingest from S3 to Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data warehouse&lt;/td&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Analytical data warehouse for business intelligence and Dasher monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature store&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;Sink for Riviera/Fabricator real-time ML feature pipelines; serves inference services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OLAP / internal analytics&lt;/td&gt;
&lt;td&gt;Apache Pinot&lt;/td&gt;
&lt;td&gt;Real-time OLAP for ad campaign reporting and Risk Platform dashboards; fed from Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring backend&lt;/td&gt;
&lt;td&gt;Chronosphere&lt;/td&gt;
&lt;td&gt;Operational metrics and alerting; receives real-time events from Iguazu; confirms Kafka topic readiness after provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Runs Flink jobs (as standalone services) and the Kafka REST Proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Helm&lt;/td&gt;
&lt;td&gt;Deploys Flink jobs on Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure-as-code&lt;/td&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;Infrastructure provisioning for Kafka and surrounding systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflow orchestration&lt;/td&gt;
&lt;td&gt;Cadence&lt;/td&gt;
&lt;td&gt;Workflow engine backing the Minions orchestration service for Iguazu onboarding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal UI&lt;/td&gt;
&lt;td&gt;Retool&lt;/td&gt;
&lt;td&gt;Internal management UI for Iguazu pipeline configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;SASL/SCRAM&lt;/td&gt;
&lt;td&gt;Per-service Kafka authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorisation&lt;/td&gt;
&lt;td&gt;Kafka ACLs&lt;/td&gt;
&lt;td&gt;Controls which producers and consumer groups can access each topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability / context&lt;/td&gt;
&lt;td&gt;OpenTelemetry (OTEL)&lt;/td&gt;
&lt;td&gt;Context propagation for Kafka multi-tenancy; carries tenant and route metadata on each record header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search index&lt;/td&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;Continuously updated via Kafka + Flink indexing pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ad state storage&lt;/td&gt;
&lt;td&gt;CockroachDB&lt;/td&gt;
&lt;td&gt;Stores budget-capped flags written by the Kafka/Flink advertising pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing&lt;/td&gt;
&lt;td&gt;Apache Spark&lt;/td&gt;
&lt;td&gt;Large-scale data lake transformations (separate from the Kafka streaming path)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch orchestration&lt;/td&gt;
&lt;td&gt;Apache Airflow&lt;/td&gt;
&lt;td&gt;Orchestrates batch pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature pipeline orchestration&lt;/td&gt;
&lt;td&gt;Dagster&lt;/td&gt;
&lt;td&gt;DAG construction and orchestration within the Fabricator feature engineering framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL query engine&lt;/td&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;Unified SQL query layer over the data lake&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Allen Wang&lt;/td&gt;
&lt;td&gt;Tech Lead, Data Platform; founding member, Real-Time Streaming Platform&lt;/td&gt;
&lt;td&gt;Architect of Iguazu; authored the primary August 2022 engineering blog post; presented at QCon San Francisco 2022 and QCon Plus 2022; previously built Netflix’s Keystone Kafka pipeline; contributed rack-aware consumer partition assignment to Apache Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashwin Kachhara&lt;/td&gt;
&lt;td&gt;Software Engineer, Kotlin and Go Platform team&lt;/td&gt;
&lt;td&gt;Led the 2019 RabbitMQ-to-Kafka migration; authored the September 2020 engineering blog post; presented at the Bay Area Apache Kafka meetup and a Confluent event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jessica Zhao&lt;/td&gt;
&lt;td&gt;Software Engineer&lt;/td&gt;
&lt;td&gt;Co-authored the February 2021 post on making Kafka consumer compatible with Gevent in Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boyang Wei&lt;/td&gt;
&lt;td&gt;Software Engineer&lt;/td&gt;
&lt;td&gt;Co-authored the February 2021 post on making Kafka consumer compatible with Gevent in Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carlos Herrera&lt;/td&gt;
&lt;td&gt;Software Engineer, Developer Platform (Infrastructure Engineering)&lt;/td&gt;
&lt;td&gt;Lead author on the March 2024 Kafka multi-tenancy blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seed Zeng&lt;/td&gt;
&lt;td&gt;Software Engineer, Storage team&lt;/td&gt;
&lt;td&gt;Co-authored the August 2024 Kafka Self-Serve blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kane Du&lt;/td&gt;
&lt;td&gt;Software Engineer, Storage Self-Serve platform&lt;/td&gt;
&lt;td&gt;Co-authored the August 2024 Kafka Self-Serve blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Donovan Bai&lt;/td&gt;
&lt;td&gt;Software Engineer, Storage team (stateful systems including Kafka)&lt;/td&gt;
&lt;td&gt;Co-authored the August 2024 Kafka Self-Serve blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use S3 as a durable layer between Kafka and your warehouse.&lt;/strong&gt; Kafka retention is finite; if your downstream sink has an outage, you lose the ability to replay. Routing Flink output through S3 (Parquet) before Snowpipe decouples ingestion from Kafka retention windows and allows backfill regardless of Kafka log state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tune your producer for null-keyed high-volume topics.&lt;/strong&gt; If you have event streams without natural message keys, round-robin partition assignment produces small, frequent batches and elevated broker CPU. Kafka’s sticky partitioner combined with a &lt;code&gt;linger.ms&lt;/code&gt; of 50-100 ms is a low-risk configuration change that can meaningfully reduce broker load.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restrict what users can configure when self-serving topics.&lt;/strong&gt; DoorDash’s API-first governance deliberately exposes only capacity-related settings (retention, partition count) to internal users, hiding complex broker parameters. This design choice reduces misconfiguration without removing autonomy, and it scales better than a review-based GitOps model as topic volume grows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consider isolating Flink jobs per event type rather than sharing pipelines.&lt;/strong&gt; Per-event isolation means a failure in one pipeline does not cascade to others. The trade-off is more Kubernetes deployments to manage; DoorDash addressed that with Helm and centralised orchestration through Minions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you run large-scale ML feature pipelines on Kafka, evaluate a declarative DSL layer.&lt;/strong&gt; Riviera and Fabricator show that abstracting Flink SQL behind a YAML configuration significantly widens the pool of engineers who can create and maintain real-time feature pipelines, at the cost of framework complexity that the platform team must own.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Primary sources&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ashwin Kachhara, “&lt;a href=&quot;https://doordash.engineering/2020/09/03/eliminating-task-processing-outages-with-kafka/&quot;&gt;Eliminating Task Processing Outages by Replacing RabbitMQ with Apache Kafka Without Downtime&lt;/a&gt;,” DoorDash Engineering Blog, September 2020.&lt;/li&gt;
&lt;li&gt;Jessica Zhao and Boyang Wei, “&lt;a href=&quot;https://doordash.engineering/2021/02/17/how-to-make-kafka-consumer-compatible-with-gevent-in-python/&quot;&gt;How to Make Kafka Consumer Compatible with Gevent in Python&lt;/a&gt;,” DoorDash Engineering Blog, February 2021.&lt;/li&gt;
&lt;li&gt;DoorDash Engineering, “&lt;a href=&quot;https://doordash.engineering/2021/03/04/building-a-declarative-real-time-feature-engineering-framework/&quot;&gt;Building A Declarative Real-Time Feature Engineering Framework&lt;/a&gt;,” DoorDash Engineering Blog, March 2021.&lt;/li&gt;
&lt;li&gt;Satish, Danial, and Siddharth, “&lt;a href=&quot;https://doordash.engineering/2021/07/14/open-source-search-indexing/&quot;&gt;Building Faster Indexing with Apache Kafka and Elasticsearch&lt;/a&gt;,” DoorDash Engineering Blog, July 2021.&lt;/li&gt;
&lt;li&gt;Allen Wang, “&lt;a href=&quot;https://doordash.engineering/2022/08/02/building-scalable-real-time-event-processing-with-kafka-and-flink/&quot;&gt;Building Scalable Real-Time Event Processing with Kafka and Flink&lt;/a&gt;,” DoorDash Engineering Blog, August 2022.&lt;/li&gt;
&lt;li&gt;Allen Wang, “&lt;a href=&quot;https://qconsf.com/presentation/oct2022/zero-hundred-billion-building-scalable-real-time-event-processing-doordash&quot;&gt;From Zero to a Hundred Billion: Building Scalable Real-Time Event Processing at DoorDash&lt;/a&gt;,” QCon San Francisco, October 2022.&lt;/li&gt;
&lt;li&gt;Allen Wang, “&lt;a href=&quot;https://www.infoq.com/presentations/doordash-event-system/&quot;&gt;Building Scalable Real Time Event Processing with Kafka and Flink&lt;/a&gt;,” InfoQ / QCon Plus (recorded), June 2023.&lt;/li&gt;
&lt;li&gt;DoorDash Engineering (RTSP team), “&lt;a href=&quot;https://doordash.engineering/2023/12/05/api-first-approach-to-kafka-topic-creation/&quot;&gt;API-First Approach to Kafka Topic Creation&lt;/a&gt;,” DoorDash Engineering Blog, December 2023.&lt;/li&gt;
&lt;li&gt;DoorDash Engineering, “&lt;a href=&quot;https://doordash.engineering/2024/02/27/introducing-doordashs-in-house-search-engine/&quot;&gt;Introducing DoorDash’s In-House Search Engine&lt;/a&gt;,” DoorDash Engineering Blog, February 2024.&lt;/li&gt;
&lt;li&gt;Carlos Herrera, Amit Gud, and Yunji Zhong, “&lt;a href=&quot;https://doordash.engineering/2024/03/27/setting-up-kafka-multi-tenancy/&quot;&gt;Setting Up Kafka Multi-Tenancy&lt;/a&gt;,” DoorDash Engineering Blog, March 2024.&lt;/li&gt;
&lt;li&gt;Seed Zeng, Kane Du, and Donovan Bai, “&lt;a href=&quot;https://doordash.engineering/2024/08/13/doordash-engineers-with-kafka-self-serve/&quot;&gt;DoorDash Empowers Engineers with Kafka Self-Serve&lt;/a&gt;,” DoorDash Engineering Blog, August 2024.&lt;/li&gt;
&lt;li&gt;DoorDash Engineering, “&lt;a href=&quot;https://careersatdoordash.com/blog/introducing-fabricator-a-declarative-feature-engineering-framework/&quot;&gt;Introducing Fabricator: A Declarative Feature Engineering Framework&lt;/a&gt;,” DoorDash Engineering Blog, April 2025.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are running Kafka in production and want visibility into consumer lag, topic health, and cluster performance, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a UI and monitoring layer that works with any Kafka deployment. You can try it with a free 30-day trial and connect it to any cluster in minutes via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Goldman Sachs uses Apache Kafka in production</title><link>https://factorhouse.io/articles/goldman-sachs-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/goldman-sachs-kafka-architecture/</guid><description>A deep-dive into Goldman Sachs&apos;s Kafka architecture — covering use cases across three divisions, migration to Amazon MSK, resilience design, and key engineering decisions.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Goldman Sachs runs &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; across at least three separate business divisions, each with a different deployment model and a different set of problems to solve. One team operates a resilient on-premises cluster designed around the assumption that failures will always occur. Another migrated its cluster to Amazon MSK to reduce operational overhead. A third uses Kafka as the messaging bus for a payments platform that must process instant transactions with exactly-once guarantees and strict ISO20022 schema compliance.&lt;/p&gt;
&lt;p&gt;The result is less a single Kafka story and more three distinct engineering contexts that happen to share the same underlying technology. What connects them is a consistent focus on data integrity and availability in an industry where those properties are non-negotiable.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Goldman Sachs is a global investment banking and financial services firm operating across investment banking, asset management, consumer banking, and transaction banking. The scale and latency requirements of financial markets mean the firm has long invested in real-time data infrastructure.&lt;/p&gt;
&lt;p&gt;Goldman Sachs’s public Kafka work spans at least eight years of documented activity. Anton Gorshkov, Managing Director at Goldman Sachs Asset Management (GSAM), described the firm’s on-premises Kafka architecture at QCon New York as early as 2017. In the same year, the Global Investment Research (GIR) division began planning to refactor its on-premises stack, a process that ultimately culminated in a migration to Amazon MSK. The Transaction Banking (TxB) division separately built a payments platform on Amazon MSK and has presented on its monitoring and resiliency testing approach at Kafka Summit Europe 2021.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DateEvent&lt;/strong&gt;2017Anton Gorshkov presents GSAM Core Platform Kafka architecture at QCon New York and Kafka Summit2018GIR begins refactoring its on-premises technology stack, starting the path toward AWS migration2021Sheikh Araf and Ameya Panse present TxB Kafka monitoring and resiliency approach at Kafka Summit Europe 20212023AWS blog post published detailing GIR’s completed migration from on-premises Kafka to Amazon MSK using MirrorMaker 2.0&lt;/p&gt;
&lt;h2 id=&quot;goldman-sachss-kafka-use-cases&quot;&gt;Goldman Sachs’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;gsam-core-front-office-platform-financial-transaction-streaming&quot;&gt;GSAM Core Front Office Platform: financial transaction streaming&lt;/h3&gt;
&lt;p&gt;The Core Platform team within Goldman Sachs Asset Management uses Kafka as a pub-sub messaging platform for financial transaction events. Downstream consumers receive messages via three pathways: a direct sink to an in-memory RDBMS used for operational troubleshooting, a Spark Streaming job that processes events and feeds a second in-memory RDBMS queried via REST and Vert.x APIs, and a batch ETL job that persists the full event stream to a data lake for audit and governance purposes. Every message carries a globally unique identifier assigned by the upstream service, which makes idempotent replay practical in outage recovery scenarios.&lt;/p&gt;
&lt;h3 id=&quot;global-investment-research-publication-workflow-orchestration&quot;&gt;Global Investment Research: publication workflow orchestration&lt;/h3&gt;
&lt;p&gt;The GIR division uses Kafka as the backbone for orchestrating research publication workflows. When a research document is ready for distribution, Kafka coordinates the handoff between approximately 12 microservices running across 30 instances, routing publications to the correct downstream channels: the client portal, the API, and mobile notifications.&lt;/p&gt;
&lt;h3 id=&quot;transaction-banking-instant-payments-processing&quot;&gt;Transaction Banking: instant payments processing&lt;/h3&gt;
&lt;p&gt;TxB uses Kafka as the inter-process messaging layer between the Payment Gateway and other TxB microservices. The division chose Kafka for this use case in part because Kafka’s native producer idempotency and schema registry integration map well to the requirements of instant payments: exactly-once processing guarantees and strict message format validation. In internal lab testing, Kafka outperformed traditional queuing technologies by approximately 175% for this workload.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;Public scale figures are available for the GSAM Core Front Office Platform. As of 2017, the cluster handled approximately 1.5 TB per week of traffic, with peak production rates reaching around 1,500 messages per second.&lt;/p&gt;
&lt;p&gt;For GIR, the cluster served approximately 12 microservices across 30 instances, though no message rate or throughput figures have been published.&lt;/p&gt;
&lt;p&gt;No public scale figures are available for the TxB cluster.&lt;/p&gt;
&lt;h2 id=&quot;goldman-sachss-kafka-architecture&quot;&gt;Goldman Sachs’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;gsam-core-front-office-platform&quot;&gt;GSAM Core Front Office Platform&lt;/h3&gt;
&lt;p&gt;The GSAM platform runs on an on-premises virtualised cluster spanning multiple data centres within the New York City metro area. The team’s design principle is to treat multiple co-located data centres as a single redundant logical data centre rather than as primary and backup sites. With inter-site network latency of approximately 4ms between New York City and New Jersey facilities, synchronous replication between sites is feasible and is the approach taken.&lt;/p&gt;
&lt;p&gt;A minimum of three in-sync replicas is required at all times. There is no primary/backup distinction and no failover event to manage: all replicas are considered equivalent.&lt;/p&gt;
&lt;p&gt;The platform uses the Kafka Connect API to attach downstream consumers to the cluster. Those consumers fall into three categories: a direct RDBMS sink for operational visibility, a Spark Streaming job that feeds a real-time query layer, and a batch ETL sink to a data lake.&lt;/p&gt;
&lt;p&gt;Data protection is layered across four mechanisms. Starting from the most granular: synchronous disk-level replication via EMC Symmetrix Remote Data Facility (SRDF), asynchronous replication between sites, nightly batch replication, and tape backup. This arrangement is designed to cover failure scenarios ranging from a single VM loss (estimated to occur 1-5 times per year) to a full data centre outage (modelled as a once-in-20-years event).&lt;/p&gt;
&lt;h3 id=&quot;global-investment-research&quot;&gt;Global Investment Research&lt;/h3&gt;
&lt;p&gt;GIR initially ran an on-premises Kafka cluster and migrated it to Amazon MSK beginning in 2018 and completing the cutover in the 2021-2023 period. Connectivity between the on-premises network and the MSK cluster in AWS uses AWS PrivateLink with Network Load Balancers, routed through a GS Transit Account VPC. Services use the Spring Kafka client library.&lt;/p&gt;
&lt;p&gt;Avro is used for message serialisation, with a Schema Registry managing schema evolution. The Schema Registry itself was migrated to AWS as part of the same project, with the &lt;code&gt;_schemas&lt;/code&gt; topic replicated via MirrorMaker 2 before the cluster cutover.&lt;/p&gt;
&lt;h3 id=&quot;transaction-banking&quot;&gt;Transaction Banking&lt;/h3&gt;
&lt;p&gt;TxB runs its Payment Gateway on Amazon ECS with AWS Fargate as the compute layer. Amazon MSK handles all inter-process communication between the Payment Gateway and downstream TxB microservices. Schema compliance is enforced via a Schema Registry configured to validate all messages against ISO20022 specifications.&lt;/p&gt;
&lt;h4 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h4&gt;
&lt;p&gt;GSAM uses Apache Spark Streaming for stateful processing of events downstream of Kafka, feeding results to in-memory RDBMS instances that serve end-user-facing APIs.&lt;/p&gt;
&lt;h4 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h4&gt;
&lt;p&gt;GSAM uses the Kafka Connect API to connect message sinks to its on-premises cluster. Documented sinks include the in-memory RDBMS and the batch ETL data lake sink. Specific connector implementations have not been published.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;symmetric-multi-datacenter-replication-without-failover&quot;&gt;Symmetric multi-datacenter replication without failover&lt;/h3&gt;
&lt;p&gt;The GSAM platform avoids the operational complexity of primary/backup data centre topologies by treating the NYC metro area as a single logical deployment zone. With synchronous replication and a minimum of three in-sync replicas spread across facilities, there is no failover scenario: if a broker or data centre becomes unavailable, the remaining in-sync replicas continue serving reads and writes without any reconfiguration. The team documented a range of actual failure frequencies: single VM failures happen 1-5 times per year with no impact; simultaneous loss of two VM hosts occurs approximately once a year with processing halted for some topics; a three-host or data centre-level failure is rare enough to be modelled as a once-in-several-years event.&lt;/p&gt;
&lt;h3 id=&quot;belt-and-suspenders-data-protection&quot;&gt;Belt-and-suspenders data protection&lt;/h3&gt;
&lt;p&gt;Beyond Kafka’s built-in replication, the GSAM platform layers three additional data protection mechanisms: asynchronous replication, nightly batch replication, and tape backup. The most granular layer is synchronous disk-level replication via EMC SRDF, which mirrors data between storage arrays in different facilities. The team’s stated principle is that failure will always occur, and each protection layer is sized against a specific failure probability.&lt;/p&gt;
&lt;h3 id=&quot;globally-unique-identifiers-for-idempotent-replay&quot;&gt;Globally unique identifiers for idempotent replay&lt;/h3&gt;
&lt;p&gt;Every message produced to the GSAM cluster is tagged with a globally unique identifier by the upstream service before it enters Kafka. If an outage requires messages to be resent, the identifier allows consumers to detect and discard duplicates without additional coordination or changes to consumer logic.&lt;/p&gt;
&lt;h3 id=&quot;atomic-cutover-migration-with-mirrormaker-2&quot;&gt;Atomic cutover migration with MirrorMaker 2&lt;/h3&gt;
&lt;p&gt;GIR evaluated two migration approaches for its move to Amazon MSK: an atomic cutover with a planned downtime window, and an incremental hybrid migration that would keep both clusters running in parallel. The team chose the atomic approach to avoid rewriting the Spring Kafka library configuration across all 12 services. The migration sequence was: replicate the Avro Schema Registry using a unidirectional MirrorMaker 2 stream, replicate all topics using a custom MM2 replication policy that stripped the default topic-name prefixes, stop all services, reconfigure DNS endpoints from on-premises brokers to MSK brokers, restart services, and validate end-to-end. Total downtime was approximately 2 hours within a 7-hour migration window.&lt;/p&gt;
&lt;h3 id=&quot;iso20022-schema-validation-with-fail-fast-enforcement&quot;&gt;ISO20022 schema validation with fail-fast enforcement&lt;/h3&gt;
&lt;p&gt;TxB enforces ISO20022 message format compliance at the producer and consumer level via Schema Registry integration. If a producer or consumer presents a message that does not conform to the registered schema, it is rejected before any further processing occurs. The team chose this fail-fast behaviour deliberately: in a payments context, processing a malformed payment message would have a worse outcome than rejecting it at the boundary.&lt;/p&gt;
&lt;h3 id=&quot;kafka-producer-idempotency-for-exactly-once-payments&quot;&gt;Kafka producer idempotency for exactly-once payments&lt;/h3&gt;
&lt;p&gt;TxB uses Kafka’s native producer idempotency feature to achieve exactly-once delivery semantics for payment messages. The team notes that traditional queuing technologies do not provide native producer idempotency, making Kafka a better fit for this specific requirement without additional application-level deduplication logic.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;gsam-monitoring&quot;&gt;GSAM monitoring&lt;/h3&gt;
&lt;p&gt;The GSAM platform exposes a REST service that provides cluster insights: topic metadata, consumer lag, and in-sync replica counts. Metrics are collected at three levels: application, JVM, and infrastructure. All metrics feed into a time-series database with centralised alerting.&lt;/p&gt;
&lt;h3 id=&quot;txb-monitoring-and-resiliency-testing&quot;&gt;TxB monitoring and resiliency testing&lt;/h3&gt;
&lt;p&gt;TxB monitors its Kafka clusters using DataDog dashboards. A JMX agent sidecar collects metrics from producers and consumers, covering error rates, connection rates, latencies, and consumer lag, giving the team a live view across the entire service footprint.&lt;/p&gt;
&lt;p&gt;Cluster health is additionally tracked by a custom heartbeat application that generates alerts in DataDog when it detects degraded cluster state.&lt;/p&gt;
&lt;p&gt;Beyond monitoring, TxB runs regular game days where the team simulates various failure scenarios across the full client infrastructure to validate recovery behaviour and improve availability characteristics before those failures occur in production.&lt;/p&gt;
&lt;h3 id=&quot;deployment-models&quot;&gt;Deployment models&lt;/h3&gt;
&lt;p&gt;GSAM Core Platform runs self-managed Kafka on virtualised on-premises infrastructure. GIR migrated to Amazon MSK after 2018. TxB runs on Amazon MSK with compute on Amazon ECS and AWS Fargate.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;mirrormaker-2-flush-timeout-too-short-during-gir-migration&quot;&gt;MirrorMaker 2 flush timeout too short during GIR migration&lt;/h3&gt;
&lt;p&gt;During topic replication in the GIR migration, the default MirrorMaker 2 flush timeout of 5 seconds caused failures. The team increased it to 30 seconds to accommodate the volume of data being replicated.&lt;/p&gt;
&lt;h3 id=&quot;message-size-limits-exceeded-in-transit&quot;&gt;Message size limits exceeded in transit&lt;/h3&gt;
&lt;p&gt;Default settings for &lt;code&gt;max.request.size&lt;/code&gt; and &lt;code&gt;max.message.bytes&lt;/code&gt; were too small for some GIR messages during replication. Both parameters were increased in the MSK and MirrorMaker 2 configuration before the full cutover.&lt;/p&gt;
&lt;h3 id=&quot;network-load-balancer-idle-timeout-shorter-than-kafka-client-timeout&quot;&gt;Network Load Balancer idle timeout shorter than Kafka client timeout&lt;/h3&gt;
&lt;p&gt;AWS Network Load Balancers have a default idle connection timeout of 350 seconds. The Kafka client default for &lt;code&gt;connections.max.idle.ms&lt;/code&gt; is 540 seconds, meaning connections would be silently dropped by the NLB before the Kafka client detected and re-established them. GIR resolved this by setting &lt;code&gt;connections.max.idle.ms&lt;/code&gt; to a value below 350 seconds on all client services.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (on-premises, virtualised)&lt;/td&gt;
&lt;td&gt;GSAM Core Front Office Platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Amazon MSK&lt;/td&gt;
&lt;td&gt;GIR (post-migration) and TxB instant payments platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Avro + Schema Registry&lt;/td&gt;
&lt;td&gt;Message serialisation and schema governance in GIR and TxB; ISO20022 validation in TxB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Spark Streaming&lt;/td&gt;
&lt;td&gt;Stateful processing downstream of Kafka in GSAM; feeds in-memory RDBMS query layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;Message sink connectors in GSAM Core Platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration tooling&lt;/td&gt;
&lt;td&gt;Apache Kafka MirrorMaker 2.0&lt;/td&gt;
&lt;td&gt;Topic and schema registry replication during GIR on-prem to MSK migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;Amazon ECS + AWS Fargate&lt;/td&gt;
&lt;td&gt;Payment Gateway container compute in TxB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Networking&lt;/td&gt;
&lt;td&gt;AWS PrivateLink + Network Load Balancers&lt;/td&gt;
&lt;td&gt;Secure connectivity between on-premises network and MSK during GIR migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client library&lt;/td&gt;
&lt;td&gt;Spring Kafka&lt;/td&gt;
&lt;td&gt;Used across GIR microservices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;DataDog&lt;/td&gt;
&lt;td&gt;Cluster monitoring dashboards and alerting in TxB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics collection&lt;/td&gt;
&lt;td&gt;JMX agent sidecar&lt;/td&gt;
&lt;td&gt;Producer and consumer metrics collection in TxB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage replication&lt;/td&gt;
&lt;td&gt;EMC Symmetrix Remote Data Facility (SRDF)&lt;/td&gt;
&lt;td&gt;Synchronous disk-level replication between GSAM on-premises sites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;In-memory RDBMS&lt;/td&gt;
&lt;td&gt;Real-time and troubleshooting query layer downstream of Kafka in GSAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API layer&lt;/td&gt;
&lt;td&gt;Vert.x / REST APIs&lt;/td&gt;
&lt;td&gt;Application API layer serving data from in-memory RDBMS in GSAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Anton Gorshkov&lt;/td&gt;
&lt;td&gt;Managing Director, GSAM Core Platform&lt;/td&gt;
&lt;td&gt;Presented GSAM Kafka architecture and resilience design at QCon New York 2017 and Kafka Summit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sheikh Araf&lt;/td&gt;
&lt;td&gt;Associate, Transaction Banking&lt;/td&gt;
&lt;td&gt;Presented TxB Kafka monitoring and resiliency testing at Kafka Summit Europe 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ameya Panse&lt;/td&gt;
&lt;td&gt;Associate, Transaction Banking&lt;/td&gt;
&lt;td&gt;Presented TxB Kafka monitoring and resiliency testing at Kafka Summit Europe 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zachary Whitford&lt;/td&gt;
&lt;td&gt;Associate, Global Investment Research&lt;/td&gt;
&lt;td&gt;Co-authored AWS blog post on GIR on-premises to MSK migration (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Richa Prajapati&lt;/td&gt;
&lt;td&gt;Vice President, Global Investment Research&lt;/td&gt;
&lt;td&gt;Co-authored AWS blog post on GIR on-premises to MSK migration (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aldo Piddiu&lt;/td&gt;
&lt;td&gt;Vice President, Global Investment Research&lt;/td&gt;
&lt;td&gt;Co-authored AWS blog post on GIR on-premises to MSK migration (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model your failure probability explicitly before designing replication.&lt;/strong&gt; The GSAM team published specific failure rates for single-VM, dual-VM, and data centre-level failures. That grounding in actual observed frequencies is what justified the layered redundancy approach rather than over- or under-engineering it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat multi-datacenter latency as a design input, not a constraint.&lt;/strong&gt; The GSAM decision to use synchronous replication across NYC metro sites is only viable because the inter-site latency is approximately 4ms. If you are evaluating multi-site synchronous replication, measure your actual latency before committing to the topology.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose your migration strategy based on what you are willing to rewrite.&lt;/strong&gt; GIR picked atomic cutover specifically to avoid modifying Spring Kafka configuration across 12 services. If your codebase can accommodate a gradual migration, the incremental approach gives you more rollback options. If it cannot, atomic cutover with thorough pre-migration replication may be simpler.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network Load Balancer idle timeouts will silently drop Kafka connections.&lt;/strong&gt; If you are running Kafka clients behind AWS NLBs, set &lt;code&gt;connections.max.idle.ms&lt;/code&gt; below the NLB idle timeout rather than relying on the Kafka default of 540 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema enforcement at the boundary is a valid trade-off in high-stakes pipelines.&lt;/strong&gt; TxB’s fail-fast approach rejects non-compliant payment messages before any processing occurs. For most event streaming workloads this would be too strict, but in payments, a rejected message is recoverable; a processed malformed payment is not.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/articles/resilient-kafka-goldman-sachs/&quot;&gt;When Streams Fail: Implementing a Resilient Apache Kafka Cluster at Goldman Sachs&lt;/a&gt; — Anton Gorshkov, InfoQ / QCon New York 2017&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/presentations/streaming-kafka-spark/&quot;&gt;When Streams Fail: Kafka Off the Shore&lt;/a&gt; — Anton Gorshkov, InfoQ / QCon New York 2017 (video presentation)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/big-data/how-goldman-sachs-migrated-from-their-on-premises-apache-kafka-cluster-to-amazon-msk/&quot;&gt;How Goldman Sachs migrated from their on-premises Apache Kafka cluster to Amazon MSK&lt;/a&gt; — Zachary Whitford, Richa Prajapati, Aldo Piddiu, AWS Big Data Blog, March 2023&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.confluent.io/resources/presentation/monitoring-and-resiliency-testing-our-apache-kafka-clusters-at-goldman-sachs/&quot;&gt;Monitoring and Resiliency Testing our Apache Kafka Clusters at Goldman Sachs&lt;/a&gt; — Sheikh Araf and Ameya Panse, Kafka Summit Europe 2021&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.gs.com/blog/posts/txb-instant-payments&quot;&gt;How Transaction Banking Implemented an Instant Payment Architecture for High-Throughput, Low-Latency and 24x7 Availability&lt;/a&gt; — Goldman Sachs Developer Blog&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are managing Kafka clusters across multiple teams or deployment environments, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a single view of broker health, consumer lag, schema registry state, and topic activity. You can connect it to any Kafka cluster in minutes and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Grab uses Apache Kafka in production</title><link>https://factorhouse.io/articles/grab-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/grab-kafka-architecture/</guid><description>A deep-dive into Grab&apos;s Kafka architecture — how the Coban team built a terabyte-per-hour streaming platform serving 300 billion events a week across GrabFood, GrabPay, mobility, and more.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Grab’s engineering team built one of Southeast Asia’s most sophisticated real-time data platforms on top of &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt;, and then spent five years publishing exactly how they did it. The Coban team’s body of work is unusually candid: cross-AZ traffic costs that consumed half their Kafka budget, broker crashes that required manual intervention, partition rebalancing that took six hours and caused prolonged latency spikes. What makes Grab’s story worth studying is not just the scale, but the systematic way they worked through each operational constraint and wrote it up.&lt;/p&gt;
&lt;p&gt;The platform processes more than 300 billion events per week at terabytes of ingress per hour. As of late 2023, the Coban control plane managed over 5,000 data streaming resources across every Grab vertical.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company Overview&lt;/h2&gt;
&lt;p&gt;Grab is Southeast Asia’s leading superapp, operating across mobility, food delivery, package delivery, payments, and financial services in eight countries. The company serves hundreds of millions of consumers across more than 400 cities.&lt;/p&gt;
&lt;p&gt;Kafka sits at the centre of Grab’s event-driven architecture. Every time a user books a ride, a series of events ripples through booking state machines, driver notification systems, rewards computation, personalisation pipelines, and analytics dashboards. The Coban team, whose name comes from a waterfall in Indonesia, was formed to build and operate the streaming infrastructure underneath all of it. Their mandate: provide a NoOps, managed platform for seamless, secure access to event streams in real time, for every team at Grab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2020-01&lt;/td&gt;
&lt;td&gt;“Plumbing at Scale” published; Go-based Stream Processing Framework serving 300B+ events/week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020-10&lt;/td&gt;
&lt;td&gt;VPA with fixed pod count reduces Kafka consumer resource waste by 45%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Kafka Connect initiative launched; Debezium CDC and MirrorMaker2 productionised&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021-H2&lt;/td&gt;
&lt;td&gt;Company-wide chaos engineering validates Kafka platform resilience and DR failover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-02&lt;/td&gt;
&lt;td&gt;Cross-VPC Kafka exposure via AWS VPC Endpoint Service published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-04&lt;/td&gt;
&lt;td&gt;Kafka Connect blog: CDC, message mirroring, Azure Event Hubs connector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-12&lt;/td&gt;
&lt;td&gt;Zero-trust mTLS + OPA + Strimzi security model deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-07&lt;/td&gt;
&lt;td&gt;Closest-replica fetching rolled out; consumer cross-AZ cost reduced to near zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-11&lt;/td&gt;
&lt;td&gt;Coban control plane managing 5,000+ streaming resources; monthly active users quadrupled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023-12&lt;/td&gt;
&lt;td&gt;AWS NTH + EBS storage + Load Balancer Controller eliminate manual broker intervention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-09&lt;/td&gt;
&lt;td&gt;AutoMQ adopted; partition migration time: 6 hours to seconds; 3x throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-10&lt;/td&gt;
&lt;td&gt;ML predictive autoscaler for Flink: more than 35% CPU cost reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-11&lt;/td&gt;
&lt;td&gt;Real-time data quality monitoring deployed across 100+ Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;grabs-kafka-use-cases&quot;&gt;Grab’s Kafka Use Cases&lt;/h2&gt;
&lt;p&gt;Kafka at Grab is not a single-purpose bus. The Coban platform serves transactional and analytical use cases simultaneously, across every vertical in the company.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time event sourcing&lt;/strong&gt; is the foundational use case. Every booking, payment, food order, and driver status change produces events that fan out to multiple downstream consumers. Booking state machines, reward point computation, feed generation, and personalisation all run asynchronously from a centralised Kafka event log. Services apply changes from this log to their own state independently, without blocking each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Change Data Capture&lt;/strong&gt; eliminated a dual-write problem that was widespread across Grab’s MySQL-heavy backend. Services previously had to write to both MySQL and Kafka atomically, which required two-phase commit and created consistency risks. With Debezium connectors running on Kafka Connect, services write only to MySQL, and Coban’s CDC pipeline captures binlog changes and publishes them to Kafka automatically, including before-and-after row snapshots. GrabFood’s Elasticsearch index synchronisation was one of the most significant early adopters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disaster recovery and cross-cluster migration&lt;/strong&gt; are handled through MirrorMaker2 deployed on Kafka Connect. Critical services can fail over across AWS regions with zero message loss and seamless offset resumption. Coban validated this during a company-wide chaos engineering campaign in 2021. MM2 has also been used to migrate streams from self-managed Kafka clusters into the Coban platform with zero producer or consumer downtime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time analytics pipelines&lt;/strong&gt; connect Kafka to Apache Pinot. Partner Gateway API metrics flow from Kafka producers through Flink (for serialisation and transformation) into Pinot topics, where they power sub-second time-series dashboards and anomaly detection for partners monitoring their API integrations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data lake ingestion&lt;/strong&gt; is one of Coban’s core responsibilities. The platform is the entry point to Grab’s data lake, ingesting events from all services for storage and batch or streaming analysis downstream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stream processing pipelines&lt;/strong&gt; for ML features and business intelligence run as either Apache Flink jobs or Go-based Stream Processing Framework (SPF) pipelines on Kubernetes. Time-windowed aggregations, stream joins, filtering, and mapping are all standard operations. For example, personalising the Grab app landing page requires counting a user’s interactions with widget elements across a recent time window, pre-aggregated rather than computed on demand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time data quality monitoring&lt;/strong&gt; was deployed in 2025. FlinkSQL jobs consume production Kafka topics, apply data contract rules (both syntactic and semantic), and halt propagation of invalid data before it cascades to downstream consumers. An LLM analyses Kafka stream schemas and anonymised sample data to recommend semantic validation rules, reducing the manual effort of defining per-field rules across hundreds of topics. As of late 2025, the system actively monitors more than 100 critical Kafka topics.&lt;/p&gt;
&lt;h2 id=&quot;scale&quot;&gt;Scale&lt;/h2&gt;
&lt;p&gt;Grab’s streaming platform operates at a scale that makes conventional approaches expensive and brittle.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Events processed per week&lt;/td&gt;
&lt;td&gt;300 billion+ (as of 2020; platform has grown since)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data ingress rate&lt;/td&gt;
&lt;td&gt;Terabytes per hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming resources managed by Coban&lt;/td&gt;
&lt;td&gt;5,000+ (topics, Flink pipelines, CDC pipelines, Kafka Connect connectors, Zeppelin notebooks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka topics monitored for data quality&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-AZ traffic as share of Kafka cost (before 2023)&lt;/td&gt;
&lt;td&gt;~50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka consumer resource reduction after VPA adoption&lt;/td&gt;
&lt;td&gt;~45%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heimdall API uptime (H1 2023)&lt;/td&gt;
&lt;td&gt;Above 99.95%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End-to-end workflow success rate (H1 2023)&lt;/td&gt;
&lt;td&gt;Above 90%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;architecture&quot;&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure layer.&lt;/strong&gt; Grab’s platform is hosted on AWS in a single region spanning three Availability Zones. All Kafka brokers and clients are distributed across these three AZs for high availability. Each Kafka partition has three replicas, distributed rack-aware, with one replica per AZ.&lt;/p&gt;
&lt;p&gt;Kafka runs on Amazon EKS using the Strimzi operator. Each production Kafka cluster runs on a dedicated EKS cluster, with one Kafka broker per dedicated worker node, enforced via Kubernetes taints and tolerations. Storage uses AWS EBS gp3 volumes dynamically provisioned via the EBS CSI driver. This replaced an earlier design using NVMe instance store volumes, which could not survive worker node replacement without manual intervention.&lt;/p&gt;
&lt;p&gt;Kafka clusters are accessible across VPC boundaries via AWS Network Load Balancers, with each broker advertised by a private zonal DNS name and a distinct TCP port. This enables deterministic per-broker connections required by Kafka producers and consumers. For cross-account access (for example, GrabKios in its own AWS account), a VPC Endpoint Service is used in place of VPC peering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Control plane.&lt;/strong&gt; The Coban control plane has three tiers:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coban UI&lt;/td&gt;
&lt;td&gt;React SPA for self-service resource creation and monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heimdall&lt;/td&gt;
&lt;td&gt;Go backend; exposes CRUD APIs with ABAC; converts UI actions to Terraform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Khone&lt;/td&gt;
&lt;td&gt;Git repository; stores Terraform code and metadata; CI provisioner via Atlantis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;All streaming resources are declared as Terraform code under the hood. Heimdall abstracts this from users who prefer a UI or REST API, and Khone provides the Git-based audit trail, peer review workflow, and CI pipeline. The Coban UI shows real-time metrics for Kafka clusters and topic byte rates, and integrates with Grab’s monitoring stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stream processing layer.&lt;/strong&gt; Two frameworks handle stream processing at Grab. Apache Flink is used for stateful stream processing, including the data quality monitoring system, Partner Gateway analytics, and the ML feature pipelines. The Go-based Stream Processing Framework (SPF) runs as Kafka consumer pods on Kubernetes and handles hundreds of simpler pipelines with filtering, mapping, aggregation, and windowing operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AutoMQ adoption (2025).&lt;/strong&gt; For a subset of clusters requiring high elasticity, Coban adopted AutoMQ, a Kafka-compatible broker with a shared EBS WAL and S3 storage architecture. With AutoMQ, partition reassignment no longer requires moving data between brokers, reducing migration time from six hours to seconds and delivering a 3x throughput improvement.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques&quot;&gt;Special Techniques&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Rack-aware closest-replica fetching.&lt;/strong&gt; By default, Kafka consumers fetch from partition leaders, which reside in a different AZ 67% of the time. This generated cross-AZ traffic that represented approximately 50% of total Kafka platform cost. Coban upgraded their legacy Kafka clusters to version 3.1 and configured &lt;code&gt;replica.selector.class=RackAwareReplicaSelector&lt;/code&gt; on brokers, with &lt;code&gt;broker.rack&lt;/code&gt; set to the AZ ID. On the consumer side, the internal Golang Kafka SDK was updated so that teams can enable closest-replica fetching by exporting a single environment variable, with the SDK dynamically setting &lt;code&gt;client.rack&lt;/code&gt; from EC2 instance metadata at startup. The same logic was applied to Flink pipelines and Kafka Connect connectors. After rollout, consumer cross-AZ traffic cost dropped to near zero. Note: up to 500ms of additional end-to-end latency can appear for non-latency-sensitive flows due to replication lag, so latency-sensitive pipelines continue to fetch from partition leaders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protobuf SerDes with Confluent Schema Registry.&lt;/strong&gt; Grab standardised on Protobuf for all Kafka message serialisation and deserialisation. Confluent Schema Registry sits at the centre of the Kafka Connect ecosystem, enabling format conversions: Protobuf to Avro, Protobuf to JSON, and Protobuf to Parquet. For cross-cloud ingestion to Azure Event Hubs (which does not support Protobuf natively), Coban built an in-house converter that deserialises Protobuf bytes using a schema retrieved from the registry, traverses the message tree recursively, converts each field to a JSON node, and serialises the result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MirrorMaker2 deployed per-connector on Kafka Connect.&lt;/strong&gt; Rather than running the standard three-connector MM2 bundle, Coban deploys MirrorSourceConnector and MirrorCheckpointConnector independently on the Kafka Connect framework. This provides finer control over each connector and enables IaC management via Terraform, with offset mirroring handled separately so consumers can seamlessly resume from backup clusters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero-trust mTLS and OPA authorisation.&lt;/strong&gt; mTLS is enforced for all client-broker and broker-broker communications via Strimzi. Short-lived ephemeral certificates are issued by a HashiCorp Vault PKI engine, with a Root CA per environment signed down to cluster-level and client-level intermediate CAs. Open Policy Agent (OPA) is integrated with Kafka brokers to enforce topic-level, least-privilege authorisation: each client is whitelisted for the specific topics and permissions (produce or consume) it strictly needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ML-based predictive autoscaling for Flink.&lt;/strong&gt; A time-series forecasting model trained on Kafka source topic throughput predicts CPU demand ahead of time. A separate regression model maps the forecast to required TaskManager CPU. By scaling vertically before demand spikes rather than reacting after, the system avoids the reactive scaling spirals common with HPA. Deployed to the majority of applicable Flink pipelines, the system delivered more than 35% reduction in cloud CPU cost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLM-assisted data contract rule generation.&lt;/strong&gt; The data quality monitoring system uses an LLM to analyse Kafka stream schemas and anonymised sample data, recommending semantic test rules that would be impractical to define manually at scale across hundreds of topics. FlinkSQL auto-generates the test definitions from the approved contracts.&lt;/p&gt;
&lt;h2 id=&quot;operating-practices&quot;&gt;Operating Practices&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure-as-Code for all streaming resources.&lt;/strong&gt; Every Kafka topic, cluster, Kafka Connect connector, Flink pipeline, and CDC pipeline is declared as Terraform. Changes go through Git MRs, peer review, and CI pipeline application via Atlantis. The Coban UI and Heimdall API abstract this for users who prefer not to write Terraform directly, but the underlying system remains fully auditable and reproducible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cruise Control for partition balancing.&lt;/strong&gt; Cruise Control is deployed alongside each Kafka cluster for partition leader rebalancing. It is used during Kafka upgrades and rolling updates to maintain cluster balance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AWS Node Termination Handler for graceful broker shutdown.&lt;/strong&gt; NTH runs in Queue Processor mode, capturing ASG scale-in events, manual instance terminations, and EC2 maintenance events via SQS and EventBridge. When a node is going to be terminated, NTH cordons and drains it, which sends a SIGTERM to the Kafka pod and triggers a graceful shutdown. Strimzi’s &lt;code&gt;terminationGracePeriodSeconds=180&lt;/code&gt; gives brokers enough time to migrate all partition leaders, typically around 60 seconds for 600 partition leaders. The result is that unexpected node termination no longer requires engineer intervention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-AZ consumer load monitoring.&lt;/strong&gt; After enabling closest-replica fetching, CPU utilisation became skewed across AZs because consumers in well-provisioned AZs handled a disproportionate share of reads. Coban updated their internal Kafka SDK to expose AZ as a metric, enabling operators to proactively rebalance consumers across zones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VPA with fixed pod count for Kafka consumer pipelines.&lt;/strong&gt; SPF pipelines match pod count to the number of partitions in the source Kafka topic, fetched at runtime via the Kafka API. Vertical Pod Autoscaler handles CPU and memory allocation per pod based on historical load trends. This approach delivered approximately 45% reduction in total resource usage versus resources requested, compared to the previous HPA-based approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-service platform with high reliability targets.&lt;/strong&gt; The Heimdall API maintained greater than 99.95% uptime in H1 2023. End-to-end workflow success rate for self-service resource creation, change, and deletion held above 90% during the same period, even as monthly active users nearly quadrupled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chaos engineering for DR validation.&lt;/strong&gt; A company-wide chaos engineering campaign in 2021 validated the robustness of the Kafka platform, including MirrorMaker2-based cross-region failover. Coban’s Kafka demonstrated resilience across multiple chaos rounds.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-solutions&quot;&gt;Challenges and Solutions&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Cross-AZ network traffic consumed half the Kafka budget.&lt;/strong&gt; In Grab’s initial design, Kafka consumers fetched from partition leaders by default. With three AZs and partition leaders distributed across them, any given consumer had a 67% chance of fetching from a different AZ. At Grab’s scale, this consumer-side cross-AZ traffic dominated the bill and represented roughly 50% of total Kafka platform cost.&lt;/p&gt;
&lt;p&gt;Root cause: the default Kafka consumer configuration fetches only from partition leaders, regardless of replica proximity.&lt;/p&gt;
&lt;p&gt;Solution: Coban upgraded to Kafka 3.1, configured &lt;code&gt;RackAwareReplicaSelector&lt;/code&gt; on brokers, and updated the internal Golang SDK to expose a single environment variable that enables closest-replica fetching, setting &lt;code&gt;client.rack&lt;/code&gt; dynamically from EC2 instance metadata. Applied to all Coban-managed pipelines and Kafka Connect connectors.&lt;/p&gt;
&lt;p&gt;Outcome: consumer cross-AZ traffic cost dropped to near zero. Side effect to watch: end-to-end latency increases up to 500ms for flows that now read from replicas rather than leaders, due to replication lag. Latency-sensitive pipelines are configured to continue leader fetching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Kafka broker crashes on Kubernetes required manual engineer intervention.&lt;/strong&gt; When EKS worker nodes were unexpectedly terminated (hardware failure, AWS maintenance), the Kafka pod could not restart on the replacement node for two reasons: the NLB target groups still pointed to the dead node, and the Kubernetes PVC remained bound to the now-missing NVMe instance store PV. The cluster ran degraded (two of three brokers) until a Coban engineer manually reconfigured the target groups and deleted the zombie PVC.&lt;/p&gt;
&lt;p&gt;Root cause: NVMe instance store volumes are local to the EC2 instance and cannot be reattached to a replacement node. Static Kubernetes PV provisioning meant the PVC stayed bound to the missing volume.&lt;/p&gt;
&lt;p&gt;Solution: migrated broker storage from NVMe instance store to AWS EBS gp3 volumes, dynamically provisioned via the EBS CSI driver and managed by Strimzi. Integrated AWS Load Balancer Controller (LBC) for dynamic NLB target group mapping. Added AWS NTH in Queue Processor mode to gracefully drain nodes before termination.&lt;/p&gt;
&lt;p&gt;Outcome: unexpected node termination no longer requires engineer intervention. The cluster self-heals automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Partition rebalancing took six hours and caused prolonged latency spikes.&lt;/strong&gt; In traditional Kafka, partition reassignment moves data between broker nodes. During rebalancing events, this caused multi-hour periods of elevated latency and significant operational risk. Over-provisioning based on peak usage also meant significant resource waste during off-peak periods.&lt;/p&gt;
&lt;p&gt;Root cause: Kafka’s replication-based storage model couples partition data to specific broker nodes. Moving a partition requires copying potentially gigabytes of data between brokers.&lt;/p&gt;
&lt;p&gt;Solution: adopted AutoMQ for a subset of clusters. AutoMQ uses a shared EBS WAL and S3 storage layer, where all brokers read from shared storage. Partition reassignment no longer requires moving data between brokers.&lt;/p&gt;
&lt;p&gt;Outcome: partition migration time dropped from six hours to seconds. AutoMQ’s self-balancing mechanism periodically triggers rebalancing without the risk associated with traditional partition movement. 3x throughput improvement reported.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Reactive HPA scaling spirals for Flink pipelines.&lt;/strong&gt; HPA-triggered Flink restarts caused CPU and latency spikes. Those spikes triggered further HPA scale events, creating feedback loops that destabilised pipelines. The problem was compounded by the fact that Flink’s Kafka connector has fixed parallelism bounded by partition count, so horizontal scaling was often impossible, leaving only vertical scaling as an option.&lt;/p&gt;
&lt;p&gt;Root cause: reactive scaling based on CPU or latency thresholds can amplify instability rather than resolve it, particularly for Kafka-bound Flink jobs where restart itself causes temporary load spikes.&lt;/p&gt;
&lt;p&gt;Solution: ML-based predictive vertical autoscaler. A time-series model forecasts Kafka source topic throughput (which follows seasonal patterns). A regression model maps throughput to required CPU. TaskManager CPU is adjusted ahead of demand spikes.&lt;/p&gt;
&lt;p&gt;Outcome: deployed to the majority of applicable Flink pipelines; more than 35% reduction in cloud CPU cost; scaling spirals eliminated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Invalid data silently propagated through Kafka to downstream consumers.&lt;/strong&gt; Without effective data quality monitoring, bad data would enter Kafka topics and cascade to multiple downstream services before anyone detected the problem. Identifying the source, notifying the right team, and containing the blast radius was slow and manual.&lt;/p&gt;
&lt;p&gt;Root cause: no systematic mechanism existed for declaring and testing data contracts on Kafka streams. Monitoring tools tracked throughput and lag but not data validity.&lt;/p&gt;
&lt;p&gt;Solution: a data contract system where teams declare schemas and field-level semantic rules. FlinkSQL jobs auto-generated from these contracts consume production topics, detect violations in real time, and route errors to Grab’s observability platform (Genchi), Slack, and S3 sinks. An LLM helps generate semantic rules to reduce the manual definition burden.&lt;/p&gt;
&lt;p&gt;Outcome: the system now monitors more than 100 critical Kafka topics and can immediately identify and halt propagation of invalid data across multiple streams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Challenge: Dual-write inconsistency between MySQL and Kafka.&lt;/strong&gt; Services were writing state changes to both MySQL and a Kafka topic. Keeping these two writes atomic required two-phase commit, was non-trivial to implement correctly, and impacted service availability. Additionally, some downstream consumers needed before-and-after row snapshots that event-based producers could not easily supply.&lt;/p&gt;
&lt;p&gt;Root cause: publishing to Kafka from application code forces producers to solve distributed transaction problems that are not their core concern.&lt;/p&gt;
&lt;p&gt;Solution: Debezium CDC connectors running on Kafka Connect. Services write only to MySQL. Debezium reads MySQL binlogs and publishes changes to Kafka, including before-and-after row snapshots. DB schema changes, migrations, and outages are handled via Debezium’s built-in binlog offset management.&lt;/p&gt;
&lt;p&gt;Outcome: dual-write eliminated. GrabFood’s Elasticsearch index synchronisation was one of the most significant early adopters.&lt;/p&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Core event log and message bus for all Grab verticals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Connect&lt;/td&gt;
&lt;td&gt;Framework for CDC, message mirroring, and data sink/source connectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debezium&lt;/td&gt;
&lt;td&gt;MySQL CDC connector on Kafka Connect for row-level change capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MirrorMaker2&lt;/td&gt;
&lt;td&gt;Cross-cluster message and offset mirroring for DR and live migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;Stateful stream processing: data quality tests, analytics pipelines, ML feature computation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Zeppelin&lt;/td&gt;
&lt;td&gt;Interactive notebook interface for stream data exploration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Pinot&lt;/td&gt;
&lt;td&gt;Real-time OLAP; ingests from Kafka for Partner Gateway metrics dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strimzi&lt;/td&gt;
&lt;td&gt;CNCF Kafka on Kubernetes operator; mTLS, rolling upgrades, certificate management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes / Amazon EKS&lt;/td&gt;
&lt;td&gt;Container orchestration for Kafka brokers and consumer pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS EBS gp3&lt;/td&gt;
&lt;td&gt;Persistent storage for Kafka broker data volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS NLB + Load Balancer Controller&lt;/td&gt;
&lt;td&gt;Per-broker network routing; dynamic target group mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Node Termination Handler&lt;/td&gt;
&lt;td&gt;Graceful Kafka broker shutdown on node termination events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cruise Control&lt;/td&gt;
&lt;td&gt;Partition leader rebalancing and cluster metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zookeeper&lt;/td&gt;
&lt;td&gt;Kafka cluster coordination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HashiCorp Vault&lt;/td&gt;
&lt;td&gt;PKI engine for ephemeral mTLS certificate issuance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open Policy Agent&lt;/td&gt;
&lt;td&gt;Topic-level Kafka authorisation (least-privilege ACLs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protobuf + Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Message serialisation, schema versioning, and format conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform + Atlantis&lt;/td&gt;
&lt;td&gt;IaC provisioning for all streaming resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Golang Kafka SDK&lt;/td&gt;
&lt;td&gt;Internal SDK encapsulating mTLS, AZ rack-awareness, and VPA config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AutoMQ&lt;/td&gt;
&lt;td&gt;Kafka-compatible broker with EBS WAL + S3 shared storage for elastic clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Hudi&lt;/td&gt;
&lt;td&gt;Data lake table format; target sink for Kafka Connect pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS S3&lt;/td&gt;
&lt;td&gt;Object storage for data lake ingestion and AutoMQ storage layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon SQS + EventBridge&lt;/td&gt;
&lt;td&gt;NTH event routing for graceful broker shutdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React (Coban UI)&lt;/td&gt;
&lt;td&gt;Self-service web portal for topic, pipeline, and connector management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-contributors&quot;&gt;Key Contributors&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / Team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Karan Kamath&lt;/td&gt;
&lt;td&gt;Coban team&lt;/td&gt;
&lt;td&gt;“Plumbing at Scale” (2020); “Optimally Scaling Kafka Consumer Applications” (2020); “How Kafka Connect helps move data seamlessly” (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fabrice Harbulot&lt;/td&gt;
&lt;td&gt;Coban, Platform Engineering&lt;/td&gt;
&lt;td&gt;“Exposing a Kafka Cluster via a VPC Endpoint Service” (2022); “Zero trust with Kafka” (2022); “Zero traffic cost for Kafka consumers” (2023); “An Elegant Platform” (2023); “Kafka on Kubernetes: Reloaded for fault tolerance” (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quang Minh Tran&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Co-author: “Zero traffic cost for Kafka consumers” (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thang Le&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Co-author: “Kafka on Kubernetes: Reloaded for fault tolerance” (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minh Khoi Nguyen&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Co-author: “An Elegant Platform” (2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wenli Wan&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Co-author: “How Kafka Connect helps move data seamlessly” (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thanh Tung Dao&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Co-author: “How Kafka Connect helps move data seamlessly” (2022); “Zero trust with Kafka” (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shubham Badkur&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;“Optimally Scaling Kafka Consumer Applications” (2020)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yuanzhe Liu&lt;/td&gt;
&lt;td&gt;Coban&lt;/td&gt;
&lt;td&gt;Lead author: real-time data quality monitoring (2025)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Author&lt;/th&gt;
&lt;th&gt;Publication&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/plumbing-at-scale&quot;&gt;https://engineering.grab.com/plumbing-at-scale&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Karan Kamath&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Jan 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/optimally-scaling-kafka-consumer-applications&quot;&gt;https://engineering.grab.com/optimally-scaling-kafka-consumer-applications&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Shubham Badkur&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Oct 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/exposing-kafka-cluster&quot;&gt;https://engineering.grab.com/exposing-kafka-cluster&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fabrice Harbulot&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Feb 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/kafka-connect&quot;&gt;https://engineering.grab.com/kafka-connect&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Wenli Wan, Karan Kamath, Thanh Tung Dao&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Apr 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/zero-trust-with-kafka&quot;&gt;https://engineering.grab.com/zero-trust-with-kafka&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fabrice Harbulot, Thanh Tung Dao&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Dec 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/zero-traffic-cost&quot;&gt;https://engineering.grab.com/zero-traffic-cost&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fabrice Harbulot, Quang Minh Tran&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Jul 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/an-elegant-platform&quot;&gt;https://engineering.grab.com/an-elegant-platform&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fabrice Harbulot, Minh Khoi Nguyen&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Nov 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/kafka-on-kubernetes&quot;&gt;https://engineering.grab.com/kafka-on-kubernetes&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fabrice Harbulot, Thang Le&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Dec 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/pinot-partnergateway-tech-blog&quot;&gt;https://engineering.grab.com/pinot-partnergateway-tech-blog&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Grab Engineering&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://engineering.grab.com/ml-predictive-autoscaling-for-flink&quot;&gt;https://engineering.grab.com/ml-predictive-autoscaling-for-flink&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Grab Engineering&lt;/td&gt;
&lt;td&gt;Grab Engineering Blog&lt;/td&gt;
&lt;td&gt;Oct 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.automq.com/blog/how-grab-uses-automq-solve-kafka-challenges&quot;&gt;https://www.automq.com/blog/how-grab-uses-automq-solve-kafka-challenges&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;AutoMQ Team&lt;/td&gt;
&lt;td&gt;AutoMQ Blog&lt;/td&gt;
&lt;td&gt;Sep 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.infoq.com/news/2025/12/grab-kafka-data-quality/&quot;&gt;https://www.infoq.com/news/2025/12/grab-kafka-data-quality/&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;InfoQ Staff&lt;/td&gt;
&lt;td&gt;InfoQ&lt;/td&gt;
&lt;td&gt;Dec 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;If you operate Kafka in production and want visibility into consumer lag, broker health, topic throughput, and rebalancing events without writing your own tooling, take a look at &lt;a href=&quot;/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;. It’s built for engineering teams who need operational control without the overhead.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How JPMorgan uses Apache Kafka in production</title><link>https://factorhouse.io/articles/jpmorgan-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/jpmorgan-kafka-architecture/</guid><description>A deep-dive into JPMorgan Chase&apos;s Kafka architecture — covering multi-tenant cluster design, managed Kafka Connect, the Photon Framework, and the engineering decisions behind one of the largest financial services deployments.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JPMorgan Chase runs one of the largest disclosed &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; deployments in financial services: 102 clusters, 510 nodes, 13,000 topics, and 1.5 PB of configured storage — figures the firm’s engineers shared publicly at Kafka Summit San Francisco in 2019. By 2021, that platform was ingesting 400 billion events per day in production.&lt;/p&gt;
&lt;p&gt;The engineering problem at the centre of the deployment is coordination at firm scale: tens of thousands of applications across business lines, geographies, and regulatory boundaries, all needing reliable, low-latency event exchange without each team managing its own messaging infrastructure.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;JPMorgan Chase &amp;amp; Co. is a global financial services firm operating across investment banking, commercial banking, financial transaction processing, asset management, and retail banking. The firm employs more than 300,000 people and processes trillions of dollars in transactions annually, making data consistency, latency, and auditability first-order engineering constraints.&lt;/p&gt;
&lt;p&gt;Apache Kafka entered JPMorgan Chase’s platform as part of a broader shift toward microservices and event-driven architecture. The firm’s CTO, Andrew J. Lang, described the goal as creating “a digital nervous system” connecting disconnected and siloed systems at scale. By January 2019, the firm publicly recognised Confluent’s Kafka distribution as part of its Hall of Innovation programme, confirming the Confluent Platform as the foundation of its streaming infrastructure.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January 2019&lt;/td&gt;
&lt;td&gt;Confluent inducted into JPMorgan Chase Hall of Innovation, publicly confirming the Confluent Platform partnership. CTO Andrew J. Lang quoted in press release.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Kafka Summit San Francisco: engineers Vishnu Balusu and Ashok Kadambala present the multi-tenant managed Kafka service, disclosing 102 clusters, 510 nodes, 13,000 topics, and 1.5 PB of storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas: Ashok Kadambala and Shreesha Hebbar present the Managed Kafka Connect architecture, citing 400 billion incoming events per day in production.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;jpmorgan-chases-kafka-use-cases&quot;&gt;JPMorgan Chase’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;JPMorgan Chase’s Kafka adoption started with straightforward infrastructure concerns and expanded into a firm-wide event backbone as the platform matured.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log aggregation and metrics transport&lt;/strong&gt; were the earliest use cases. Before the platform broadened in scope, teams used Kafka to consolidate operational logs and carry metadata and monitoring signals across services — a pattern that provided enough operational value to justify the investment before more complex streaming architectures were in place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microservices event bus&lt;/strong&gt; is now the primary use case at firm scale. JPMorgan Chase operates approximately 8,000 microservices built on an internal framework called Photon. Kafka acts as the communications layer between those services, with Photon providing client-side resiliency on top of the open-source Kafka drivers: automatic consumer failover and a “phone-home” mechanism that reports runtime configurations (including batch size) to simplify production diagnosis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Near real-time stream processing and data moving pipelines&lt;/strong&gt; expanded Kafka’s role beyond service-to-service messaging. The firm uses Kafka to move data across cloud environments and datacentres in near real-time, supporting fan-in (many producers, centralised processing) and fan-out (one source, many downstream consumers) patterns. By 2021, this also encompassed data mesh architecture, with Kafka Connect serving as the data movement layer between domain-owned data stores.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tenant-level mirroring&lt;/strong&gt; extends Kafka’s reach across datacentres for teams that need their topics replicated geographically, either for disaster recovery or to serve consumers in different regions.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;The scale figures JPMorgan Chase shared at Kafka Summit 2019 give a concrete picture of what “enterprise Kafka” looks like in a regulated financial institution.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;As of&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total clusters&lt;/td&gt;
&lt;td&gt;102 (40 production)&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total nodes&lt;/td&gt;
&lt;td&gt;510&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics (total / production)&lt;/td&gt;
&lt;td&gt;13,000 / 1,300&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connected applications&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configured storage&lt;/td&gt;
&lt;td&gt;1.5 PB&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incoming events per day&lt;/td&gt;
&lt;td&gt;400 billion&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka version&lt;/td&gt;
&lt;td&gt;Apache Kafka 2.2.1 (Confluent 5.2.2)&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The ratio of total to production clusters (102 to 40) reflects the firm’s multi-environment discipline: non-production clusters for development, testing, and staging are tracked and governed under the same platform rather than being managed ad hoc by individual teams. The gap between total topics (13,000) and production topics (1,300) tells a similar story: provisioning is self-service and relatively unconstrained, but production promotion is controlled.&lt;/p&gt;
&lt;h2 id=&quot;jpmorgan-chases-kafka-architecture&quot;&gt;JPMorgan Chase’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;cluster-topology-and-multi-availability-zone-design&quot;&gt;Cluster topology and multi-availability-zone design&lt;/h3&gt;
&lt;p&gt;Each Kafka cluster runs on five nodes with a replication factor of four. This configuration tolerates the simultaneous failure of two nodes, which matters in a regulated environment where cluster availability affects downstream financial processes. Every node in a cluster runs a dedicated ZooKeeper ensemble alongside the Kafka brokers, Schema Registry instances, and monitoring agents, keeping coordination local to the cluster rather than shared across the estate.&lt;/p&gt;
&lt;p&gt;For clusters requiring higher fault tolerance, JPMorgan Chase uses a 2.5 AZ stretch cluster configuration: brokers are organised into four logical racks across two availability zones (two racks per AZ), with replication factor four and a minimum in-sync replica count of three. ZooKeeper quorum is placed in a separate region to ensure it remains available even if one AZ is lost. This setup provides continued write availability during a single AZ failure without requiring manual intervention.&lt;/p&gt;
&lt;h3 id=&quot;multi-tenancy-and-the-control-plane&quot;&gt;Multi-tenancy and the control plane&lt;/h3&gt;
&lt;p&gt;Running 100+ clusters for 400 applications across a large enterprise requires a systematic approach to isolation and governance. JPMorgan Chase addresses this through logical namespace-based multi-tenancy: each application team gets a namespace that abstracts the underlying physical cluster. Entitlements, governance policies, and quota limits are enforced at the namespace level, while the physical infrastructure is shared.&lt;/p&gt;
&lt;p&gt;A centralised, data-driven control plane sits above the clusters and provides a self-service API layer for topic provisioning, schema registration, Kafka Streams deployment, and quota management. All Kafka artifacts created by an application are maintained within that application’s namespace, making ownership explicit. The control plane also exposes a centralised data lineage view derived from the namespace metadata.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-architecture&quot;&gt;Kafka Connect architecture&lt;/h3&gt;
&lt;p&gt;JPMorgan Chase built a managed Kafka Connect service to handle near real-time data integration between Kafka and external systems. The design centres on a non-obvious deployment decision: instead of hosting Kafka Connect instances on shared provider infrastructure, the firm deploys Connect instances within each customer’s own Kubernetes namespace.&lt;/p&gt;
&lt;p&gt;This placement solves the credential problem. When a Connect instance sits on shared infrastructure, source and sink systems must expose their credentials to the service provider. Deploying Connect into the customer namespace means the connectors authenticate to source and sink systems using the customer’s own credentials, with no cross-boundary credential sharing required. Each deployment gets a unique HTTPS URL (for example, &lt;code&gt;connect1.jpmchase.net&lt;/code&gt;), along with dedicated configuration, offset, and status topics.&lt;/p&gt;
&lt;p&gt;A centralised and federated control plane manages all Connect deployments across namespaces, keeping operational visibility consistent even as the deployment boundary sits inside each team’s Kubernetes environment.&lt;/p&gt;
&lt;h3 id=&quot;multi-datacenter-replication&quot;&gt;Multi-datacenter replication&lt;/h3&gt;
&lt;p&gt;JPMorgan Chase uses MirrorMaker 2.0 for cross-datacenter topic replication. Both active-active and active-passive configurations are in use, with configurable topic patterns determining which topics are replicated and in which direction. Active-active replication supports geographically distributed consumers; active-passive configurations serve disaster recovery scenarios where a standby cluster needs to stay current.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Connection Profiles&lt;/strong&gt; abstract cluster addressing from application configuration. Rather than configuring a Kafka client with a list of broker addresses, applications reference a profile name (such as “NAD1700”). The control plane resolves that profile to the current broker addresses at runtime. When infrastructure changes — broker replacements, cluster migrations, IP reassignments — applications require no reconfiguration. The profile name is the stable handle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Health Index&lt;/strong&gt; is a metrics-based cluster availability score computed from broker and partition health signals and fed into Prometheus. When a cluster’s Health Index falls below a threshold, it is excluded from routing, and application-level resiliency controls react accordingly. This creates an automated circuit-breaker layer that sits between raw Kafka metrics and consumer behaviour without requiring each application team to implement their own health logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom Schema Registry authorisation extension&lt;/strong&gt; adds operation-level access control to the Schema Registry REST API. A REST extension intercepts incoming requests and separates read operations (GET) from write operations (POST, PUT, DELETE), enforcing role-appropriate permissions before requests reach the schema storage layer. This prevents teams from unintentionally overwriting schemas owned by other namespaces while keeping read access open for discovery.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Federated ADFS OAuth&lt;/strong&gt; integrates JPMorgan Chase’s existing Active Directory Federation Services identity provider with Kafka’s pluggable SASL/OAUTHBEARER authentication (KIP-255). Rather than managing a separate identity system for Kafka, the firm routes authentication through its existing corporate identity infrastructure, which simplifies credential lifecycle management and satisfies audit requirements around identity traceability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orchestrated broker patching&lt;/strong&gt; replaces manual rolling restarts with an automated lifecycle management system. The process sequences broker updates, validates cluster health between each step, and monitors for under-replicated partitions before proceeding. If a step introduces replication lag or partition leadership instability, patching pauses until the cluster recovers, reducing the risk of data unavailability during routine maintenance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Photon Framework Kafka client resiliency&lt;/strong&gt; addresses gaps in the open-source Kafka client libraries for consumer failover scenarios. Photon, the internal Spring-Boot microservices framework, wraps the Kafka client with automatic failover logic and a “phone-home” capability that surfaces runtime configuration values — including batch size settings that commonly affect consumer throughput — for use in production diagnosis without requiring a service restart.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;JPMorgan Chase runs Confluent Platform on-premises in a hybrid configuration. The operational model is built around the centralised control plane rather than per-cluster management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-service provisioning&lt;/strong&gt; is the default path for teams. Topic creation, schema registration, quota adjustments, and Kafka Streams deployment all go through the control plane’s self-service API, keeping infrastructure teams out of routine provisioning workflows. Governance constraints — topic size limits, throughput quotas, namespace isolation — are encoded in the control plane and enforced automatically, so teams work within boundaries without needing to understand the physical cluster topology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metrics and observability&lt;/strong&gt; rely on Prometheus for data collection. The Health Index pipeline feeds cluster-level signals into Prometheus, and the Photon Framework produces standard metrics and distributed traces across all Kafka producers and consumers. This gives platform operators a consistent observability interface across the 8,000 microservices that use Kafka, rather than requiring each team to instrument their Kafka clients independently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tenant quota enforcement&lt;/strong&gt; is per-namespace: topic retention limits and producer/consumer throughput quotas are set at the namespace level and enforced by the control plane. All Kafka artifacts are owned by the namespace that created them, making quota attribution and governance auditing straightforward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Managed Kafka Connect operations&lt;/strong&gt; are handled through a federated control plane that spans all Connect deployments across customer namespaces. Each deployment has its own scoped configuration, offset, and status topics, so an issue in one deployment’s offset management does not affect others. The federated control plane provides operators with a consistent view of all Connect deployments without requiring direct access to each customer namespace.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Kafka Connect credential problem.&lt;/strong&gt; When Kafka Connect runs on shared infrastructure, connectors that need to read from or write to source and sink systems must present credentials to a service provider outside the team’s trust boundary. For a regulated financial institution, this is unacceptable: credentials for core banking systems cannot be handed to a shared service operator. JPMorgan Chase’s resolution was to shift the deployment boundary: Connect instances run inside the customer’s Kubernetes namespace, so all authentication to source and sink systems uses credentials that remain within the customer’s control perimeter throughout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-tenant Kafka Streams application ID collisions.&lt;/strong&gt; In a shared Kafka environment, Kafka Streams applications identify themselves using an application ID, which is also used to namespace internal topics (repartition and changelog topics). If two teams in different namespaces choose the same application ID, their internal topics collide. JPMorgan Chase identified this as an operational risk in a multi-tenant deployment at their scale. The control plane’s namespace model and self-service API provide the framework for enforcing application ID uniqueness within and across namespaces, though the specific enforcement mechanism was not detailed in public presentations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Offset management across replicated clusters.&lt;/strong&gt; Active-passive replication with MirrorMaker 2.0 introduces a consumer group offset synchronisation challenge: offsets in the primary cluster do not translate directly to offsets in the replica cluster, because topic partition assignments and log segment layouts may differ. JPMorgan Chase identified this as an operational complexity at their scale, particularly for clusters used in disaster recovery scenarios where consumers may need to resume from a specific position in the replica.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operational burden at 100+ clusters.&lt;/strong&gt; Managing individual clusters becomes untenable at the scale JPMorgan Chase operates. Monitoring, patching, quota enforcement, and health assessment across 100 clusters requires automation rather than manual workflows. The Health Index, centralised control plane, and orchestrated patching pipeline were all built in response to this operational pressure rather than as upfront design decisions.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka 2.2.1 via Confluent 5.2.2&lt;/td&gt;
&lt;td&gt;Core distributed event streaming platform (version as of 2019)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution&lt;/td&gt;
&lt;td&gt;Confluent Platform (Enterprise)&lt;/td&gt;
&lt;td&gt;Managed Kafka distribution providing Schema Registry, Kafka Connect, Control Center, and enterprise security features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster coordination&lt;/td&gt;
&lt;td&gt;ZooKeeper&lt;/td&gt;
&lt;td&gt;Per-cluster coordination ensemble, co-located with each Kafka cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema management&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Schema registration and evolution, extended with a custom REST authorisation extension separating read and write access by role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;MirrorMaker 2.0&lt;/td&gt;
&lt;td&gt;Multi-datacenter topic replication in active-active and active-passive configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data integration&lt;/td&gt;
&lt;td&gt;Kafka Connect (Managed)&lt;/td&gt;
&lt;td&gt;Near real-time data pipelines deployed in customer Kubernetes namespaces for fan-in, fan-out, and multi-cloud integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container runtime&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Runtime environment for Managed Kafka Connect instances, scoped per customer namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;Metrics collection from the Health Index pipeline and cluster monitoring agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Kerberos&lt;/td&gt;
&lt;td&gt;Broker authentication for on-premises cluster environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity federation&lt;/td&gt;
&lt;td&gt;SASL/OAUTHBEARER (KIP-255) with ADFS&lt;/td&gt;
&lt;td&gt;Federated identity via Active Directory Federation Services, integrated with Kafka’s pluggable authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microservices framework&lt;/td&gt;
&lt;td&gt;Photon Framework (internal)&lt;/td&gt;
&lt;td&gt;Spring-Boot-based framework used by approximately 8,000 services; provides Kafka client resiliency, automatic failover, distributed tracing, and standardised metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Title / team&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vishnu Balusu&lt;/td&gt;
&lt;td&gt;Managing Director, Cloud Services&lt;/td&gt;
&lt;td&gt;Co-presenter at Kafka Summit SF 2019: “Secure Kafka at scale in true multi-tenant environment”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashok Kadambala&lt;/td&gt;
&lt;td&gt;Global Messaging / Streaming Engineering&lt;/td&gt;
&lt;td&gt;Co-presenter at Kafka Summit SF 2019 and Kafka Summit Americas 2021; architect of the managed Kafka and Managed Kafka Connect services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shreesha Hebbar&lt;/td&gt;
&lt;td&gt;Engineering Manager, Enterprise Messaging / Streaming&lt;/td&gt;
&lt;td&gt;Co-presenter at Kafka Summit Americas 2021: “Changing landscapes in data integration — Kafka Connect for near real-time data moving pipelines”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Andrew J. Lang&lt;/td&gt;
&lt;td&gt;Chief Technology Officer&lt;/td&gt;
&lt;td&gt;Quoted in the Confluent Hall of Innovation press release on the role of the Confluent Platform in the firm’s event streaming strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haiying Guo&lt;/td&gt;
&lt;td&gt;Tech Partner&lt;/td&gt;
&lt;td&gt;Co-author of the Next at Chase article describing the Photon Framework and its Kafka integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vidya Meyyappan&lt;/td&gt;
&lt;td&gt;Product Manager, New Banking Architecture Delivery Platform&lt;/td&gt;
&lt;td&gt;Co-author of the Next at Chase article describing the Photon Framework and its Kafka integration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Separate your deployment boundary from your management boundary.&lt;/strong&gt; JPMorgan Chase deploys Kafka Connect inside customer namespaces but manages all deployments from a centralised control plane. This keeps credentials within the team’s trust perimeter while preserving operational visibility for the platform team — a pattern worth considering any time a shared service needs access to systems that require privileged credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build indirection into client configuration early.&lt;/strong&gt; Connection Profiles — stable name references that resolve to broker addresses at runtime — mean that infrastructure changes (IP changes, broker migrations, cluster splits) have no impact on application configuration. If you hard-code broker addresses into application configs today, you will pay the cost of that decision at every infrastructure change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode governance as code in the control plane, not in process.&lt;/strong&gt; Topic size limits, throughput quotas, and namespace isolation enforced automatically by a control plane are more reliable than guidelines enforced by review. At scale, the gap between what teams are supposed to do and what they actually do widens; a control plane that makes the wrong thing impossible is more durable than one that makes the right thing easy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design your cluster health model before you need it.&lt;/strong&gt; JPMorgan Chase’s Health Index was built in response to the operational burden of managing 100+ clusters. A computed availability score that drives routing decisions is more useful than raw metrics alone, but it requires you to define what “healthy enough to route to” means for your workloads before you are in a position where that definition matters urgently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-tenant Kafka Streams requires namespace-scoped application ID enforcement.&lt;/strong&gt; Application ID collisions in a shared cluster cause internal topic conflicts that are difficult to diagnose and resolve without downtime. If you are offering Kafka Streams as a shared platform capability, enforce application ID uniqueness at the provisioning layer rather than relying on teams to coordinate independently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vishnu Balusu and Ashok Kadambala (JP Morgan Chase): &lt;a href=&quot;https://www.slideshare.net/slideshow/secure-kafka-at-scale-in-true-multitenant-environment-vishnu-balusu-ashok-kadambala-jp-morgan-chase-kafka-summit-sf-2019/179645183&quot;&gt;“Secure Kafka at scale in true multi-tenant environment”&lt;/a&gt; — Kafka Summit San Francisco 2019&lt;/li&gt;
&lt;li&gt;Ashok Kadambala and Shreesha Hebbar (JP Morgan Chase): &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-americas-2021/changing-landscapes-in-data-iintegration-kafka-connect-for-near-real-time/&quot;&gt;“Changing landscapes in data integration — Kafka Connect for near real-time data moving pipelines”&lt;/a&gt; — Kafka Summit Americas 2021&lt;/li&gt;
&lt;li&gt;Confluent: &lt;a href=&quot;https://www.confluent.io/press-release/confluent-inducted-into-jpmorgan-chase-hall-of-innovation/&quot;&gt;“Confluent inducted into JPMorgan Chase Hall of Innovation”&lt;/a&gt; — January 2019&lt;/li&gt;
&lt;li&gt;Haiying Guo and Vidya Meyyappan (JP Morgan Chase): &lt;a href=&quot;https://medium.com/next-at-chase/driving-native-cloud-adoption-at-scale-through-a-microservice-framework-a461e87bb8f2&quot;&gt;“Driving native cloud adoption at scale through a microservice framework”&lt;/a&gt; — Next at Chase, 2021&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are managing a Kafka deployment at a similar scale and want visibility into consumer lag, broker health, and topic configuration across your clusters, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a single interface for monitoring and managing Apache Kafka. You can connect it to any Kafka cluster in minutes and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How LinkedIn uses Apache Kafka in production</title><link>https://factorhouse.io/articles/linkedin-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/linkedin-kafka-architecture/</guid><description>A deep-dive into LinkedIn&apos;s Kafka architecture, covering use cases, scale, engineering decisions, and key contributors.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;LinkedIn built &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; internally in 2010 to solve the problem of moving large volumes of event data reliably between internal systems. By 2019 the company was processing more than 7 trillion messages per day across over 100 clusters, 4,000 brokers, and 7 million partitions - one of the largest Kafka deployments publicly documented. Kafka connects virtually every system at LinkedIn, from member activity tracking and real-time search indexing to database replication and stream processing at scale.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;LinkedIn is a professional networking platform with over one billion members. Its products span hiring, marketing, learning, and news, and almost all of its personalisation, recommendations, and analytics workloads are underpinned by data pipelines.&lt;/p&gt;
&lt;p&gt;Kafka was developed at LinkedIn in 2010 by Jay Kreps, Neha Narkhede, and Jun Rao. The motivation was straightforward: LinkedIn needed a unified, durable, high-throughput channel to move event data between its growing set of internal systems. It was open-sourced in June 2011 and donated to the Apache Software Foundation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LinkedIn Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;Apache Kafka developed internally at LinkedIn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 2011&lt;/td&gt;
&lt;td&gt;Kafka open-sourced and released to the Apache community&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;July 2011&lt;/td&gt;
&lt;td&gt;Kafka processing 1 billion messages per day in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;20 billion messages per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;July 2013&lt;/td&gt;
&lt;td&gt;200 billion messages per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Apache Samza open-sourced by LinkedIn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;1 trillion messages per day; peak of 4.5 million messages per second; Burrow open-sourced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Approximately 1.4 trillion messages per day; approximately 1,400 brokers; more than 2 PB per week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2017&lt;/td&gt;
&lt;td&gt;Kafka Cruise Control open-sourced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Migrated from Kafka MirrorMaker to Brooklin for all cross-cluster replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;7 trillion messages per day; Brooklin and Cruise Control Frontend open-sourced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2022&lt;/td&gt;
&lt;td&gt;Load-balanced Brooklin Mirror Maker published; Microsoft Azure migration paused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;November 2022&lt;/td&gt;
&lt;td&gt;TopicGC published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Apache Beam adopted to unify batch and streaming pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;linkedins-kafka-use-cases&quot;&gt;LinkedIn’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Kafka at LinkedIn is not limited to a single team or product area. It serves as the central data transport layer across the company.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Activity tracking&lt;/strong&gt; is Kafka’s original use case at LinkedIn. The platform captures member activity events - pageviews, search queries, and ad impressions - and delivers them to both offline batch analytics pipelines (Hadoop) and real-time online services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time search indexing&lt;/strong&gt; uses Kafka to deliver network-update events to LinkedIn’s search engine. According to the 2011 engineering post by Jay Kreps, updates become searchable within seconds of being posted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inter-service messaging&lt;/strong&gt; runs asynchronously over Kafka topics, connecting LinkedIn’s microservices without direct coupling between them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Database replication (Espresso CDC)&lt;/strong&gt; placed Kafka on a latency-sensitive, mission-critical path. LinkedIn replaced MySQL replication with a Kafka-backed CDC pipeline for Espresso, its internal NoSQL document store, requiring no-data-loss guarantees and low transfer latencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Derived-data upload to Venice&lt;/strong&gt; uses Kafka to asynchronously upload data into LinkedIn’s derived-data serving store, decoupling the write path from downstream consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stream processing with Samza&lt;/strong&gt; relies entirely on Kafka as both input and output. Apache Samza, which LinkedIn open-sourced in 2013, consumes from and produces to Kafka for all real-time processing jobs. Samza also uses log-compacted Kafka topics as durable state backup stores.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mobile event ingestion&lt;/strong&gt; arrives via a REST interface built around the LiKafka client, allowing mobile devices and non-Java services to produce events into Kafka reliably without depending on the native Java client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-cluster and cross-datacenter aggregation&lt;/strong&gt; is handled by Brooklin, which mirrors Kafka topics between clusters and datacenters to support centralised analytics and fault tolerance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log and metrics storage&lt;/strong&gt; treats Kafka as the circulatory system of LinkedIn’s infrastructure, routing log data and system metrics between internal services.&lt;/p&gt;
&lt;h2 id=&quot;scale--throughput&quot;&gt;Scale &amp;amp; throughput&lt;/h2&gt;
&lt;p&gt;LinkedIn’s Kafka footprint grew by a factor of approximately 1,200 in four years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;July 2011: 1 billion messages per day&lt;/li&gt;
&lt;li&gt;2012: 20 billion messages per day&lt;/li&gt;
&lt;li&gt;July 2013: 200 billion messages per day&lt;/li&gt;
&lt;li&gt;2015: 1 trillion messages per day, with a peak throughput of 4.5 million messages per second&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By 2019, LinkedIn had surpassed 7 trillion messages per day, spread across more than 100 clusters, 4,000 brokers, 100,000 topics, and 7 million partitions. Each message was consumed by approximately four applications on average.&lt;/p&gt;
&lt;p&gt;Data volume reached 1.34 PB per week at the 1 trillion messages per day milestone in 2015. With approximately 1,400 brokers in a later configuration, LinkedIn received over 2 PB of data per week.&lt;/p&gt;
&lt;p&gt;Brooklin, used for cross-cluster replication, was itself mirroring more than 7 trillion messages per day as of 2022.&lt;/p&gt;
&lt;p&gt;The maximum message size is capped at 1 MB.&lt;/p&gt;
&lt;h2 id=&quot;linkedins-kafka-architecture&quot;&gt;LinkedIn’s Kafka architecture&lt;/h2&gt;
&lt;p&gt;LinkedIn’s Kafka deployment is self-managed and multi-datacenter. The company operates its own data centers across US locations and Singapore.&lt;/p&gt;
&lt;h3 id=&quot;cluster-topology&quot;&gt;Cluster topology&lt;/h3&gt;
&lt;p&gt;Each datacenter runs multiple Kafka clusters, each containing an independent set of data with distinct purposes. Activity tracking, Espresso replication, and metrics are kept in separate clusters. This isolation limits the blast radius of failures and allows cluster configurations to be tuned per workload.&lt;/p&gt;
&lt;p&gt;The overall topology is tiered: producers write to a local Kafka cluster in their datacenter, data is replicated to aggregate clusters that consolidate events across datacenters, and consumers read from local copies. Data is copied only once per datacenter.&lt;/p&gt;
&lt;h3 id=&quot;cross-datacenter-replication&quot;&gt;Cross-datacenter replication&lt;/h3&gt;
&lt;p&gt;LinkedIn replaced Kafka MirrorMaker (KMM) with Brooklin Mirror Maker (BMM) in 2018. A single Brooklin cluster handles hundreds of simultaneous datastreams, whereas the KMM approach required a separate cluster for each pipeline - an arrangement that became operationally unsustainable at LinkedIn’s scale.&lt;/p&gt;
&lt;h3 id=&quot;kafka-release-branches&quot;&gt;Kafka release branches&lt;/h3&gt;
&lt;p&gt;LinkedIn maintains internal release branches of Apache Kafka, suffixed &lt;code&gt;-li&lt;/code&gt;, that are kept close to upstream. These are not a fork: LinkedIn submits patches upstream via Kafka Improvement Proposals (KIPs), either before cherry-picking them into the LinkedIn branch or shortly after committing a hotfix internally. New internal releases are cut approximately every quarter.&lt;/p&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;All data pipelines at LinkedIn are standardised on Apache Avro. The LiKafka client handles schema registration with a central Schema Registry, Avro encoding and decoding, and auditing automatically on both producer and consumer sides. Consumers receive schemas without needing to manage them explicitly.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;LinkedIn’s LiKafka client wraps the standard Kafka producer and adds schema registration, auditing, and large message support. Quota enforcement - per-producer bandwidth limits in bytes per second - is applied at the broker level to prevent any single application from saturating a cluster. Quotas can be exceeded by whitelisted users, and configuration changes take effect without a rolling broker restart.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Each Kafka message is consumed by approximately four applications on average. Offset management and consumer lag monitoring are handled through Burrow (described under Operating Kafka at scale). Consumer groups and lag are evaluated across every partition using a sliding-window model rather than static threshold comparisons.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Samza handles all real-time stream processing at LinkedIn and depends on Kafka both as the event source and as a durable state store. Samza backs up stream processor state to log-compacted Kafka topics, enabling recovery after disk failures without replaying the entire topic history.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;LinkedIn built Gobblin, a Kafka-to-Hadoop ingestion framework, to continuously copy Kafka data into HDFS for offline analytics. Gobblin replaced an earlier framework called Camus. Brooklin also serves as a streaming bridge between Kafka and external systems including Azure Event Hubs and AWS Kinesis.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques--engineering-innovations&quot;&gt;Special techniques &amp;amp; engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Time-based replica lag thresholds:&lt;/strong&gt; LinkedIn changed the Kafka replica lag calculation from a bytes-behind threshold to a time-based threshold. The bytes model caused large messages to incorrectly mark replicas as out of sync during normal operation. Switching to time prevents false positives from message size spikes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rack-aware replica placement:&lt;/strong&gt; Replicas for a partition are placed on brokers in different datacenter racks. This prevents a top-of-rack switch failure from taking all replicas for a partition offline simultaneously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TopicGC - automated unused topic garbage collection:&lt;/strong&gt; LinkedIn built an internal service called TopicGC to detect unused Kafka topics, seal them (blocking reads and writes), disable mirroring, and delete them in batches of up to three concurrent deletions. A final usage check runs before each deletion step. In one of LinkedIn’s largest data pipelines, TopicGC reduced the topic count by approximately 20% and improved produce and consume performance by at least 30%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka as Espresso CDC backbone:&lt;/strong&gt; Replacing MySQL replication with Kafka for Espresso required significantly stronger delivery guarantees than typical event streaming. LinkedIn tuned for no-data-loss delivery while maintaining the low transfer latencies Espresso’s online traffic requires.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LiKafka REST service for non-Java clients:&lt;/strong&gt; The original REST interface had no guaranteed delivery. LinkedIn redesigned it so an event is only considered consumed after successful delivery to the destination topic, enabling reliable mobile and non-Java ingestion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Load-balanced Brooklin partition assignment:&lt;/strong&gt; The default Brooklin partition assignment distributed partitions by count rather than throughput. LinkedIn migrated Brooklin’s Kafka connectors from high-level consumer APIs to manual partition assignment APIs and introduced a throughput-based assignment strategy, reducing replication latency imbalance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Audit - end-to-end message completeness verification:&lt;/strong&gt; LiKafka clients emit per-topic message counts in 10-minute windows to a dedicated audit topic. The Kafka Audit Service aggregates counts from producers, each tier of the cluster topology, and critical consumers such as Hadoop, verifying that no messages are lost or duplicated across the pipeline.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; LinkedIn runs Kafka on its own hardware across multiple datacenters. As of early 2022, a planned migration to Microsoft Azure was paused.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag monitoring with Burrow:&lt;/strong&gt; Burrow evaluates consumer group health across every partition using a sliding window of offset commits - approximately 10 commits, representing roughly 10 minutes of lag history at LinkedIn’s typical commit rate. Rather than alerting on a fixed lag threshold, Burrow assesses the direction and rate of change of consumer lag to produce a health status: good, degraded, or stopped. It is exposed via an HTTP API and was open-sourced in 2015.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster rebalancing with Cruise Control:&lt;/strong&gt; Broker failures occur at LinkedIn’s scale on a daily basis. Manual SRE intervention for partition reassignment was not sustainable. Cruise Control continuously monitors disk, network, and CPU utilisation across all brokers and automatically reassigns partitions to meet pre-defined performance goals. It also handles self-healing on broker failure. LinkedIn open-sourced Cruise Control in August 2017. The Cruise Control Frontend (CCFE), a web dashboard for managing all Kafka clusters, was open-sourced in 2019.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live availability monitoring with Kafka Monitor:&lt;/strong&gt; Kafka Monitor runs synthetic producer and consumer applications against live clusters to continuously validate ordering, delivery, and data integrity guarantees and measure end-to-end latencies. It is also used to qualify new Kafka builds before they reach production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-service topic management with Nuage:&lt;/strong&gt; LinkedIn’s Nuage portal gives teams self-service access to Kafka topic lifecycle operations, including topic creation, deletion, configuration changes, metadata browsing, and ACL management. It delegates operations to the Kafka REST service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Usage attribution with Bean Counter:&lt;/strong&gt; Bean Counter tracks megabytes sent and received per application, attributing Kafka infrastructure costs to individual teams and providing an incentive for responsible usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chaos testing with Simoorg:&lt;/strong&gt; LinkedIn integrates Simoorg, its open-source failure inducer, into Kafka release validation. Simoorg introduces low-level failures such as dropped packets, low memory, failed disk writes, and process kills to verify that new Kafka builds behave correctly under real failure conditions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quarterly release cadence:&lt;/strong&gt; LinkedIn cuts a new internal Kafka release approximately every quarter, tracking open-source trunk closely and upstreaming patches where possible.&lt;/p&gt;
&lt;h2 id=&quot;challenges--how-they-solved-them&quot;&gt;Challenges &amp;amp; how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kafka MirrorMaker data loss on upgrades and reboots&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; The original KMM design could lose messages when a cluster was upgraded or a machine rebooted, because the delivery contract did not guarantee persistence before marking a message as consumed.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; LinkedIn first redesigned the delivery contract so a message is only marked consumed after successful delivery to the destination. Later, in 2018, LinkedIn replaced all KMM clusters with Brooklin, which also eliminated the operational overhead of managing hundreds of separate pipeline-specific clusters.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Reliable cross-cluster replication at scale, with a single Brooklin cluster handling hundreds of simultaneous datastreams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ZooKeeper controller initialisation bottleneck from metadata bloat&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; Accumulation of unused Kafka topics inflated ZooKeeper response payloads. For LinkedIn’s largest cluster, the ZooKeeper response size had reached 0.75 MB, close to the 1 MB limit that would trigger availability issues.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; LinkedIn built TopicGC to automatically detect and delete unused topics in a controlled sequence, with a last-minute usage check before each deletion step.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Topic count reduced by approximately 20% in one of LinkedIn’s largest pipelines; produce and consume performance improved by at least 30%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unbalanced cluster load and partition skew after broker failures&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; Broker failures at LinkedIn’s scale occur daily. Manual partition reassignment by SREs was not a sustainable response.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; LinkedIn built Cruise Control, which continuously monitors cluster resource utilisation and automatically reassigns partitions to meet disk, network, and CPU goals.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Automated self-healing on broker failure; Cruise Control open-sourced and adopted across the industry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Uneven load distribution across Brooklin replication tasks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; Brooklin’s original partition assignment distributed partitions by count rather than by throughput, causing some tasks to process significantly more data than others.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; LinkedIn migrated to manual partition assignment APIs and implemented a throughput-based partition assignment strategy.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Reduced replication latency imbalance across Brooklin tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;False replica out-of-sync alerts caused by large messages&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Root cause:&lt;/strong&gt; The bytes-of-lag threshold used to determine replica health could be exceeded by a single large message, incorrectly marking a healthy replica as out of sync.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Solution:&lt;/strong&gt; LinkedIn switched the replica lag calculation to a time-based threshold.&lt;/p&gt;
&lt;p&gt;‍&lt;strong&gt;Outcome:&lt;/strong&gt; Fewer false positive alerts and more accurate replica health reporting.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (-li branches)&lt;/td&gt;
&lt;td&gt;LinkedIn-internal release branches kept close to upstream; patches submitted via KIPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;LinkedIn Schema Registry&lt;/td&gt;
&lt;td&gt;Central Avro schema store; integrated with LiKafka client on both producer and consumer sides&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Mandatory standard across all LinkedIn data pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client&lt;/td&gt;
&lt;td&gt;LiKafka&lt;/td&gt;
&lt;td&gt;Custom client wrapping the OSS Kafka producer and consumer; adds schema management, auditing, and large message support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Samza, Apache Beam, Apache Spark&lt;/td&gt;
&lt;td&gt;Samza for real-time; Beam adopted in 2023 for unified batch and streaming; Spark for petabyte-scale batch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-cluster replication&lt;/td&gt;
&lt;td&gt;Brooklin (Brooklin Mirror Maker)&lt;/td&gt;
&lt;td&gt;Replaced Kafka MirrorMaker in 2018; also bridges Kafka to Azure Event Hubs and AWS Kinesis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka-to-Hadoop ingestion&lt;/td&gt;
&lt;td&gt;Gobblin&lt;/td&gt;
&lt;td&gt;Replaced Camus; continuously copies Kafka data to HDFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage and serving&lt;/td&gt;
&lt;td&gt;Espresso, Venice, Hadoop/HDFS&lt;/td&gt;
&lt;td&gt;Espresso (NoSQL, CDC via Kafka), Venice (derived-data store), HDFS (offline analytics)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster rebalancing&lt;/td&gt;
&lt;td&gt;Kafka Cruise Control, Cruise Control Frontend (CCFE)&lt;/td&gt;
&lt;td&gt;Automated partition rebalancing and self-healing; CCFE provides a central web dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer lag monitoring&lt;/td&gt;
&lt;td&gt;Burrow&lt;/td&gt;
&lt;td&gt;Sliding-window lag analysis; HTTP API; open-sourced 2015&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live cluster health&lt;/td&gt;
&lt;td&gt;Kafka Monitor&lt;/td&gt;
&lt;td&gt;Synthetic produce/consume tests; used in release qualification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic lifecycle&lt;/td&gt;
&lt;td&gt;Nuage, TopicGC&lt;/td&gt;
&lt;td&gt;Nuage: self-service portal for topic management; TopicGC: automated garbage collection of unused topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage attribution&lt;/td&gt;
&lt;td&gt;Bean Counter&lt;/td&gt;
&lt;td&gt;Per-application MB tracking for cost attribution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chaos testing&lt;/td&gt;
&lt;td&gt;Simoorg&lt;/td&gt;
&lt;td&gt;Open-source failure inducer; integrated into Kafka release validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time OLAP&lt;/td&gt;
&lt;td&gt;Apache Pinot&lt;/td&gt;
&lt;td&gt;Serves partition-level metrics collected by Cruise Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;Kafka broker metadata coordination&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jay Kreps&lt;/td&gt;
&lt;td&gt;Co-creator of Kafka at LinkedIn&lt;/td&gt;
&lt;td&gt;Co-designed and built the original Kafka system; co-authored the 2011 open-source release post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neha Narkhede&lt;/td&gt;
&lt;td&gt;Co-creator of Kafka at LinkedIn&lt;/td&gt;
&lt;td&gt;Co-designed and built the original Kafka system; later co-founded Confluent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jun Rao&lt;/td&gt;
&lt;td&gt;Co-creator of Kafka at LinkedIn&lt;/td&gt;
&lt;td&gt;Co-designed and built the original Kafka system; later co-founded Confluent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kartik Paramasivam&lt;/td&gt;
&lt;td&gt;Engineering Lead, Kafka team&lt;/td&gt;
&lt;td&gt;Authored “How we’re improving and advancing Kafka at LinkedIn” (2015); led quota, consumer, and reliability work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Todd Palino&lt;/td&gt;
&lt;td&gt;Data Infrastructure SRE&lt;/td&gt;
&lt;td&gt;Built Burrow; authored “Running Kafka at scale”; presented “More data centers, more problems” at Kafka Summit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Joel Koshy&lt;/td&gt;
&lt;td&gt;Staff Engineer, Kafka team&lt;/td&gt;
&lt;td&gt;Authored “Kafka ecosystem at LinkedIn”; presented “Kafkaesque days at LinkedIn in 2015” at Kafka Summit; led Nuage development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efe Gencer&lt;/td&gt;
&lt;td&gt;Kafka team&lt;/td&gt;
&lt;td&gt;Created Cruise Control as an intern project in 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Joseph Lin&lt;/td&gt;
&lt;td&gt;Streaming Infrastructure team&lt;/td&gt;
&lt;td&gt;Co-invented TopicGC; implemented its original design (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lincong Li&lt;/td&gt;
&lt;td&gt;Streaming Infrastructure team&lt;/td&gt;
&lt;td&gt;Co-invented TopicGC; implemented its original design (2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clark Haskins&lt;/td&gt;
&lt;td&gt;Data Infrastructure SRE&lt;/td&gt;
&lt;td&gt;Burrow core developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grayson Chao&lt;/td&gt;
&lt;td&gt;Data Infrastructure SRE&lt;/td&gt;
&lt;td&gt;Burrow core developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jon Bringhurst&lt;/td&gt;
&lt;td&gt;Data Infrastructure SRE&lt;/td&gt;
&lt;td&gt;Burrow core developer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Separate clusters by workload type.&lt;/strong&gt; LinkedIn runs multiple clusters per datacenter rather than a single shared cluster. This isolates failure domains and lets each cluster be tuned for its specific performance and durability requirements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate rebalancing from day one.&lt;/strong&gt; Manual partition reassignment does not scale. LinkedIn’s experience building Cruise Control demonstrates that continuous automated rebalancing is more reliable than on-demand SRE intervention, particularly as broker failure frequency increases with fleet size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor consumer lag by behaviour, not by threshold.&lt;/strong&gt; Burrow’s sliding-window approach evaluates whether consumers are making progress relative to their recent history, rather than alerting when lag exceeds a fixed byte count. This reduces false positives and gives a more accurate picture of consumer health.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in topic lifecycle management.&lt;/strong&gt; Unused topics accumulate over time and can create real operational problems at scale. TopicGC’s results at LinkedIn - a 20% reduction in topic count and a 30% improvement in performance - demonstrate that proactive topic hygiene is worth automating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardise serialisation across all pipelines early.&lt;/strong&gt; LinkedIn’s universal adoption of Apache Avro and a central Schema Registry allowed teams to share infrastructure and tooling. Retrofitting schema standards onto an existing pipeline ecosystem is significantly harder.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources--further-reading&quot;&gt;Sources &amp;amp; further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[S1] Jay Kreps, LinkedIn Engineering Blog (2011): &lt;a href=&quot;https://www.linkedin.com/blog/member/archive/open-source-linkedin-kafka&quot;&gt;https://www.linkedin.com/blog/member/archive/open-source-linkedin-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S2] Todd Palino, “Running Kafka at scale”, LinkedIn Engineering Blog (March 2015): &lt;a href=&quot;https://engineering.linkedin.com/kafka/running-kafka-scale&quot;&gt;https://engineering.linkedin.com/kafka/running-kafka-scale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S3] Joel Koshy, “Kafka ecosystem at LinkedIn”, LinkedIn Engineering Blog (~2016): &lt;a href=&quot;https://www.linkedin.com/blog/engineering/open-source/kafka-ecosystem-at-linkedin&quot;&gt;https://www.linkedin.com/blog/engineering/open-source/kafka-ecosystem-at-linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S4] Kartik Paramasivam, “How we’re improving and advancing Kafka at LinkedIn”, LinkedIn Engineering Blog (September 2015): &lt;a href=&quot;https://engineering.linkedin.com/apache-kafka/how-we_re-improving-and-advancing-kafka-linkedin&quot;&gt;https://engineering.linkedin.com/apache-kafka/how-we_re-improving-and-advancing-kafka-linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S5] LinkedIn Engineering team, “Apache Kafka: more than 1 trillion messages”, LinkedIn Engineering Blog (2019): &lt;a href=&quot;https://www.linkedin.com/blog/engineering/open-source/apache-kafka-trillion-messages&quot;&gt;https://www.linkedin.com/blog/engineering/open-source/apache-kafka-trillion-messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S6] LinkedIn Streaming Infrastructure team, “Load-balanced Brooklin Mirror Maker”, LinkedIn Engineering Blog (April 2022): &lt;a href=&quot;https://engineering.linkedin.com/blog/2022/load-balanced-brooklin-mirror-maker--replicating-large-scale-kaf&quot;&gt;https://engineering.linkedin.com/blog/2022/load-balanced-brooklin-mirror-maker--replicating-large-scale-kaf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S7] LinkedIn Streaming team, “Brooklin: near real-time data streaming at scale”, LinkedIn Engineering Blog (August 2019): &lt;a href=&quot;https://engineering.linkedin.com/blog/2019/brooklin-open-source&quot;&gt;https://engineering.linkedin.com/blog/2019/brooklin-open-source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S8] Junaid Effendi, “LinkedIn data tech stack”, Substack (October 2024): &lt;a href=&quot;https://www.junaideffendi.com/p/linkedin-data-tech-stack&quot;&gt;https://www.junaideffendi.com/p/linkedin-data-tech-stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S9] Joseph Lin, Lincong Li, “TopicGC: how LinkedIn cleans up unused Kafka metadata”, LinkedIn Engineering Blog (November 2022): &lt;a href=&quot;https://engineering.linkedin.com/blog/2022/topicgc_how-linkedin-cleans-up-unused-metadata-for-its-kafka-clu&quot;&gt;https://engineering.linkedin.com/blog/2022/topicgc_how-linkedin-cleans-up-unused-metadata-for-its-kafka-clu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S10] Todd Palino, Clark Haskins, Grayson Chao, Jon Bringhurst, “Burrow: Kafka consumer monitoring reinvented”, LinkedIn Engineering Blog (June 2015): &lt;a href=&quot;https://engineering.linkedin.com/apache-kafka/burrow-kafka-consumer-monitoring-reinvented&quot;&gt;https://engineering.linkedin.com/apache-kafka/burrow-kafka-consumer-monitoring-reinvented&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[S11] Efe Gencer + Kafka team, “Open-sourcing Kafka Cruise Control”, LinkedIn Engineering Blog (August 2017): &lt;a href=&quot;https://engineering.linkedin.com/blog/2017/08/open-sourcing-kafka-cruise-control&quot;&gt;https://engineering.linkedin.com/blog/2017/08/open-sourcing-kafka-cruise-control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are managing a Kafka deployment of your own, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides observability, control, and governance for Kafka clusters across self-managed, MSK, and Confluent environments. You can try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How PayPal uses Apache Kafka in production</title><link>https://factorhouse.io/articles/paypal-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/paypal-kafka-architecture/</guid><description>A deep-dive into PayPal&apos;s Kafka architecture — covering use cases, scale, engineering decisions, and key contributors across a fleet handling 1.3 trillion messages per day.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;PayPal’s &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; deployment sits at a scale that most engineering teams will only read about: more than 85 clusters, over 1,500 brokers, and a peak throughput of 1.3 trillion messages per day during Retail Friday 2022 — roughly 21 million messages per second. Kafka underpins nearly every data pipeline at PayPal, from real-time fraud scoring to clickstream ingestion to database synchronisation, and the engineering challenges involved in running it reliably at that volume have shaped both internal tooling and the open-source project itself.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;PayPal is a global payments platform operating in more than 200 markets. At the time of writing, it processes hundreds of millions of transactions per year across consumer, merchant, and financial-services products including PayPal, Venmo, Braintree, and Xoom.&lt;/p&gt;
&lt;p&gt;The company adopted Kafka in 2015, starting with a handful of isolated clusters for specific pipelines. Over the following years, payment volume and product surface area grew steadily, and Kafka evolved from a collection of point-to-point pipelines into a centralised event-streaming fabric used across dozens of teams and use cases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2015: Kafka introduced as isolated clusters for specific use cases&lt;/li&gt;
&lt;li&gt;2016: Lambda architecture documented, combining Kafka with Spark Streaming for near-real-time analytics&lt;/li&gt;
&lt;li&gt;2018: 400 billion messages per day across 40+ clusters in three geographically distributed data centres&lt;/li&gt;
&lt;li&gt;2020: Approaching 1 trillion messages per day; multi-tenant fleet management formalised&lt;/li&gt;
&lt;li&gt;2021: Zero-downtime migration of 20+ clusters (1,000+ broker and ZooKeeper nodes, 60+ MirrorMaker groups) across data centres&lt;/li&gt;
&lt;li&gt;2022: 1.3 trillion messages per day at Retail Friday peak (21 million messages per second)&lt;/li&gt;
&lt;li&gt;2023: Fleet documented at 85+ clusters, 1,500+ brokers, 20,000+ topics, approximately 2,000 MirrorMaker nodes, 99.99% availability&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;paypals-kafka-use-cases&quot;&gt;PayPal’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;PayPal uses Kafka across seven primary use cases, each processing more than 100 billion messages per day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First-party user behaviour tracking&lt;/strong&gt; is one of the busiest pipelines. All clickstream and interaction events from PayPal’s web and mobile surfaces are ingested via Kafka and enriched before being distributed to downstream analytics and personalisation systems. The pipeline processes 18.8 billion messages per day in that use case alone, up from 9 billion before a reactive architecture rewrite in 2018.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application health metrics&lt;/strong&gt; are streamed through Kafka from brokers, ZooKeeper nodes, MirrorMakers, and Kafka clients themselves. A custom metrics library registers telemetry via Micrometer and forwards it to the SignalFX observability backend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Database synchronisation&lt;/strong&gt; uses Kafka as a change-data-capture transport, streaming operational database changes to downstream consumers for near-real-time replication across services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application log aggregation&lt;/strong&gt; consolidates logs from across PayPal’s service estate via Kafka, feeding log-analysis and compliance tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Risk detection and management&lt;/strong&gt; depends on Kafka to move payment events to fraud-scoring and anomaly-detection models with low latency. Spark Streaming jobs consume directly from Kafka topics to support real-time risk decisions at transaction time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Analytics and compliance pipelines&lt;/strong&gt; use Kafka as the backbone for several analytical systems, including a consumer application that ingests 30 to 35 billion daily events from Kafka and publishes them to BigQuery on Google Cloud. Before this pipeline existed, the reporting latency for the underlying data was 12 hours; after the Kafka-based rewrite, it dropped to seconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Batch processing&lt;/strong&gt; supplements the real-time pipelines. Event streams also feed macro-batch Spark jobs used for end-of-day reconciliation and reporting.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Peak daily messages (Retail Friday 2022)&lt;/td&gt;
&lt;td&gt;1.3 trillion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak message rate&lt;/td&gt;
&lt;td&gt;21 million messages per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka clusters&lt;/td&gt;
&lt;td&gt;85+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brokers&lt;/td&gt;
&lt;td&gt;1,500+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;20,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MirrorMaker nodes&lt;/td&gt;
&lt;td&gt;~2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform availability&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messages per day per primary use case&lt;/td&gt;
&lt;td&gt;100 billion+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The growth trajectory is as informative as the peak numbers. PayPal started with isolated clusters in 2015, reached 400 billion messages per day by 2018, crossed 1 trillion by 2021, and then handled a 30% increase on top of that during the 2022 Retail Friday peak. Quarter-on-quarter traffic growth of around 30% was noted by Maulin Vasavada at Kafka Summit 2020, which helps explain why PayPal’s engineering approach has consistently prioritised operational automation over manual intervention.&lt;/p&gt;
&lt;h2 id=&quot;paypals-kafka-architecture&quot;&gt;PayPal’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;PayPal runs Kafka brokers on &lt;strong&gt;bare metal&lt;/strong&gt; for I/O performance reasons. ZooKeeper and MirrorMaker instances run on virtual machines. The production Kafka fleet spans multiple geographically distributed data centres, with clusters segmented by security zone within each data centre. Cluster placement is governed by data classification requirements and business criticality rather than a single flat topology.&lt;/p&gt;
&lt;p&gt;The QA environment is hosted on &lt;strong&gt;Google Cloud Platform&lt;/strong&gt;, with brokers spread across multiple GCP availability zones. The GCP QA environment maps directly to production cluster topology, follows the same security standards, and cost 75% less than the previous on-premises QA setup while delivering 40% better performance.&lt;/p&gt;
&lt;h3 id=&quot;kafka-config-service&quot;&gt;Kafka Config Service&lt;/h3&gt;
&lt;p&gt;One of the more consequential internal tools at PayPal is the &lt;strong&gt;Kafka Config Service&lt;/strong&gt;: a highly available, stateless service that pushes bootstrap server addresses and standardised client configurations to applications. Rather than hard-coding broker IPs in application configs, each application queries the Config Service at startup.&lt;/p&gt;
&lt;p&gt;This decouples application deployments from broker topology changes. When clusters are rebalanced, scaled, or reorganised, the Config Service absorbs the change and applications continue connecting without redeployment. It also reduces the operational burden on the SRE team, since broker address updates do not require coordinating application-side config changes across hundreds of teams.&lt;/p&gt;
&lt;h3 id=&quot;security-and-access-control&quot;&gt;Security and access control&lt;/h3&gt;
&lt;p&gt;SASL-based authentication is enforced across all clusters. Applications must authenticate and declare producer or consumer intent before connecting. Each topic onboarding request generates a unique authentication token scoped to that topic. The plaintext port that existed in the earlier deployment has been removed.&lt;/p&gt;
&lt;p&gt;This replaced a configuration where applications could connect without identifying themselves. The shift to ACLs gave the Kafka team visibility into exactly which producers and consumers were using each topic, which both improved security posture and simplified capacity planning.&lt;/p&gt;
&lt;h3 id=&quot;topic-onboarding&quot;&gt;Topic onboarding&lt;/h3&gt;
&lt;p&gt;Application teams submit onboarding requests through an internal &lt;strong&gt;Onboarding Dashboard&lt;/strong&gt;. A capacity analysis tool integrated into the workflow evaluates cluster placement before the topic is provisioned. MirrorMaker groups for any cross-zone replication requirements are set up as part of the same workflow.&lt;/p&gt;
&lt;h3 id=&quot;cross-cluster-replication-with-mirrormaker&quot;&gt;Cross-cluster replication with MirrorMaker&lt;/h3&gt;
&lt;p&gt;PayPal uses approximately 2,000 MirrorMaker nodes to replicate events between clusters. Replication serves two purposes: disaster recovery (maintaining a mirror in a secondary data centre) and inter-security-zone communication (moving data from a high-classification zone to a lower one where consuming services operate).&lt;/p&gt;
&lt;p&gt;In 2021, Lei Huang and Na Yang presented the data centre migration at Kafka Summit Americas. The team migrated 20+ clusters (1,000+ broker and ZooKeeper nodes, 60+ MirrorMaker groups) across data centres with zero service outage, zero message loss, and no application-side changes required. MirrorMaker groups provided the traffic cut-over path, with traffic gradually shifting from source clusters to destination clusters through the MirrorMaker pipeline.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;The BigQuery pipeline producer uses a batch size of 250,000 bytes and a linger time of 5 seconds to optimise throughput over latency. For the user behaviour tracking pipeline, a &lt;strong&gt;Chronicle Queue&lt;/strong&gt; (memory-mapped file store) sits between the HTTP ingestion layer and the Kafka producer. When a Kafka consumer group rebalance causes the producer to become temporarily unavailable, events buffer to disk rather than being dropped. Once the rebalance completes, the Chronicle Queue drains into the Kafka producer in order.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;The BigQuery consumer application runs on 75 production machines (2-core CPU, 8 GB RAM each) and is built on &lt;strong&gt;Project Reactor&lt;/strong&gt; (reactive streams). Key configuration values: max.poll.records=1,000, fetch.min.bytes approximately 100 KB, max.partition.fetch.bytes approximately 1 MB. The application manages 300 partitions in production. GC was switched from Concurrent Mark Sweep to G1GC during benchmarking, which improved stability under high throughput. Sustained throughput of approximately 950,000 events per minute was achieved per instance.&lt;/p&gt;
&lt;p&gt;Session timeout is set to 60 seconds, with a heartbeat interval of 20 seconds.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Spark Streaming jobs consume Kafka topics directly for risk detection and metrics aggregation pipelines. The user behaviour tracking pipeline uses Akka Streams (via the Squbs reactive framework) for in-process stream processing and fan-in from multiple HTTP sources. The &lt;code&gt;MergeHub&lt;/code&gt; Akka primitive is used to dynamically merge HTTP connection streams into the downstream enrichment flow, so each new HTTP connection becomes its own sub-stream without reconfiguring the pipeline.&lt;/p&gt;
&lt;h3 id=&quot;client-library-ecosystem&quot;&gt;Client library ecosystem&lt;/h3&gt;
&lt;p&gt;PayPal maintains three internal client libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;connectivity library&lt;/strong&gt; for resilient connection to the Config Service and brokers&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;monitoring library&lt;/strong&gt; that registers Kafka client metrics via Micrometer and forwards them to SignalFX&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;security library&lt;/strong&gt; that manages SSL certificate lifecycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Supported languages are Java, Python, Go, Scala, and Node.js, as well as applications built on the internal Squbs framework.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kafka Config Service&lt;/strong&gt; is the most operationally significant internal tool. It removes the dependency between broker topology and application configuration, enabling infrastructure changes without application redeployment. At a scale of 85+ clusters, the operational leverage this provides is substantial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chronicle Queue buffering&lt;/strong&gt; addresses a specific failure mode in the user behaviour tracking pipeline. Kafka consumer group rebalances can cause brief producer unavailability. Without a durable buffer, events arriving during a rebalance are either queued in memory (at risk of loss on restart) or dropped. Chronicle Queue writes to memory-mapped files on disk, providing durability at near-memory speeds. This pattern is not standard Kafka tooling; PayPal built it to meet the data loss requirements of a pipeline processing tens of billions of events per day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smart patching plugin&lt;/strong&gt; checks under-replicated partition (URP) counts before initiating any broker patch. A patch proceeds only when the affected broker’s partitions are fully replicated. This gating condition enables parallel patching of multiple clusters concurrently, with single-broker restarts proceeding in sequence within each cluster. Before this plugin, patching at 1,500+ broker scale stretched maintenance windows to multiple days.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Targeted partition reassignment&lt;/strong&gt; modifies the default Kafka reassignment behaviour. The default algorithm reassigns all partitions on a broker, which at PayPal’s partition counts produces very long rebalancing windows. PayPal’s modification restricts reassignment to only the under-replicated partitions on the affected broker, which dramatically shortens the rebalancing duration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upstream open-source contributions&lt;/strong&gt; reflect the depth of operational experience the team has developed. PayPal authored three Kafka Improvement Proposals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;KIP-351: adds a &lt;code&gt;--under-min-isr&lt;/code&gt; flag to &lt;code&gt;kafka-topics.sh&lt;/code&gt; for identifying partitions below minimum in-sync replicas&lt;/li&gt;
&lt;li&gt;KIP-427: adds an &lt;code&gt;at-min-isr&lt;/code&gt; partition category metric to the broker&lt;/li&gt;
&lt;li&gt;KIP-517: adds consumer polling behaviour metrics for observing &lt;code&gt;max.poll.interval.ms&lt;/code&gt; compliance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three address monitoring gaps that PayPal encountered running Kafka at scale before these metrics existed natively.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Self-managed, on bare metal (brokers) and VMs (ZooKeeper, MirrorMaker), with QA on Google Cloud Platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring and alerting:&lt;/strong&gt; The Kafka Metrics library collects metrics from brokers, ZooKeeper nodes, MirrorMakers, and Kafka clients. Metrics are registered via Micrometer and forwarded to SignalFX. Alert thresholds are tuned to fire on actionable conditions rather than informational noise. The team made deliberate decisions about which metrics to surface, discarding many default metrics that did not correlate reliably with user-facing issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Capacity management:&lt;/strong&gt; Infrastructure is scaled ahead of peak periods. The topic onboarding workflow includes a capacity analysis step so that new topics are placed on clusters with headroom. Retail Friday planning involves expanding broker capacity to accommodate the expected traffic surge above the 21 million messages per second baseline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; The Onboarding Dashboard and Config Service form the main interface between application teams and the Kafka platform. Application teams do not need to know which cluster a topic lives on or manage their own broker bootstrap configurations; the platform handles that. This abstraction has enabled broad adoption across teams without requiring each team to develop deep Kafka operational knowledge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upgrade and migration strategy:&lt;/strong&gt; The 2021 data centre migration demonstrated PayPal’s approach to large-scale infrastructure changes: use MirrorMaker pipelines to shift traffic gradually, maintain application transparency throughout, and validate at each step before cutting over. The same principles apply to broker version upgrades, where the URP-checking plugin provides the gate condition for rolling restarts.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unauthenticated client connections&lt;/td&gt;
&lt;td&gt;Early clusters had a plaintext port with no authentication requirement&lt;/td&gt;
&lt;td&gt;SASL-based ACLs requiring clients to authenticate and declare producer or consumer intent; unique tokens per topic&lt;/td&gt;
&lt;td&gt;Full visibility of producers and consumers; anonymous access eliminated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker patching at 1,500+ node scale&lt;/td&gt;
&lt;td&gt;Default patching interrupted replication; sequential single-broker patching was impractical across 85+ clusters&lt;/td&gt;
&lt;td&gt;URP-checking plugin gates patches on replication health; enables parallel cluster patching with single-broker restarts&lt;/td&gt;
&lt;td&gt;Patching windows reduced from multiple days to hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long rebalancing after broker changes&lt;/td&gt;
&lt;td&gt;Default reassignment algorithm moves all partitions on a broker, producing very long rebalancing windows at high partition counts&lt;/td&gt;
&lt;td&gt;Modified reassignment to restrict to only under-replicated partitions on the affected broker&lt;/td&gt;
&lt;td&gt;Dramatically shorter rebalancing duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High QA environment cost&lt;/td&gt;
&lt;td&gt;QA clusters mirrored production footprint on-premises&lt;/td&gt;
&lt;td&gt;Rebuilt QA on Google Cloud Platform with multi-zone brokers&lt;/td&gt;
&lt;td&gt;75% cost reduction; 40% performance improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message loss during consumer group rebalances&lt;/td&gt;
&lt;td&gt;Kafka producer back-pressure had no durable buffer; events arriving during a rebalance were at risk of loss&lt;/td&gt;
&lt;td&gt;Chronicle Queue (memory-mapped disk buffer) inserted ahead of the Kafka producer in the user tracking pipeline&lt;/td&gt;
&lt;td&gt;Zero message loss during rebalance events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics latency of 12 hours&lt;/td&gt;
&lt;td&gt;Reports depended on macro-batch Spark jobs running on multi-hour intervals&lt;/td&gt;
&lt;td&gt;Kafka consumer pipeline publishing to BigQuery in real time using Project Reactor&lt;/td&gt;
&lt;td&gt;Analytics latency reduced from 12 hours to seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-downtime data centre migration at 1 trillion+ messages per day&lt;/td&gt;
&lt;td&gt;Migrating 20+ clusters required cut-over without service interruption or data loss&lt;/td&gt;
&lt;td&gt;60+ MirrorMaker groups used for gradual, transparent traffic migration with no application-side changes&lt;/td&gt;
&lt;td&gt;0% service outage; 0% message loss&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Bare metal deployment for broker nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;VM-deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;MirrorMaker&lt;/td&gt;
&lt;td&gt;~2,000 nodes on VMs; cross-DC and cross-security-zone replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Spark Streaming, Akka Streams (via Squbs), Project Reactor&lt;/td&gt;
&lt;td&gt;Spark for risk/analytics pipelines; Squbs for user tracking; Reactor for BigQuery pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;SignalFX (via Micrometer and Kafka Metrics library)&lt;/td&gt;
&lt;td&gt;Broker, ZooKeeper, MirrorMaker, and client metrics; alerting on threshold breaches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QA infrastructure&lt;/td&gt;
&lt;td&gt;Google Cloud Platform (multi-zone)&lt;/td&gt;
&lt;td&gt;Topic-for-topic mapping of production clusters; same security posture as production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client languages&lt;/td&gt;
&lt;td&gt;Java, Python, Go, Scala, Node.js&lt;/td&gt;
&lt;td&gt;Internal reactive framework: Squbs (Akka Streams-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent buffer&lt;/td&gt;
&lt;td&gt;Chronicle Queue&lt;/td&gt;
&lt;td&gt;Memory-mapped disk buffer ahead of Kafka producer in user tracking pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data sinks&lt;/td&gt;
&lt;td&gt;BigQuery (Google Cloud), Teradata&lt;/td&gt;
&lt;td&gt;BigQuery: real-time analytics pipeline; Teradata: batch and warehouse workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config distribution&lt;/td&gt;
&lt;td&gt;Internal Kafka Config Service&lt;/td&gt;
&lt;td&gt;Stateless; distributes bootstrap addresses and client config to all applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certificate management&lt;/td&gt;
&lt;td&gt;Internal security library&lt;/td&gt;
&lt;td&gt;SSL certificate lifecycle management for all Kafka clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics instrumentation&lt;/td&gt;
&lt;td&gt;Micrometer&lt;/td&gt;
&lt;td&gt;Client-side metrics registration layer; forwards to SignalFX&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maulin Vasavada&lt;/td&gt;
&lt;td&gt;Software Developer and Architect, Kafka team, PayPal&lt;/td&gt;
&lt;td&gt;Strata NY 2018; Kafka Summit 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Lu&lt;/td&gt;
&lt;td&gt;Software Engineer, PayPal&lt;/td&gt;
&lt;td&gt;Strata NY 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Na Yang&lt;/td&gt;
&lt;td&gt;Engineering Lead, PayPal&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lei Huang&lt;/td&gt;
&lt;td&gt;MTS2 Engineer, PayPal&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monish Koppa&lt;/td&gt;
&lt;td&gt;Software Engineer, PayPal&lt;/td&gt;
&lt;td&gt;PayPal developer blog, September 2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archit Agarwal&lt;/td&gt;
&lt;td&gt;Engineer, PayPal&lt;/td&gt;
&lt;td&gt;PayPal Tech Blog — consumer benchmarking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sakshi Ganeriwal&lt;/td&gt;
&lt;td&gt;Engineer, PayPal&lt;/td&gt;
&lt;td&gt;PayPal Tech Blog — user behaviour tracking, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Michael Zeltser&lt;/td&gt;
&lt;td&gt;Engineer, PayPal&lt;/td&gt;
&lt;td&gt;PayPal Tech Blog — fast data architecture, 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decouple application config from broker topology.&lt;/strong&gt; PayPal’s Kafka Config Service, which pushes bootstrap addresses and client config to applications at runtime, is one of the most operationally significant tools in their stack. At any meaningful scale, hard-coded broker IPs become a maintenance liability. A centralised config distribution layer makes broker changes transparent to applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add a durable buffer ahead of your producer if message loss during rebalances is unacceptable.&lt;/strong&gt; Kafka consumer group rebalances are not exceptional events; they happen routinely as deployments roll, instances scale, and partitions reassign. If your producer pipeline has no durable buffer, events arriving during a rebalance window are at risk. Chronicle Queue (or an equivalent mechanism) can absorb the gap with minimal latency overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gate your patching and reassignment operations on under-replicated partition counts.&lt;/strong&gt; PayPal’s URP-checking plugin and targeted reassignment modification both reflect the same principle: do not proceed with infrastructure changes while replication is degraded. Building this check into your automation, rather than relying on manual observation, is what enables parallel patching at scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in your QA environment’s fidelity early.&lt;/strong&gt; PayPal’s migration of QA to GCP delivered a 75% cost reduction and 40% performance improvement. A QA environment that accurately reflects production topology is the foundation for safe rollouts, and the cost of maintaining it does not have to scale linearly with production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribute upstream when you find gaps in observability.&lt;/strong&gt; KIP-351, KIP-427, and KIP-517 all addressed monitoring blind spots that PayPal encountered before these metrics existed in Kafka natively. If you are running Kafka at scale and hitting the edges of what the built-in metrics tell you, the upstream KIP process is a viable path to a permanent fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monish Koppa, “Scaling Kafka to Support PayPal’s Data Growth,” PayPal developer blog, September 2023 — &lt;a href=&quot;https://developer.paypal.com/community/blog/scaling-kafka-to-support-paypals-data-growth/&quot;&gt;https://developer.paypal.com/community/blog/scaling-kafka-to-support-paypals-data-growth/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Monish Koppa, “Scaling Kafka to Support PayPal’s Data Growth,” The PayPal Technology Blog, Medium — &lt;a href=&quot;https://medium.com/paypal-tech/scaling-kafka-to-support-paypals-data-growth-a0b4da420fab&quot;&gt;https://medium.com/paypal-tech/scaling-kafka-to-support-paypals-data-growth-a0b4da420fab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Maulin Vasavada, “Marching Toward a Trillion Kafka Messages per Day,” Kafka Summit 2020, Confluent — &lt;a href=&quot;https://www.confluent.io/resources/kafka-summit-2020/marching-toward-a-trillion-kafka-messages-per-day-running-kafka-at-scale-at-paypal/&quot;&gt;https://www.confluent.io/resources/kafka-summit-2020/marching-toward-a-trillion-kafka-messages-per-day-running-kafka-at-scale-at-paypal/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lei Huang and Na Yang, “How Did We Move the Mountain? Migrating 1 Trillion+ Messages Per Day Across Data Centers at PayPal,” Kafka Summit Americas 2021, Confluent — &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-americas-2021/how-did-we-move-the-mountain-migrating-1-trillion-messages-per-day-across/&quot;&gt;https://www.confluent.io/events/kafka-summit-americas-2021/how-did-we-move-the-mountain-migrating-1-trillion-messages-per-day-across/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kevin Lu, Maulin Vasavada, Na Yang, “Kafka at PayPal: Enabling 400 Billion Messages a Day,” Strata Data Conference NY 2018 — &lt;a href=&quot;https://conferences.oreilly.com/strata/strata-ny-2018/public/schedule/detail/69459.html&quot;&gt;https://conferences.oreilly.com/strata/strata-ny-2018/public/schedule/detail/69459.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Archit Agarwal et al., “Scaling Kafka Consumer for Billions of Events,” The PayPal Technology Blog, Medium — &lt;a href=&quot;https://medium.com/paypal-tech/kafka-consumer-benchmarking-c726fbe4000&quot;&gt;https://medium.com/paypal-tech/kafka-consumer-benchmarking-c726fbe4000&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Sakshi Ganeriwal, “Tracking User Behavior at Scale with Streaming Reactive Big Data Systems,” The PayPal Technology Blog, Medium, September 2018 — &lt;a href=&quot;https://medium.com/paypal-tech/https-medium-com-paypal-engineering-tracking-user-behavior-at-scale-f0c584c4ddd4&quot;&gt;https://medium.com/paypal-tech/https-medium-com-paypal-engineering-tracking-user-behavior-at-scale-f0c584c4ddd4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Michael Zeltser, “From Big Data to Fast Data in Four Weeks — Part 2,” The PayPal Technology Blog, Medium, November 2016 — &lt;a href=&quot;https://medium.com/paypal-tech/from-big-data-to-fast-data-in-four-weeks-or-how-reactive-programming-is-changing-the-world-part-2-29a9f7d48318&quot;&gt;https://medium.com/paypal-tech/from-big-data-to-fast-data-in-four-weeks-or-how-reactive-programming-is-changing-the-world-part-2-29a9f7d48318&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Kafka Improvement Proposals authored by PayPal:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-351:+Add+--under-min-isr+option+to+describe+topics+command&quot;&gt;KIP-351&lt;/a&gt; — Add –under-min-isr option to describe topics command&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=103089398&quot;&gt;KIP-427&lt;/a&gt; — Add AtMinIsr topic partition category (new metric &amp;amp; TopicCommand option)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-517:+Add+consumer+metrics+to+observe+user+poll+behavior&quot;&gt;KIP-517&lt;/a&gt; — Add consumer metrics to observe user poll behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are running Kafka in production and want deeper visibility into your brokers, consumer lag, and topic health, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you a full-featured monitoring and management UI that connects to any Kafka cluster in minutes. You can try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Reddit uses Apache Kafka in production</title><link>https://factorhouse.io/articles/reddit-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/reddit-kafka-architecture/</guid><description>A deep-dive into Reddit&apos;s Kafka architecture — covering use cases, scale, engineering decisions and key contributors.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Reddit runs one of the largest self-managed &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; fleets in the industry: more than 500 brokers, over a petabyte of live data, and tens of millions of messages per second. In early 2026, the platform engineering team migrated that entire fleet from Amazon EC2 to Kubernetes with zero client-side connection-string changes, while the site remained live throughout.&lt;/p&gt;
&lt;p&gt;Apache Kafka sits at the centre of Reddit’s event infrastructure, carrying clickstream data, real-time safety signals, vote integrity checks, and ads pipeline events across a platform with hundreds of millions of monthly active users.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Reddit is a social news aggregation and discussion platform where users submit content, vote posts and comments up or down, and organise communities around shared interests. At the time of its 2019 infrastructure overhaul, Reddit reported 330 million monthly active users, 12 million posts per month, and 2 billion votes per month.&lt;/p&gt;
&lt;p&gt;Reddit adopted Kafka as part of a broader infrastructure rationalisation that began around 2017, when the engineering team started migrating services from manually managed Puppet configurations to Terraform. Kafka and ZooKeeper were among the first services converted. The driver was operational complexity: broker replacement required a 14-step manual runbook, ZooKeeper and Kafka were provisioned with ad hoc scripts, and the team had no reliable way to reproduce or version configuration changes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-2017&lt;/td&gt;
&lt;td&gt;Kafka on EC2 with manual Puppet configs; broker replacement required a 14-step runbook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;Large-scale migration to Terraform begins; Kafka and ZooKeeper are among the first services converted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;January 2019&lt;/td&gt;
&lt;td&gt;Krishnan Chandra presents Reddit’s Terraform-based Kafka management at HashiCorp; broker replacement reduced to three Terraform steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;ksqlDB-backed event QA tool and vote-manipulation detection pipeline presented at Kafka Summit Americas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;REV1 real-time safety system (Kafka and Flink Stateful Functions) presented at Flink Forward Global&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2023&lt;/td&gt;
&lt;td&gt;REV2 (Rule Execution V2) published: per-action-type Protobuf Kafka topics, GitHub/S3 rule versioning, time-travel via offset resets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025–2026&lt;/td&gt;
&lt;td&gt;Entire Kafka fleet (500+ brokers, 1+ petabyte of live data) migrated from EC2 to Kubernetes using Strimzi; ZooKeeper-to-KRaft migration follows as a separate phase&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;reddits-kafka-use-cases&quot;&gt;Reddit’s Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;clickstream-and-event-ingestion&quot;&gt;Clickstream and event ingestion&lt;/h3&gt;
&lt;p&gt;Kafka’s longest-standing role at Reddit is as the backbone for clickstream and tracking-event ingestion. Events from web and mobile clients flow through load balancers to stateless application servers and into Kafka. From there, a stream-processing layer routes events to downstream consumers including Hive/S3 archival, BigQuery, and real-time safety systems. This pipeline was in place by 2019 and has remained the foundation of Reddit’s event infrastructure since.&lt;/p&gt;
&lt;h3 id=&quot;real-time-event-qa&quot;&gt;Real-time event QA&lt;/h3&gt;
&lt;p&gt;Reddit’s data engineering team, led by Hannah Hagen and Paul Kiernan, built an internal QA tool that streams events directly from the production Kafka pipeline using ksqlDB. Engineers deploying a new app version can filter events by user ID, device ID, or interaction type and receive feedback on whether events are firing correctly within seconds. Before this tool, developers had to wait approximately two hours for events to appear in the data warehouse. The tool is backed by both ksqlDB and Kafka Streams.&lt;/p&gt;
&lt;h3 id=&quot;vote-manipulation-detection&quot;&gt;Vote-manipulation detection&lt;/h3&gt;
&lt;p&gt;Reddit’s Anti-Evil Engineering team replaced hourly Airflow batch jobs with a ksqlDB streaming pipeline for detecting vote manipulation and derogatory content. Derek Hsieh presented the approach at Kafka Summit Americas 2021. Detection latency dropped from hours to minutes, with Kafka providing the continuous event stream that ksqlDB queries against.&lt;/p&gt;
&lt;h3 id=&quot;real-time-safety-actioning&quot;&gt;Real-time safety actioning&lt;/h3&gt;
&lt;p&gt;Reddit’s real-time safety applications team uses Kafka as both the ingestion and egress layer for detecting and actioning policy-violating content. The first version of this system (REV1), presented at Flink Forward Global 2021 by Frédérique Mittelstaedt, Bhavani Balasubramanyam, and Vignesh Raja, used Kafka topics to carry post and comment events into Flink Stateful Functions, which dispatched messages to remote Python service endpoints executing Lua-based rules. Action messages then flowed to Kafka egress topics, where Safety Actioning Workers consumed them and applied the corresponding platform action.&lt;/p&gt;
&lt;p&gt;The successor system, REV2, published by Reddit Safety Engineering in October 2023, extended this architecture with per-action-type Kafka egress topics, Protobuf-formatted action messages, GitHub-backed rule versioning with S3 distribution, and a time-travel feature implemented via consumer group offset resets.&lt;/p&gt;
&lt;h3 id=&quot;ads-data-pipeline&quot;&gt;Ads data pipeline&lt;/h3&gt;
&lt;p&gt;Reddit operates a dedicated Kafka engineering team for ads data infrastructure. This team builds and maintains Kafka consumers using Flink and Spark as the downstream processing layer, serving all of Reddit’s Ads engineering teams.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;At the time of Reddit’s 2025-2026 EC2-to-Kubernetes migration, the Kafka fleet comprised:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brokers&lt;/td&gt;
&lt;td&gt;500+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live data held in Kafka&lt;/td&gt;
&lt;td&gt;More than 1 petabyte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;Tens of millions of messages per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client services requiring reconfiguration&lt;/td&gt;
&lt;td&gt;250+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The 2019 platform context provides additional background: at the time Krishnan Chandra presented Reddit’s Terraform-based Kafka management, Reddit had 330 million monthly active users generating 12 million posts and 2 billion votes per month. No public breakdown of topic count, partition count, or consumer group count has been published by Reddit’s engineering team.&lt;/p&gt;
&lt;h2 id=&quot;reddits-kafka-architecture&quot;&gt;Reddit’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;deployment-model&quot;&gt;Deployment model&lt;/h3&gt;
&lt;p&gt;Reddit’s Kafka deployment has changed substantially since 2019. The original model was self-managed Kafka on Amazon EC2, provisioned through Terraform modules and Puppet, with operators applying changes directly from their workstations. By 2025, the team had migrated the entire fleet to Kubernetes, managed via the Strimzi operator.&lt;/p&gt;
&lt;h3 id=&quot;control-plane&quot;&gt;Control plane&lt;/h3&gt;
&lt;p&gt;Reddit originally used ZooKeeper for Kafka metadata management. After completing the data-plane migration to Kubernetes, the team migrated the control plane from ZooKeeper to KRaft as a separate, sequenced phase.&lt;/p&gt;
&lt;h3 id=&quot;safety-pipeline-rev2&quot;&gt;Safety pipeline (REV2)&lt;/h3&gt;
&lt;p&gt;The REV2 architecture follows this sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Kafka ingress topics receive post and comment events from upstream producers.&lt;/li&gt;
&lt;li&gt;Flink Stateful Functions processes messages and dispatches them to remote Python service endpoints.&lt;/li&gt;
&lt;li&gt;The Python endpoints execute Lua-based safety rules.&lt;/li&gt;
&lt;li&gt;Rule outputs are written as Protobuf-formatted messages to per-action-type Kafka egress topics.&lt;/li&gt;
&lt;li&gt;Safety Actioning Workers consume egress topics and apply the corresponding platform action.&lt;/li&gt;
&lt;li&gt;Rule configurations are stored in S3, pushed from GitHub CI. A Kubernetes sidecar in each worker pod polls S3 for updates, enabling rule changes without a full service redeploy.&lt;/li&gt;
&lt;li&gt;The time-travel feature resets consumer group offsets to replay historical content through newly published rules.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;Reddit uses Protobuf as the serialisation format for action messages on REV2 Kafka egress topics. Each action type has its own dedicated topic with a Protobuf schema enforced per topic.&lt;/p&gt;
&lt;h3 id=&quot;clickstream-pipeline&quot;&gt;Clickstream pipeline&lt;/h3&gt;
&lt;p&gt;As of 2019, the event ingestion path ran: mobile and web clients to load balancers, to stateless application servers, into Kafka, then to a stream-processing layer, with archival to Hive/S3 and analytics writes to BigQuery.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Reddit uses ksqlDB and Kafka Streams for the event QA tool and vote-manipulation detection, and Apache Flink Stateful Functions for the REV2 safety system.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;dns-facade-for-zero-downtime-broker-migration&quot;&gt;DNS facade for zero-downtime broker migration&lt;/h3&gt;
&lt;p&gt;Before moving any broker from EC2 to Kubernetes, Reddit introduced an intermediate DNS layer: client applications connected to infrastructure-controlled DNS records rather than directly to broker hostnames. This decoupled client connection strings from physical broker addresses, meaning no client application needed to update its configuration during the migration. Over 250 client services were effectively transparent to the change.&lt;/p&gt;
&lt;h3 id=&quot;forking-strimzi-for-a-mixed-cluster-transition&quot;&gt;Forking Strimzi for a mixed-cluster transition&lt;/h3&gt;
&lt;p&gt;Strimzi does not natively support hybrid clusters spanning EC2 and Kubernetes. Reddit forked the operator and introduced targeted modifications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Plaintext inter-broker listeners accessible from both environments&lt;/li&gt;
&lt;li&gt;Shared ZooKeeper metadata management during the transition period&lt;/li&gt;
&lt;li&gt;Consistent Cruise Control configuration across EC2 and Kubernetes brokers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After all EC2 brokers were decommissioned, Reddit removed the fork and switched to the standard Strimzi operator.&lt;/p&gt;
&lt;h3 id=&quot;broker-id-space-reorganisation&quot;&gt;Broker ID space reorganisation&lt;/h3&gt;
&lt;p&gt;Strimzi requires low broker IDs for the brokers it manages. Because the existing EC2 brokers occupied the low-ID space, Reddit resolved this by temporarily doubling the cluster: new high-numbered EC2 brokers were added, Cruise Control drained partitions from the original low-numbered brokers, those brokers were decommissioned, and Kubernetes brokers were assigned the freed low IDs.&lt;/p&gt;
&lt;h3 id=&quot;reversibility-as-a-design-constraint&quot;&gt;Reversibility as a design constraint&lt;/h3&gt;
&lt;p&gt;Each phase of the migration was required to be fully reversible before the team proceeded to the next. Reddit treated non-reversibility as a hard blocker. This constrained the migration timeline but ensured that any unexpected failure could be rolled back without data loss.&lt;/p&gt;
&lt;h3 id=&quot;kraft-migration-sequenced-separately&quot;&gt;KRaft migration sequenced separately&lt;/h3&gt;
&lt;p&gt;Rather than migrating ZooKeeper to KRaft concurrently with the data-plane move to Kubernetes, Reddit treated them as separate phases: the data-plane migration was completed and stabilised first, and the ZooKeeper-to-KRaft migration followed as a distinct step.&lt;/p&gt;
&lt;h3 id=&quot;per-action-type-protobuf-topics&quot;&gt;Per-action-type Protobuf topics&lt;/h3&gt;
&lt;p&gt;In REV2, each safety action type has its own dedicated Kafka egress topic with a Protobuf schema enforced per topic. This gives the team granular per-action monitoring and tighter schema contracts, compared to a single multiplexed output topic.&lt;/p&gt;
&lt;h3 id=&quot;consumer-group-offset-resets-for-retroactive-rule-application&quot;&gt;Consumer group offset resets for retroactive rule application&lt;/h3&gt;
&lt;p&gt;REV2 implements a time-travel capability by resetting Kafka consumer group offsets. When a new safety rule is published, operators can replay historical content through it by rewinding the consumer group to an earlier offset, without needing a separate historical data store.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;h3 id=&quot;terraform-managed-lifecycle-pre-kubernetes&quot;&gt;Terraform-managed lifecycle (pre-Kubernetes)&lt;/h3&gt;
&lt;p&gt;By 2019, Reddit’s Terraform module had reduced broker replacement from a 14-step manual runbook to three operations: &lt;code&gt;terraform taint&lt;/code&gt; the broker node, run &lt;code&gt;plan&lt;/code&gt;, run &lt;code&gt;apply&lt;/code&gt;. The new broker automatically registered with ZooKeeper and updated DNS records. A parallel module managed ZooKeeper ensemble provisioning by the same pattern. The module accepted cluster size and instance type as inputs and handled node discovery, ZooKeeper registration, and security group export automatically.&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-operator-management-post-migration&quot;&gt;Kubernetes operator management (post-migration)&lt;/h3&gt;
&lt;p&gt;After the EC2-to-Kubernetes migration, Kafka clusters are managed declaratively via the Strimzi Kubernetes operator. Configuration changes and upgrades are applied through manifest updates rather than ad hoc commands.&lt;/p&gt;
&lt;h3 id=&quot;cruise-control-for-partition-rebalancing&quot;&gt;Cruise Control for partition rebalancing&lt;/h3&gt;
&lt;p&gt;Reddit uses Cruise Control to automate partition reassignment during scaling events and broker replacements. During the 2025-2026 migration, Cruise Control was used to incrementally drain partitions from EC2 brokers before decommissioning them.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;strimzis-lack-of-support-for-mixed-ec2kubernetes-clusters&quot;&gt;Strimzi’s lack of support for mixed EC2/Kubernetes clusters&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The team needed brokers in both EC2 and Kubernetes to coexist in a single cluster during migration so that partitions could be drained gradually rather than cut over all at once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Strimzi is designed to manage Kafka clusters entirely within Kubernetes and has no built-in support for inter-broker communication across EC2 and Kubernetes networking boundaries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Reddit forked Strimzi and added support for plaintext inter-broker listeners accessible from both environments, shared ZooKeeper metadata management, and unified Cruise Control configuration. The fork ran for several weeks during migration, then was retired.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Zero-downtime migration. No client application changed its connection string.&lt;/p&gt;
&lt;h3 id=&quot;client-services-tightly-coupled-to-broker-hostnames&quot;&gt;Client services tightly coupled to broker hostnames&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; 250+ client services held connection strings pointing directly to EC2 broker hostnames. Changing all of them simultaneously was not feasible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; No abstraction layer existed between client configuration and physical broker addresses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Reddit introduced a DNS facade layer before any broker movement. Clients were redirected to stable DNS names under Reddit’s control, while physical broker addresses changed independently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; All 250+ client services continued operating without modification throughout the migration.&lt;/p&gt;
&lt;h3 id=&quot;broker-id-space-exhaustion&quot;&gt;Broker ID space exhaustion&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Strimzi requires low broker IDs for the brokers it manages. All low IDs were already in use by existing EC2 brokers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Kafka broker IDs are fixed at creation time and cannot be reassigned to a running broker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Reddit doubled the cluster by provisioning new high-numbered EC2 brokers, used Cruise Control to drain all partitions from the original low-numbered brokers, decommissioned those brokers, then assigned the freed IDs to Kubernetes brokers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; No disruption to producers or consumers during the ID space reorganisation.&lt;/p&gt;
&lt;h3 id=&quot;slow-and-error-prone-safety-rule-deployment-rev1&quot;&gt;Slow and error-prone safety rule deployment (REV1)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; REV1 ran each safety rule as an independent process on Python 2.7 and raw EC2. There was no version control for rules, no staging environment, and no rule history. Deployment required engineers to SSH directly into hosts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; REV1 was not built for operational scale. Rules were written and deployed outside of standard engineering workflows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; REV2 moved rules to GitHub, pushed configurations to S3 via CI, and introduced a Kubernetes sidecar that polls for updates, reducing rule deployment time by approximately 90% and eliminating direct host access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Rules can be updated and rolled back without a full service redeploy, with a complete audit trail in version control.&lt;/p&gt;
&lt;h3 id=&quot;two-hour-instrumentation-feedback-lag&quot;&gt;Two-hour instrumentation feedback lag&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Engineers deploying new app versions had to wait approximately two hours before events appeared in the data warehouse, making it difficult to verify that instrumentation was correct immediately after a release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The data warehouse pipeline introduced significant processing latency between event production and queryable state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Hannah Hagen and Paul Kiernan built a ksqlDB-backed web application that filters events directly from the live Kafka pipeline. Feedback is available in seconds rather than hours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Engineers can verify event instrumentation in near real-time during or immediately after a deployment.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Self-managed; migrated from EC2 to Kubernetes in 2025-2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster management&lt;/td&gt;
&lt;td&gt;Strimzi&lt;/td&gt;
&lt;td&gt;Kubernetes operator for declarative Kafka management (post-migration)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control plane&lt;/td&gt;
&lt;td&gt;KRaft (post-migration), ZooKeeper (pre-migration)&lt;/td&gt;
&lt;td&gt;ZooKeeper-to-KRaft migration completed as a separate phase after the data-plane migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition rebalancing&lt;/td&gt;
&lt;td&gt;Cruise Control&lt;/td&gt;
&lt;td&gt;Used during broker replacement and migration partition draining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;ksqlDB, Kafka Streams, Apache Flink (Stateful Functions)&lt;/td&gt;
&lt;td&gt;ksqlDB and Kafka Streams for event QA and vote-manipulation detection; Flink Stateful Functions for REV2 safety system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Protobuf&lt;/td&gt;
&lt;td&gt;Used for REV2 action messages on per-action-type Kafka egress topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure as code&lt;/td&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;Kafka and ZooKeeper provisioning and broker lifecycle management from 2017 onwards (pre-Kubernetes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration management&lt;/td&gt;
&lt;td&gt;Puppet&lt;/td&gt;
&lt;td&gt;Pre-Terraform; Terraform modules were partially derived from existing Puppet configs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Hosts Flink, safety workers, Kafka brokers (post-migration), and rule-update sidecars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Amazon Web Services (EC2, S3)&lt;/td&gt;
&lt;td&gt;EC2 was the original broker host; S3 stores REV2 rule configurations and event archives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rule language&lt;/td&gt;
&lt;td&gt;Lua&lt;/td&gt;
&lt;td&gt;Safety rules executed by Flink Stateful Functions remote Python endpoints in REV2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote function language&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;REV2 remote function endpoints; Python 2.7 used in REV1 (deprecated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI and version control&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;Version control for REV2 safety rules; CI pipeline pushes rule configs to S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing (legacy)&lt;/td&gt;
&lt;td&gt;Apache Airflow&lt;/td&gt;
&lt;td&gt;Replaced by Kafka/ksqlDB streaming for safety detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data warehouse&lt;/td&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;Downstream analytics store fed from the Kafka event pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archival&lt;/td&gt;
&lt;td&gt;Apache Hive, Amazon S3&lt;/td&gt;
&lt;td&gt;Archival store for event data processed from Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Downstream processing&lt;/td&gt;
&lt;td&gt;Apache Spark&lt;/td&gt;
&lt;td&gt;Used alongside Flink in the ads data pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hannah Hagen&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Data Engineering&lt;/td&gt;
&lt;td&gt;Co-built the ksqlDB event QA tool; presented at Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paul Kiernan&lt;/td&gt;
&lt;td&gt;Staff Software Engineer&lt;/td&gt;
&lt;td&gt;Co-built the ksqlDB event QA tool; presented at Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Derek Hsieh&lt;/td&gt;
&lt;td&gt;Software Engineer III, Data Engineering&lt;/td&gt;
&lt;td&gt;Led the ksqlDB vote-manipulation detection pipeline; presented at Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frédérique Mittelstaedt&lt;/td&gt;
&lt;td&gt;Engineering Manager, Real-Time Safety Applications&lt;/td&gt;
&lt;td&gt;Led design of Reddit’s centralised streaming platform on Kafka and Flink; presented at Flink Forward Global 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bhavani Balasubramanyam&lt;/td&gt;
&lt;td&gt;Software Engineer, Anti-Evil Engineering&lt;/td&gt;
&lt;td&gt;Developed real-time pipeline for violent content detection; co-presented at Flink Forward Global 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vignesh Raja&lt;/td&gt;
&lt;td&gt;Software Engineer&lt;/td&gt;
&lt;td&gt;Worked on Flink Stateful Functions and Kafka for user safety; co-presented at Flink Forward Global 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Krishnan Chandra&lt;/td&gt;
&lt;td&gt;Senior Software Engineer&lt;/td&gt;
&lt;td&gt;Led Terraform-based Kafka infrastructure management; presented at HashiCorp 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neven Miculinić&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Messaging Infrastructure&lt;/td&gt;
&lt;td&gt;Associated with the EC2-to-Kubernetes Kafka migration project (“Swapping the Engine Mid-Flight”, 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Introduce a DNS abstraction before migrating brokers.&lt;/strong&gt; Reddit’s experience shows that decoupling client connection strings from physical broker addresses is what makes large-scale broker migrations feasible without client-side changes. If you are planning a hosting migration, build the DNS facade first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sequence control-plane and data-plane migrations separately.&lt;/strong&gt; Reddit migrated brokers from EC2 to Kubernetes first, stabilised the deployment, and only then migrated from ZooKeeper to KRaft. Attempting both simultaneously increases the blast radius if something goes wrong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat reversibility as a hard constraint, not a nice-to-have.&lt;/strong&gt; Reddit required each migration phase to be fully reversible before proceeding. This slows the timeline but makes it possible to halt and recover if an unexpected issue arises mid-migration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use per-type topics with enforced schemas for action pipelines.&lt;/strong&gt; The REV2 architecture uses a distinct Kafka egress topic per action type with a Protobuf schema. If you are building a Kafka-backed action or command pipeline, this pattern makes monitoring and schema evolution substantially more manageable than a multiplexed topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka consumer group offsets are a reprocessing primitive.&lt;/strong&gt; Reddit’s time-travel feature in REV2 demonstrates that resetting consumer group offsets to replay historical data is a practical operational technique, not only a disaster-recovery option. If your pipeline needs to apply new logic to historical events, this approach avoids the need for a separate historical data store.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Hannah Hagen and Paul Kiernan (Reddit) — &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-americas-2021/live-event-debugging-with-ksqldb-at-reddit/&quot;&gt;Live Event Debugging With ksqlDB at Reddit&lt;/a&gt;, Kafka Summit Americas 2021&lt;/li&gt;
&lt;li&gt;Hannah Hagen and Paul Kiernan (Reddit) — &lt;a href=&quot;https://www.slideshare.net/slideshow/live-event-debugging-with-ksqldb-at-reddit-hannah-hagen-and-paul-kiernan-reddit/250290598&quot;&gt;Kafka Summit Americas 2021 slide deck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Derek Hsieh (Reddit) — &lt;a href=&quot;https://www.confluent.io/events/kafka-summit-americas-2021/catching-vote-manipulation-at-reddit/&quot;&gt;Catching Vote Manipulation at Reddit“, Kafka Summit Americas 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Frédérique Mittelstaedt, Bhavani Balasubramanyam, Vignesh Raja (Reddit) — &lt;a href=&quot;https://www.ververica.com/blog/keeping-redditors-safe-with-stateful-functions-flink-forward-2021&quot;&gt;Keeping Redditors Safe With Stateful Functions&lt;/a&gt;, Flink Forward Global 2021&lt;/li&gt;
&lt;li&gt;InfoQ — &lt;a href=&quot;https://www.infoq.com/news/2023/10/reddit-rev2/&quot;&gt;Reddit’s REV2: Replacing a Batch Safety System with a Real-Time One Using Flink and Kafka&lt;/a&gt;, October 2023&lt;/li&gt;
&lt;li&gt;Krishnan Chandra (Reddit) — &lt;a href=&quot;https://www.hashicorp.com/en/resources/how-reddit-large-scale-migration-terraform&quot;&gt;How Reddit’s Large Scale Migration to Terraform&lt;/a&gt;, HashiCorp, January 2019&lt;/li&gt;
&lt;li&gt;Reddit Engineering — &lt;a href=&quot;https://www.reddit.com/r/RedditEng/comments/1qb03l9/swapping_the_engine_midflight_how_we_moved/&quot;&gt;Swapping the Engine Mid-Flight: How We Moved a Petabyte of Kafka Data from EC2 to Kubernetes&lt;/a&gt;, r/RedditEng, January 2026:&lt;/li&gt;
&lt;li&gt;ByteByteGo — summary of &lt;a href=&quot;https://blog.bytebytego.com/p/how-reddit-migrated-petabyte-scale&quot;&gt;Reddit’s Kafka migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Red Hat Developer — &lt;a href=&quot;https://developers.redhat.com/blog/2026/02/02/kafka-monthly-digest-january-2026&quot;&gt;Kafka Monthly Digest&lt;/a&gt;, February 2026&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are monitoring a Kafka deployment at the scale described in this article, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides real-time visibility across brokers, topics, consumer groups, and schema registries. You can connect it to any Kafka cluster and try it free for 30 days.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Robinhood uses Apache Kafka in production</title><link>https://factorhouse.io/articles/robinhood-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/robinhood-kafka-architecture/</guid><description>A deep-dive into Robinhood&apos;s Kafka architecture: use cases, scale, engineering decisions, and key contributors. Learn how Robinhood processes 2.2 million messages per second across equities trading, crypto, fraud detection, and more.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Robinhood’s &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; platform handles 2.2 million messages per second across 6 clusters, 160 brokers, and 90,000 partitions, underpinning every step of its trading lifecycle from order routing to fraud detection to data lake ingestion. What makes its story particularly instructive is not the scale alone but the engineering choices the team made to reach it: a custom sidecar proxy to solve Python’s connection fan-in problem, a Postgres-backed dead letter queue that trades Kafka simplicity for richer replay tooling, and a 2025 decision to replace self-managed Kafka with WarpStream for logging workloads, cutting total costs by 45%.&lt;/p&gt;
&lt;p&gt;Across fintech, Kafka is common. At Robinhood the challenge was operating a large, heterogeneous fleet of Python-based Kafka applications at financial-grade reliability without overwhelming a small platform team.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Robinhood is a US-based brokerage and financial services platform offering commission-free trading in equities, ETFs, options, and cryptocurrency, along with a debit card and cash management product. As of 2025, the platform serves more than 14 million monthly active users.&lt;/p&gt;
&lt;p&gt;Kafka has been part of Robinhood’s infrastructure since at least 2015, when engineer Jaren Glover joined and was assigned ownership of stabilising an existing Kafka, ZooKeeper, and Elasticsearch pipeline. Over the following four years, Glover scaled that infrastructure from 100,000 to 10 million users, presenting what he learned at SREcon Americas and Kafka Summit in 2019.&lt;/p&gt;
&lt;p&gt;By 2017, the team had begun developing Faust, an open-source Python stream-processing library built on top of Apache Kafka. Faust was open-sourced in July 2018 and reflected how central Kafka had become to Robinhood’s product engineering.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;Kafka already in production; Jaren Glover takes ownership of stabilising the pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015-2019&lt;/td&gt;
&lt;td&gt;Platform scales from 100,000 to 10 million users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017-2018&lt;/td&gt;
&lt;td&gt;Faust Python stream-processing library developed and open-sourced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Data lake pipeline published: Kafka as primary databus, Secor archiving to S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-2020&lt;/td&gt;
&lt;td&gt;Brokerage system grows from 100k to 750k peak requests/second in six months; shard-aware Kafka filtering introduced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas: 6 clusters, 160 brokers, 1,400 topics, 90,000 partitions, 2.2M msg/sec confirmed publicly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;CDC pipeline using Debezium and Apache Hudi reduces data lake freshness from 24 hours to 5-15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Consumer Proxy (kafkaproxy v2, Java + gRPC) presented at Confluent Current&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;Kafka clusters migrated from EC2 to Kubernetes using a custom in-house operator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Logging workloads migrated from self-managed Kafka to WarpStream; 45% total cost reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;robinhoods-kafka-use-cases&quot;&gt;Robinhood’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;The Streaming Platform team describes Kafka as involved in “almost every mission-critical step of Robinhood’s functionality.” There are 14 confirmed production use cases across product, data, and infrastructure teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Equities order routing and trade execution.&lt;/strong&gt; Stock purchase events are written to both Kafka and Postgres. Multiple downstream services consume those events and process them using Kafka Streams with exactly-once semantics. The trade execution confirmation SLA is under one second, with 5-10 Kafka hops per trade depending on product type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Crypto trading.&lt;/strong&gt; The crypto backend uses shard-aware Kafka consumers. Each shard only processes messages belonging to users assigned to it, limiting the blast radius of any single consumer failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-clearing.&lt;/strong&gt; Robinhood’s in-house clearing operations are listed as a distinct Kafka use case by the Streaming Platform team, though detailed architecture has not been published.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Market data distribution.&lt;/strong&gt; External market data feeds are ingested via Kafka and consumed by internal Faust stream-processing applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Push notifications and messaging.&lt;/strong&gt; Named explicitly as a production use case in the 2021 Kafka Summit Americas talk by Chandra Kuchi and Nick Dellamaggiore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fraud detection and risk.&lt;/strong&gt; Faust processes risk signals in real time. Apache Flink handles stateful stream processing for fraud detection and shareholder position tracking, with more than 100 concurrent Flink deployments running in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Order execution quality monitoring and ad tracking.&lt;/strong&gt; Both are listed as production Faust use cases, alongside newsfeed aggregation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDC into the data lake.&lt;/strong&gt; Debezium captures WAL changes from thousands of Postgres RDS tables, encodes them as Avro records via Confluent Schema Registry, and writes them to Kafka. Apache Hudi Deltastreamer consumes those records with exactly-once semantics and writes into S3. Data freshness for core datasets improved from 24 hours to 5-15 minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Card transaction authorisation (backup path).&lt;/strong&gt; The backup payment authorisation service subscribes to asynchronous Kafka streams to cache each cardholder’s latest account state. The service must produce a decision within a 2-second window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Investor eligibility (Say Technologies acquisition).&lt;/strong&gt; Apache Flink determines investor eligibility with approximately 2-minute end-to-end latency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log analytics and observability.&lt;/strong&gt; Application and infrastructure logs flow through Kafka (and since 2025, WarpStream) via the Vector log shipper to Humio for search and storage.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;The figures below come from Chandra Kuchi and Nick Dellamaggiore’s Kafka Summit Americas 2021 presentation, reflecting the state of the platform at that time, with more recent additions from a 2025 WarpStream case study and a September 2024 Diginomica interview with Kuchi.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source / date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka clusters&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brokers (total)&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;1,400&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitions&lt;/td&gt;
&lt;td&gt;90,000&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messages per second (all clusters)&lt;/td&gt;
&lt;td&gt;2.2 million&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API requests per second (largest cluster)&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound connections (largest cluster)&lt;/td&gt;
&lt;td&gt;&amp;gt;100,000&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data processed per day&lt;/td&gt;
&lt;td&gt;10+ TB&lt;/td&gt;
&lt;td&gt;WarpStream case study, 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly active users&lt;/td&gt;
&lt;td&gt;14+ million&lt;/td&gt;
&lt;td&gt;WarpStream case study, 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrent Flink deployments&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;td&gt;Tony Chen, Current 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres tables in CDC pipeline&lt;/td&gt;
&lt;td&gt;Thousands&lt;/td&gt;
&lt;td&gt;Robinhood Engineering blog, 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-service latency (Kafka-connected)&lt;/td&gt;
&lt;td&gt;20-50 ms&lt;/td&gt;
&lt;td&gt;Chandra Kuchi, Diginomica 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brokerage peak load growth (Dec 2019 to Jun 2020)&lt;/td&gt;
&lt;td&gt;100k to 750k requests/sec (7x)&lt;/td&gt;
&lt;td&gt;Edmond Wong and Nathan Ziebart, Robinhood blog, 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faust throughput (2018 estimate)&lt;/td&gt;
&lt;td&gt;Billions of events and terabytes of data per day&lt;/td&gt;
&lt;td&gt;Ask Solem, Medium, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;robinhoods-kafka-architecture&quot;&gt;Robinhood’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;cluster-topology-and-hosting&quot;&gt;Cluster topology and hosting&lt;/h3&gt;
&lt;p&gt;Robinhood originally ran self-managed Confluent Platform on AWS EC2, provisioned with Saltstack. Brokers were spread across three AWS availability zones per cluster.&lt;/p&gt;
&lt;p&gt;By 2024, the team had migrated all clusters to Kubernetes. Rather than adopting an existing Kafka Kubernetes operator, Robinhood built a custom in-house operator to manage the migration and ongoing operations. Mun Yong Jang and Nathan Moderwell presented details of this migration at Confluent Current 2024. The three-AZ layout was preserved in the new Kubernetes deployment.&lt;/p&gt;
&lt;p&gt;Logging workloads were separated from the main Kafka clusters in 2025 and migrated to WarpStream, a diskless, S3-backed Kafka-compatible broker. WarpStream runs as three independent Kubernetes deployments (one per AZ), each with its own horizontal pod autoscaler, so logging capacity tracks market-hours traffic without manual intervention. Ethan Chen and Renan Rueda from Robinhood presented this architecture at Confluent Current New Orleans 2025.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloud provider:&lt;/strong&gt; AWS throughout. S3 serves as primary data lake storage and as WarpStream’s backing store. Postgres runs on AWS RDS.&lt;/p&gt;
&lt;h3 id=&quot;schema-management&quot;&gt;Schema management&lt;/h3&gt;
&lt;p&gt;Confluent Schema Registry is used for Avro encoding on the CDC pipeline. Debezium captures Postgres WAL and writes Avro-encoded change records to Kafka via the Schema Registry before downstream consumers read them into the data lake.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;All Python and Golang Kafka clients at Robinhood use kafkahood, an internal wrapper library built on librdkafka. kafkahood codifies standard defaults, client-side metrics collection, dead letter queue support, message serialisation, and feature-flag-based configuration rollouts. This means the Streaming Platform team can push new client defaults across the entire application fleet by flipping a feature flag rather than waiting for individual teams to update their library versions.&lt;/p&gt;
&lt;p&gt;Producer connections from Python services are routed through kafkaproxy (described in detail in the Special Techniques section below).&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer lifecycle management is handled by the Consumer Proxy (kafkaproxy v2), a Java-based Kubernetes sidecar that runs alongside each application container. The proxy handles all Kafka consumer group coordination, rebalancing, commit management, and timeout management. It relays messages to the application container over gRPC. This design means a consumer group rebalance triggered by a scaling event does not affect the application container’s business logic, and a failure in the application container does not orphan an uncommitted Kafka offset.&lt;/p&gt;
&lt;p&gt;kafkahood also provides a Postgres-backed dead letter queue for failed consumer events. Failed messages are inserted into Postgres rather than a secondary Kafka topic, and a CLI allows engineers to inspect, query, and fix records before republishing them to a retry topic.&lt;/p&gt;
&lt;p&gt;Consumer lag, throughput, and error rates are surfaced per application as standard defaults through kafkahood’s client-side metrics, without each team needing to add their own instrumentation.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Faust (Python).&lt;/strong&gt; Robinhood open-sourced Faust in 2018 as a Python port of the Kafka Streams API. In production, Faust applications handle risk signal processing, order quality monitoring, market data feed processing, newsfeed aggregation, ad tracking, event logging, and crypto feed processing. Faust uses RocksDB as a local embedded state store and aiokafka (which Robinhood also maintains as a fork) as its async Kafka client.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache Flink.&lt;/strong&gt; Stateful stream processing for fraud detection, shareholder position tracking, investor eligibility determination, and data ingestion is handled by Flink. Robinhood runs more than 100 concurrent Flink deployments, managed by the Apache flink-kubernetes-operator. Tony Chen from the Streaming Platform team presented Robinhood’s Flink deployment practices, including a custom Checkpoint-Fetcher tool for automating recovery, at Confluent Current 2024.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Debezium is used as the primary Kafka Connect source connector, capturing CDC events from Postgres RDS. It encodes changes as Avro records using Confluent Schema Registry and writes them to Kafka for consumption by Apache Hudi Deltastreamer on the data lake side. Secor (originally open-sourced by Pinterest) archives Kafka streams to S3 for the batch path of the data lake.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kafkaproxy as a sidecar for connection aggregation.&lt;/strong&gt; Python’s process-based concurrency model creates a problem at Kafka scale: each gunicorn worker process opens its own set of TCP connections to brokers, which at Robinhood’s cluster sizes produced more than 100,000 inbound connections on the largest cluster. The first-generation kafkaproxy was a Rust-based Kubernetes sidecar that ran alongside each application pod and aggregated all producer and consumer connections from that pod’s worker pool into a single connection set to the brokers. This cut broker-side resource pressure from Python services substantially.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer Proxy with gRPC message relay (v2).&lt;/strong&gt; The second generation of kafkaproxy, presented by Tony Chen and Mun Yong Jang at Confluent Current 2023, replaced the Rust implementation with a Java-based sidecar using the standard Apache Kafka client library. The v2 proxy takes full ownership of the Kafka consumer lifecycle: it handles consumer group rebalancing, commit management, and timeout management, then relays raw messages to the application container over gRPC. This decoupling means the application team no longer needs to reason about consumer group behaviour, and the platform team maintains a single Java consumer implementation rather than N language-specific clients.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shard-aware Kafka message routing.&lt;/strong&gt; During the 2019-2020 period when brokerage traffic grew 7x, Robinhood introduced application-level sharding over Postgres. Each shard’s Kafka consumers filter messages on shared topics to only process events belonging to users assigned to that shard. For high-throughput flows where the per-message filtering overhead became significant, Robinhood created shard-specific Kafka topics to eliminate the filtering cost entirely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDC with exactly-once semantics into the data lake.&lt;/strong&gt; The CDC pipeline chains Debezium (Postgres WAL capture), Confluent Schema Registry (Avro encoding), Kafka (transport), and Apache Hudi Deltastreamer (exactly-once writes into S3). Copy-on-write mode is used for raw tables to optimise columnar read performance. This pipeline reduced data freshness from 24 hours to 5-15 minutes across thousands of Postgres tables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Postgres-backed dead letter queue.&lt;/strong&gt; Rather than routing failed consumer events to a secondary Kafka topic, Robinhood writes them to a Postgres database. A client application with a CLI allows engineers to inspect and query failed records, apply fixes, and republish messages to a Kafka retry topic for safe reprocessing. Sreeram Ramji and Wenlong Xiong, who presented this pattern at Confluent Current 2022, noted that cross-team schema contracts make silent consumer failures particularly hazardous at Robinhood, which drove the choice of a more inspectable store over a native Kafka DLQ topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WarpStream for elastic logging clusters.&lt;/strong&gt; Log throughput at Robinhood is highly cyclical: it peaks during US market hours and drops significantly in evenings and on weekends. Running fixed-capacity Kafka clusters for this workload was costly and wasteful. By migrating logging to WarpStream, the team eliminated inter-AZ data transfer costs entirely (WarpStream’s diskless architecture does not move data between availability zones) and gained per-AZ horizontal pod autoscaling. The result was a 45% reduction in total logging infrastructure cost, broken down as 36% compute savings, 13% storage savings, and 99% inter-AZ networking savings. The trade-off was an increase in end-to-end latency from 0.2 to 0.45 seconds, which was acceptable for the logging workload.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Feature-flag-based client library rollouts.&lt;/strong&gt; kafkahood uses feature flags to control adoption of new client configuration defaults across the application fleet. A new default setting can be tested on a subset of services before rolling out broadly, with fast rollback if it causes latency or throughput regressions.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model.&lt;/strong&gt; Robinhood self-manages its Kafka infrastructure throughout its history, initially on EC2 with Saltstack and since 2024 on Kubernetes with a custom in-house operator. The WarpStream deployment for logging is also self-hosted (Kubernetes) rather than using WarpStream Cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observability.&lt;/strong&gt; Client-side metrics collection is built into kafkahood as a standard default for all Python and Golang services. This provides per-application consumer lag, throughput, and error rates without requiring each team to instrument independently. The Vector log shipper routes application and infrastructure logs to Kafka (or WarpStream) for forwarding to Humio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WarpStream Agent Groups for multi-tenant isolation.&lt;/strong&gt; Within the logging WarpStream cluster, Agent Groups isolate traffic classes: VIP clients, SSL/TLS connections, and plaintext connections each run in separate groups. This prevents a high-throughput producer from saturating broker resources shared with other clients.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flink checkpoint management.&lt;/strong&gt; The Apache flink-kubernetes-operator had stability issues when reconciling more than 100 concurrent Flink deployments simultaneously. Robinhood tuned the operator’s concurrent reconciliation configuration and built a custom Checkpoint-Fetcher tool that automates locating the latest valid Flink checkpoint and updating the &lt;code&gt;initialSavepointPath&lt;/code&gt; field in the Kubernetes manifest. This reduces human error during recovery and rollback operations. Automatic rollback to the last healthy application version via health checks is also implemented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Confluent Platform usage.&lt;/strong&gt; Chandra Kuchi confirmed in a September 2024 interview that Robinhood uses Confluent CP. The current status of Confluent’s commercial components post-Kubernetes migration has not been detailed in public sources.&lt;/p&gt;
&lt;p&gt;Note: Robinhood’s SRE team has not published details of Kafka alerting thresholds, SLO definitions, quota management, or CI/CD pipelines for topic provisioning in any source reviewed for this article.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Python connection fan-in at broker scale.&lt;/strong&gt; Robinhood’s Python services use the gunicorn process-based worker model. Each worker process opens independent Kafka connections, and at hundreds of workers per cluster the broker inbound connection count exceeded 100,000 on the largest cluster, creating resource exhaustion and connection management overhead. The solution was kafkaproxy: a Kubernetes sidecar that aggregates all connections from a pod’s worker pool into a shared set, dramatically reducing broker-side connection counts. A second generation of the proxy (in Java) later extended this to full consumer lifecycle management via gRPC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Divergent Kafka client libraries across languages.&lt;/strong&gt; Separate Kafka client wrappers for Python and Golang were drifting apart in defaults, error handling, and observability. kafkahood standardised client behaviour across languages with a single librdkafka-based library. The v2 consumer proxy went further, centralising the consumer group logic in one Java implementation that serves both Golang and Python application containers via gRPC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Failed consumer events with no safe retry path.&lt;/strong&gt; Schema mismatches and deserialization errors cause consumer events to fail permanently unless there is a way to inspect and fix the message before replaying it. A secondary Kafka DLQ topic does not provide sufficient tooling for this. Robinhood built a Postgres-backed DLQ with a CLI for inspection and targeted republishing to a retry topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data lake freshness of 24 hours.&lt;/strong&gt; Batch ingestion from Postgres into S3 meant analytics consumers worked with day-old data. The CDC pipeline (Debezium, Confluent Schema Registry, Kafka, Apache Hudi Deltastreamer with exactly-once writes) reduced freshness to 5-15 minutes across thousands of Postgres tables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7x brokerage traffic growth in six months.&lt;/strong&gt; Requests grew from 100k to 750k per second between December 2019 and June 2020, overwhelming a single-shard brokerage system. Application-level sharding distributed load across Postgres shards, each with its own Kafka consumers. High-throughput flows received shard-specific Kafka topics to eliminate per-message filtering overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Logging infrastructure cost and elasticity.&lt;/strong&gt; Self-managed Kafka clusters sized for peak market-hours throughput sat largely idle in evenings and weekends, and incurred inter-AZ data transfer fees continuously. Migrating to WarpStream eliminated the inter-AZ networking cost entirely (99% reduction) and allowed per-AZ autoscaling to track actual throughput. Total cost fell 45%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Managing 100+ concurrent Flink deployments.&lt;/strong&gt; The Apache flink-kubernetes-operator struggled with concurrent reconciliation at this scale, and manual checkpoint management introduced human error during recovery. Robinhood tuned the operator’s concurrency settings, built the Checkpoint-Fetcher tool to automate manifest updates, and implemented health-check-based automatic rollback.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role at Robinhood&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka (Confluent Platform)&lt;/td&gt;
&lt;td&gt;Primary event broker across equities trading, crypto, self-clearing, market data, notifications, fraud detection, data lake ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message broker (logging)&lt;/td&gt;
&lt;td&gt;WarpStream&lt;/td&gt;
&lt;td&gt;Diskless, S3-backed Kafka-compatible broker for logging workloads; adopted 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Confluent Schema Registry&lt;/td&gt;
&lt;td&gt;Schema management and Avro encoding for CDC change records from Debezium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing (Python)&lt;/td&gt;
&lt;td&gt;Faust (open-sourced by Robinhood)&lt;/td&gt;
&lt;td&gt;Risk/fraud detection, order quality monitoring, newsfeed aggregation, ad tracking, market data feed, crypto feed, event logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing (JVM)&lt;/td&gt;
&lt;td&gt;Apache Flink&lt;/td&gt;
&lt;td&gt;Stateful processing: fraud detection, shareholder position tracking, investor eligibility; 100+ concurrent deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka client wrapper&lt;/td&gt;
&lt;td&gt;kafkahood (internal)&lt;/td&gt;
&lt;td&gt;librdkafka-based library for Python and Golang; standardised defaults, client metrics, DLQ support, feature-flag rollouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer proxy&lt;/td&gt;
&lt;td&gt;kafkaproxy / Consumer Proxy (internal)&lt;/td&gt;
&lt;td&gt;Kubernetes sidecar (v1: Rust; v2: Java + gRPC) managing Kafka consumer lifecycle and connection aggregation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDC connector&lt;/td&gt;
&lt;td&gt;Debezium (Kafka Connect)&lt;/td&gt;
&lt;td&gt;Captures WAL from Postgres RDS; writes Avro-encoded change records to Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake ingestion&lt;/td&gt;
&lt;td&gt;Apache Hudi (Deltastreamer)&lt;/td&gt;
&lt;td&gt;Exactly-once incremental writes from Kafka into S3 data lake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake archival&lt;/td&gt;
&lt;td&gt;Secor (Pinterest)&lt;/td&gt;
&lt;td&gt;Archives Kafka streams to S3 for batch processing path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing&lt;/td&gt;
&lt;td&gt;Apache Spark&lt;/td&gt;
&lt;td&gt;Batch workloads on the data lake; also used for Hudi ingestion jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query engine&lt;/td&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;SQL queries over the S3 data lake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflow orchestration&lt;/td&gt;
&lt;td&gt;Apache Airflow&lt;/td&gt;
&lt;td&gt;Orchestrates data pipeline jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes operator&lt;/td&gt;
&lt;td&gt;Apache Flink Kubernetes Operator&lt;/td&gt;
&lt;td&gt;Manages 100+ concurrent Flink application deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log shipping&lt;/td&gt;
&lt;td&gt;Vector (Datadog)&lt;/td&gt;
&lt;td&gt;Produces application and infrastructure logs to Kafka / WarpStream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log storage and search&lt;/td&gt;
&lt;td&gt;Humio&lt;/td&gt;
&lt;td&gt;Receives logs from the Kafka/WarpStream pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embedded state store&lt;/td&gt;
&lt;td&gt;RocksDB&lt;/td&gt;
&lt;td&gt;Local state store used by Faust for stateful stream processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async Kafka client&lt;/td&gt;
&lt;td&gt;aiokafka (Robinhood fork)&lt;/td&gt;
&lt;td&gt;Async Kafka client used by Faust; maintained as a fork by Robinhood&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object storage&lt;/td&gt;
&lt;td&gt;AWS S3&lt;/td&gt;
&lt;td&gt;Data lake primary storage; backing store for WarpStream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary database&lt;/td&gt;
&lt;td&gt;AWS RDS (Postgres)&lt;/td&gt;
&lt;td&gt;Source for CDC pipeline; also backing store for Postgres DLQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Hosts all services, Kafka clusters (post-2024 migration), and Flink deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metastore&lt;/td&gt;
&lt;td&gt;Apache Hive Metastore&lt;/td&gt;
&lt;td&gt;Schema management for data lake tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster manager&lt;/td&gt;
&lt;td&gt;Apache Hadoop YARN&lt;/td&gt;
&lt;td&gt;Cluster management for Spark jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jaren Glover&lt;/td&gt;
&lt;td&gt;Ops engineer, Robinhood (c. 2015-2019)&lt;/td&gt;
&lt;td&gt;Stabilised and scaled Kafka infrastructure from 100,000 to 10 million users; presented at SREcon Americas 2019 and Kafka Summit 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask Solem&lt;/td&gt;
&lt;td&gt;Software engineer, Robinhood&lt;/td&gt;
&lt;td&gt;Created Faust, the Python stream-processing library built on Kafka; open-sourced July 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vineet Goel&lt;/td&gt;
&lt;td&gt;Software engineer, Robinhood&lt;/td&gt;
&lt;td&gt;Co-authored the Faust introductory blog post and the “Adding Faust to your Existing Architecture” post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chandra Kuchi&lt;/td&gt;
&lt;td&gt;Engineering Manager, Data Infrastructure Lead, Streaming Platform&lt;/td&gt;
&lt;td&gt;Presented “Taming a Massive Fleet of Python-based Kafka Apps at Robinhood” at Kafka Summit Americas 2021; cited in Diginomica (2024) on Robinhood’s Kafka and Flink architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nick Dellamaggiore&lt;/td&gt;
&lt;td&gt;Software engineer, Kafka Infrastructure, Robinhood&lt;/td&gt;
&lt;td&gt;Co-presented at Kafka Summit Americas 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tony Chen&lt;/td&gt;
&lt;td&gt;Software engineer, Streaming Platform, Robinhood&lt;/td&gt;
&lt;td&gt;Presented “Robinhood’s Kafkaproxy” at Current 2023 and “Robinhood’s Flink Deployment Practices” at Current 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mun Yong Jang&lt;/td&gt;
&lt;td&gt;Software engineer, Streaming Platform, Robinhood&lt;/td&gt;
&lt;td&gt;Co-presented kafkaproxy at Current 2023; co-presented “Robinhood’s Kafka Journey from EC2 to Kubernetes” at Current 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nathan Moderwell&lt;/td&gt;
&lt;td&gt;Software engineer, Robinhood&lt;/td&gt;
&lt;td&gt;Co-presented Kafka EC2-to-Kubernetes migration at Current 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sreeram Ramji&lt;/td&gt;
&lt;td&gt;Senior Staff Software Engineer, Data Infra and Experimentation&lt;/td&gt;
&lt;td&gt;Co-presented “Dead Letter Queues for Kafka Consumers in Robinhood” at Current 2022; owns Kafka, Flink, Spark, Trino, Airflow, and Data Lake infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wenlong Xiong&lt;/td&gt;
&lt;td&gt;Software engineer, Streaming Platform and Batch Compute (formerly)&lt;/td&gt;
&lt;td&gt;Co-presented DLQ architecture at Current 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ethan Chen&lt;/td&gt;
&lt;td&gt;Software engineer, Robinhood&lt;/td&gt;
&lt;td&gt;Co-presented WarpStream logging migration at Current New Orleans 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Renan Rueda&lt;/td&gt;
&lt;td&gt;Software engineer, Robinhood&lt;/td&gt;
&lt;td&gt;Co-presented WarpStream logging migration at Current New Orleans 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stephen Chang&lt;/td&gt;
&lt;td&gt;Engineering Manager, Payments, Robinhood&lt;/td&gt;
&lt;td&gt;Authored “Building a Resilient Card Transaction System” (2022), covering Kafka’s role in backup payment authorisation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edmond Wong and Nathan Ziebart&lt;/td&gt;
&lt;td&gt;Technical Leads, Brokerage Engineering, Robinhood&lt;/td&gt;
&lt;td&gt;Co-authored “How we scaled Robinhood’s brokerage system for greater reliability” (2021), covering shard-aware Kafka filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Connection fan-in is a real problem for process-based concurrency models.&lt;/strong&gt; If you run Python services with gunicorn or uWSGI and connect them directly to Kafka, each worker process opens its own connections. At a few hundred workers per cluster this adds up to tens of thousands of inbound connections on your brokers. A sidecar proxy that aggregates connections from a single pod is one approach; Robinhood also considered async client libraries but chose the proxy model for the isolation benefits it provided.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Decoupling consumer lifecycle from application code simplifies failure handling.&lt;/strong&gt; Robinhood’s Consumer Proxy moves all consumer group logic, rebalancing, commits, and timeouts into a separate sidecar. Your application container receives messages over gRPC only after they have been successfully consumed. This means application failures do not stall consumer groups, and consumer group rebalances triggered by scaling do not introduce latency into your application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A Postgres-backed dead letter queue gives you more repair tooling than a DLQ topic.&lt;/strong&gt; A secondary Kafka topic stores failed messages but does not give you a way to query them by failure reason, apply a fix, or selectively replay a subset. If your teams share schema contracts and schema mismatches are a real failure mode, the ability to inspect and fix records before replay is worth the additional infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Separating workloads by their latency and cost profiles pays off at scale.&lt;/strong&gt; Robinhood’s logging traffic and its trading traffic have very different latency requirements: 0.45 seconds is acceptable for logs but not for order routing. Migrating logging to WarpStream (with higher latency but lower cost) while keeping trading on self-managed Kafka let the team optimise each cluster for its actual workload, producing a 45% cost reduction on the logging side without touching the trading path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A standardised internal Kafka client library is worth the investment early.&lt;/strong&gt; kafkahood encodes sane defaults, observability, DLQ support, and feature-flag-controlled rollouts for all services. Teams get correct client behaviour without needing to understand all the underlying librdkafka configuration options. The payoff is proportional to the number of services you run: at 14+ million users and hundreds of services, a single misconfigured default can cause widespread consumer lag or data loss.&lt;/p&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;th&gt;Author(s)&lt;/th&gt;
&lt;th&gt;Venue&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood swaps Kafka for WarpStream to tame logging workloads and costs&lt;/td&gt;
&lt;td&gt;Ethan Chen, Renan Rueda (Robinhood); Jason Lauritzen (WarpStream)&lt;/td&gt;
&lt;td&gt;WarpStream blog / Current New Orleans 2025&lt;/td&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taming a Massive Fleet of Python-based Kafka Apps at Robinhood&lt;/td&gt;
&lt;td&gt;Chandra Kuchi, Nick Dellamaggiore (Robinhood)&lt;/td&gt;
&lt;td&gt;Kafka Summit Americas 2021&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood’s Kafkaproxy: Decoupling Kafka Consumer Logic from Application Business Logic&lt;/td&gt;
&lt;td&gt;Tony Chen, Mun Yong Jang (Robinhood)&lt;/td&gt;
&lt;td&gt;Confluent Current 2023&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dead Letter Queues for Kafka Consumers in Robinhood&lt;/td&gt;
&lt;td&gt;Sreeram Ramji, Wenlong Xiong (Robinhood)&lt;/td&gt;
&lt;td&gt;Confluent Current 2022&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faust: Stream Processing for Python&lt;/td&gt;
&lt;td&gt;Ask Solem, Vineet Goel (Robinhood)&lt;/td&gt;
&lt;td&gt;Robinhood Engineering / Medium&lt;/td&gt;
&lt;td&gt;2018-07-31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood’s Kafka Journey from EC2 to Kubernetes&lt;/td&gt;
&lt;td&gt;Mun Yong Jang, Nathan Moderwell (Robinhood)&lt;/td&gt;
&lt;td&gt;Confluent Current 2024&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood’s Flink deployment practices&lt;/td&gt;
&lt;td&gt;Tony Chen (Robinhood)&lt;/td&gt;
&lt;td&gt;Confluent Current 2024&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake at Robinhood&lt;/td&gt;
&lt;td&gt;Robinhood Engineering&lt;/td&gt;
&lt;td&gt;Robinhood Newsroom&lt;/td&gt;
&lt;td&gt;2019-09-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robinhood invests in a stable and scalable future with Confluent&lt;/td&gt;
&lt;td&gt;Derek du Preez (interviewing Chandra Kuchi)&lt;/td&gt;
&lt;td&gt;Diginomica&lt;/td&gt;
&lt;td&gt;2024-09-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling Robinhood crypto systems&lt;/td&gt;
&lt;td&gt;Chirantan Mahipal, Hefu Chai, Xuan Zhang (Robinhood)&lt;/td&gt;
&lt;td&gt;Robinhood Newsroom&lt;/td&gt;
&lt;td&gt;2022-09-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fresher data lake on AWS S3&lt;/td&gt;
&lt;td&gt;Robinhood Engineering&lt;/td&gt;
&lt;td&gt;Robinhood Newsroom&lt;/td&gt;
&lt;td&gt;2022-02-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Building a resilient card transaction system&lt;/td&gt;
&lt;td&gt;Stephen Chang (Robinhood)&lt;/td&gt;
&lt;td&gt;Robinhood Newsroom&lt;/td&gt;
&lt;td&gt;2022-10-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How we scaled Robinhood’s brokerage system for greater reliability&lt;/td&gt;
&lt;td&gt;Edmond Wong, Nathan Ziebart (Robinhood)&lt;/td&gt;
&lt;td&gt;Robinhood Newsroom&lt;/td&gt;
&lt;td&gt;2021-06-25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faust: streaming at Robinhood with Ask Solem&lt;/td&gt;
&lt;td&gt;SE Daily (interview with Ask Solem)&lt;/td&gt;
&lt;td&gt;Software Engineering Daily&lt;/td&gt;
&lt;td&gt;2018-09-05&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;If you are running Kafka in production and want visibility into consumer lag, throughput, and cluster health across your brokers, give &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; a try with a free 30-day trial. You can connect it to any Kafka cluster in minutes and deploy it via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Spotify used Apache Kafka in production</title><link>https://factorhouse.io/articles/spotify-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/spotify-kafka-architecture/</guid><description>A deep-dive into Spotify&apos;s Kafka architecture — covering their event delivery system, 700K events/second scale, engineering decisions, and why they ultimately migrated to Google Cloud Pub/Sub.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Between roughly 2013 and 2017, Spotify ran one of the more thoroughly documented &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; deployments in the industry. At peak, their event delivery system processed 700,000 events per second across five datacenters and fed every major feature on the platform — Discover Weekly, Year in Music, Spotify Wrapped, and Billboard’s streaming charts all relied on data moving through Kafka. In 2017, Spotify decommissioned their Kafka infrastructure entirely, migrating to Google Cloud Pub/Sub as part of a broader move to Google Cloud Platform. What makes this story worth reading is what happened in between: the architectural decisions Spotify made, the specific failure modes they encountered at scale, and the engineering trade-offs that led them to walk away from Kafka for a managed alternative.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Spotify is a music and podcast streaming service founded in Stockholm in 2006. By 2015, when much of the documented Kafka work was underway, the platform had 60 million monthly active users, 30 million songs, and 1.5 billion playlists. By early 2016, that had grown to 100 million monthly active users across 61 markets.&lt;/p&gt;
&lt;p&gt;The company adopted Kafka around 2013 to replace an ad-hoc log-shipping system that tailed files from service hosts and sent them toward a central Hadoop cluster. Event volume was growing fast enough that iterating on the existing system was no longer practical, and the team needed a message bus that could handle cross-datacenter delivery at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Kafka milestones:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;~2013&lt;/td&gt;
&lt;td&gt;Adopted Apache Kafka 0.7 for event delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;January 2015&lt;/td&gt;
&lt;td&gt;Published Storm/Kafka personalization architecture — 3 billion+ events/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;Production peak: 700,000 events/second across 5 datacenters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015–2016&lt;/td&gt;
&lt;td&gt;Evaluated Kafka 0.8 with MirrorMaker; encountered data loss and producer failure issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 2016&lt;/td&gt;
&lt;td&gt;Announced migration to Google Cloud Pub/Sub; load-tested replacement at 2 million messages/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February 2017&lt;/td&gt;
&lt;td&gt;Kafka system fully decommissioned; Cloud Pub/Sub live in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q1 2019&lt;/td&gt;
&lt;td&gt;Post-migration: 8 million events/second peak, 500 billion events/day, 350+ TB raw data/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2021&lt;/td&gt;
&lt;td&gt;EDI v3: 600+ event types, 70 TB/day compressed, Dataflow replacing Dataproc&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;spotifys-kafka-use-cases&quot;&gt;Spotify’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Kafka sat at the centre of Spotify’s event delivery infrastructure (EDI) and served as the source of truth for all user interaction data. Every time a user played a song, skipped a track, viewed an ad, or performed a search, the client emitted an event that made its way through a Kafka producer, into a broker, and out to one or more downstream consumers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event delivery for analytics and features:&lt;/strong&gt; The primary pipeline delivered event data to a central Hadoop cluster, where hourly Crunch/MapReduce ETL jobs converted raw tab-separated text into Apache Avro format for downstream analytics. This data fed Discover Weekly, Year in Music, and Spotify Wrapped, as well as Billboard’s streaming charts and the internal A/B testing infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time personalization via Apache Storm:&lt;/strong&gt; In parallel with the batch path, Apache Storm topologies consumed from Kafka topics in real time. Topics carried event types including song completions and ad impressions. Each Storm topology subscribed to relevant topics, enriched events with entity metadata fetched from Cassandra (for example, attaching the genre of a completed song), grouped events by user, and computed user attributes for recommendation and ad-targeting models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log aggregation across data centers:&lt;/strong&gt; Spotify operated four owned data centers plus capacity on Google Cloud Platform during this period. Kafka served as the collection point for service logs from all hosts, with a custom component handling cross-datacenter batching and forwarding.&lt;/p&gt;
&lt;p&gt;Ownership was split along functional lines: the event delivery team owned the Kafka infrastructure and producer daemons, while feature teams (personalization, ads, data engineering) owned the Storm topologies and downstream consumers.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;During the Kafka era, production throughput peaked at 700,000 events per second. The team load-tested a replacement system at 2,000,000 events per second — roughly triple their production peak — before committing to the migration.&lt;/p&gt;
&lt;p&gt;The Storm cluster processing real-time events from Kafka consisted of 6 nodes with 24 cores per host and processed over 3 billion events per day by January 2015. Spotify ran this infrastructure across five datacenters simultaneously, with cross-datacenter forwarding handled by the Grouper component described below.&lt;/p&gt;
&lt;p&gt;After the migration to Google Cloud Pub/Sub, the same event volume continued to grow:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2014–2015 (Kafka)&lt;/td&gt;
&lt;td&gt;Peak production throughput&lt;/td&gt;
&lt;td&gt;700,000 events/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015 (Kafka)&lt;/td&gt;
&lt;td&gt;Real-time pipeline daily volume&lt;/td&gt;
&lt;td&gt;3+ billion events/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2017 (Pub/Sub)&lt;/td&gt;
&lt;td&gt;Daily event volume&lt;/td&gt;
&lt;td&gt;100 billion events/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q1 2019 (Pub/Sub)&lt;/td&gt;
&lt;td&gt;Peak throughput&lt;/td&gt;
&lt;td&gt;8 million events/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q1 2019 (Pub/Sub)&lt;/td&gt;
&lt;td&gt;Daily raw data&lt;/td&gt;
&lt;td&gt;350+ TB/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q1 2019 (Pub/Sub)&lt;/td&gt;
&lt;td&gt;Daily event volume&lt;/td&gt;
&lt;td&gt;500 billion events/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021 (Pub/Sub + Dataflow)&lt;/td&gt;
&lt;td&gt;Distinct event types&lt;/td&gt;
&lt;td&gt;600+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021 (Pub/Sub + Dataflow)&lt;/td&gt;
&lt;td&gt;Daily compressed data&lt;/td&gt;
&lt;td&gt;70 TB/day&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The post-migration growth is notable: peak throughput went from 700K to 8 million events/second — more than an 11x increase — in roughly two years, without Kafka in the stack.&lt;/p&gt;
&lt;h2 id=&quot;spotifys-kafka-architecture&quot;&gt;Spotify’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;high-level-architecture&quot;&gt;High-level architecture&lt;/h3&gt;
&lt;p&gt;The event delivery system had four principal components: a Syslog Producer on every service host, Kafka Brokers receiving those events, custom Grouper services for cross-datacenter forwarding, and an HDFS Consumer persisting data to Hadoop.&lt;/p&gt;
&lt;p&gt;Apache Storm topologies ran alongside this, consuming from Kafka topics directly for real-time processing, with Cassandra serving as the metadata and user-profile store downstream.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;A daemon called the &lt;strong&gt;Kafka Syslog Producer&lt;/strong&gt; ran on every service host. Its role was to tail log files and batch log lines toward the Kafka cluster. Events at this stage were treated as unstructured lines regardless of type — there was no schema enforcement at the producer level. The producer maintained a checkpoint (end-of-file marker) per log file to track delivery state; missing markers after host failure required manual intervention.&lt;/p&gt;
&lt;p&gt;Batching and compression were applied at the Grouper stage rather than the producer, which simplified the per-host daemon but pushed cross-datacenter optimisation downstream.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Two consumer patterns ran in parallel from the same Kafka topics:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Batch consumers&lt;/strong&gt; wrote events to HDFS for processing by hourly Crunch/MapReduce jobs. These converted tab-separated raw event data into Apache Avro format, partitioned by hour.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time consumers&lt;/strong&gt; used Kafka Consumer Spouts within Apache Storm topologies. Each topology version used a unique Kafka consumer group ID so that old and new versions could consume the same topic simultaneously during deployments. This allowed operators to run both versions in parallel and roll back without losing events. The team also tuned &lt;code&gt;rebalancing.max.tries&lt;/code&gt; to reduce consumer rebalancing errors under their workload.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;Liveness Monitor&lt;/strong&gt; tracked which service hosts were active during each hour by querying service discovery systems. This enabled the batch consumers to verify that all expected events for a given hour had been received before marking the partition complete.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Storm handled real-time event processing in Spotify’s personalization and ad-targeting pipelines. Storm topologies subscribed to Kafka topics (e.g. song completions, ad impressions), fetched entity metadata from Cassandra, grouped events per user, and computed user attributes that fed recommendation and targeting models.&lt;/p&gt;
&lt;p&gt;The 6-node Storm cluster (24 cores/host) processed over 3 billion events per day by early 2015 and supported use cases including Discover Weekly personalisation, ad targeting, and data visualisation.&lt;/p&gt;
&lt;h3 id=&quot;the-grouper-cross-datacenter-forwarding&quot;&gt;The Grouper: cross-datacenter forwarding&lt;/h3&gt;
&lt;p&gt;Spotify built a custom component called the &lt;strong&gt;Grouper&lt;/strong&gt; that consumed all event streams from a local datacenter and republished them as a single, compressed, efficiently batched topic for cross-datacenter transmission. Without the Grouper, raw Kafka streams would have saturated cross-datacenter links. The Grouper also allowed the team to enforce batching and compression consistently at the datacenter boundary rather than at each individual producer.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Dual consumer groups for safe topology rollouts:&lt;/strong&gt; Each new Storm topology version was deployed with a unique Kafka consumer group ID. Both the old and new topology versions consumed the full message stream simultaneously during a rollout window. If the new version misbehaved, operators could roll back without message loss and without manual reprocessing. This pattern decoupled deployment risk from data continuity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom Grouper for cross-datacenter efficiency:&lt;/strong&gt; Rather than forwarding per-topic streams cross-datacenter, the Grouper consumed multiple topics locally, merged and compressed them, and republished a single efficient stream. This was a pragmatic response to both bandwidth constraints and Kafka 0.7’s lack of native cross-datacenter replication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Custom async-google-pubsub-client:&lt;/strong&gt; When migrating to Cloud Pub/Sub, the official Google Java client could not meet Spotify’s throughput requirements. The team built an open-source, high-performance async Java client — &lt;code&gt;async-google-pubsub-client&lt;/code&gt; — which ran in production for over a year before Google’s official libraries reached comparable performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Isolation per event type (post-Kafka lesson):&lt;/strong&gt; A direct lesson from the Kafka era’s shared-channel problems was applied in the Pub/Sub architecture: each of 500+ event types received its own topic, ETL process, and final storage location. This prevented high-volume events from disrupting business-critical ones, and let the team assign different SLOs per event type (hours for high priority, up to 72 hours for low priority).&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;Spotify’s Kafka deployment was entirely self-managed, running on owned datacenter infrastructure and configured via Puppet. The system spanned five datacenters, with the Grouper component managing cross-datacenter forwarding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observability:&lt;/strong&gt; The team monitored consumer group lag and rebalancing events, and tuned &lt;code&gt;rebalancing.max.tries&lt;/code&gt; to reduce noise from regular rebalancing errors. A dedicated Liveness Monitor tracked active service hosts by hour to detect gaps in event delivery before they became incidents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment and upgrades:&lt;/strong&gt; Configuration changes were deployed via Puppet. The tight coupling between system components meant that even small changes to one component could cause system-wide outages, which the team described as difficult to recover from. There was no clean way to iterate on individual components in isolation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Incident response:&lt;/strong&gt; Missing end-of-file markers after host failure required manual intervention to unblock the Liveness Monitor and resume processing for the affected hour. The lack of automated recovery for this scenario was one of the documented pain points of the v1 architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience:&lt;/strong&gt; Spotify open-sourced &lt;code&gt;docker-kafka&lt;/code&gt; (a combined Kafka and ZooKeeper Docker image) to give developers a local Kafka environment for testing. The repository is now archived.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;kafka-07-had-no-reliable-broker-level-persistence&quot;&gt;Kafka 0.7 had no reliable broker-level persistence&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Kafka 0.7 did not support broker-level replication. All reliable persistence lived in Hadoop, making it the single point of failure for the entire event delivery system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Architectural limitation of the Kafka version in use. Broker-level replication was not available until later releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The team accepted the limitation and designed the system around HDFS as the persistence layer. This worked until the coupling between Hadoop availability and event delivery became operationally unacceptable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The design constraint accumulated into the technical debt that made the eventual migration necessary.&lt;/p&gt;
&lt;h3 id=&quot;cross-datacenter-delivery-at-700k-eventssecond&quot;&gt;Cross-datacenter delivery at 700K events/second&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Producers across five datacenters needed to deliver events to a central Hadoop cluster. Raw event streams would have exhausted cross-datacenter bandwidth, and producers needed distant datacenter acknowledgement before considering delivery complete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; No native cross-datacenter replication in Kafka 0.7; bandwidth constraints at datacenter links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Built the custom Grouper component to consume event streams locally, compress and batch them, and forward a single efficient stream per datacenter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; The Grouper reduced cross-datacenter bandwidth and improved delivery reliability, at the cost of a custom component that the team had to maintain and operate.&lt;/p&gt;
&lt;h3 id=&quot;kafka-08-mirrormaker-silently-dropped-data&quot;&gt;Kafka 0.8 MirrorMaker silently dropped data&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; When the team evaluated upgrading to Kafka 0.8, MirrorMaker instances dropped data while reporting to the source cluster that mirroring had succeeded. The producer also required full service restarts after failure, with no automated recovery path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Bugs in the MirrorMaker coordination logic in the Kafka 0.8 version under evaluation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Abandoned the Kafka 0.8 path entirely. Rather than invest further in debugging MirrorMaker, the team migrated to Google Cloud Pub/Sub and built a custom high-throughput Java client (&lt;code&gt;async-google-pubsub-client&lt;/code&gt;) for the migration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; A five-day consumer stability test of the Pub/Sub system showed a median end-to-end latency of 20 seconds with zero message loss. The Kafka system was decommissioned in February 2017.&lt;/p&gt;
&lt;h3 id=&quot;no-quality-of-service-differentiation-between-event-types&quot;&gt;No quality-of-service differentiation between event types&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; All event types shared the same delivery channel. A surge in high-volume, low-priority events could degrade delivery of business-critical event types, with no mechanism to prioritise one over another.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The single shared Kafka pipeline had no per-event-type QoS controls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Pub/Sub architecture assigned each event type its own topic, ETL process, and storage location. Priority tiers determined SLOs: High (few hours), Normal (24 hours), Low (72 hours).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; By 2019, the team could state that “a noisy, broken, or blocked event type will not halt the rest of the system.”&lt;/p&gt;
&lt;h3 id=&quot;tight-component-coupling-blocked-iteration&quot;&gt;Tight component coupling blocked iteration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Even small changes to one component caused system-wide outages that were hard to diagnose and recover from. The team could not improve the system incrementally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; All components were tightly coupled. The Hadoop dependency for persistence, the Grouper’s cross-datacenter role, and the producer’s manual checkpoint logic created a fragile dependency graph.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A full system redesign, not an iterative fix. The new architecture decomposed the system into independent microservices with explicit interfaces, enabling independent deployment and failure isolation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; By 2019, the event delivery system comprised approximately 15 microservices across ~2,500 VMs, each independently deployable.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker (Kafka era)&lt;/td&gt;
&lt;td&gt;Apache Kafka 0.7, evaluated 0.8&lt;/td&gt;
&lt;td&gt;Decommissioned February 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message broker (post-Kafka)&lt;/td&gt;
&lt;td&gt;Google Cloud Pub/Sub&lt;/td&gt;
&lt;td&gt;Per-event-type topics; 7-day retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing (Kafka era)&lt;/td&gt;
&lt;td&gt;Apache Storm&lt;/td&gt;
&lt;td&gt;6-node cluster, 24 cores/host; consumed from Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream/batch processing (post-Kafka)&lt;/td&gt;
&lt;td&gt;Scio (Scala API for Apache Beam) on Google Dataflow&lt;/td&gt;
&lt;td&gt;Unified batch and streaming; open-sourced by Spotify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Applied via hourly ETL; no schema enforcement at Kafka producer level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (Kafka era)&lt;/td&gt;
&lt;td&gt;HDFS on Hadoop&lt;/td&gt;
&lt;td&gt;Single point of failure in original architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing (Kafka era)&lt;/td&gt;
&lt;td&gt;Crunch/MapReduce on Hadoop&lt;/td&gt;
&lt;td&gt;Hourly Avro ETL from HDFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (post-Kafka)&lt;/td&gt;
&lt;td&gt;Google Cloud Storage, BigQuery&lt;/td&gt;
&lt;td&gt;GCS for hourly immutable partitions; BQ for analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Apache Cassandra&lt;/td&gt;
&lt;td&gt;User profile attributes and entity metadata for Storm enrichment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination (Kafka era)&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;Kafka dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration management&lt;/td&gt;
&lt;td&gt;Puppet&lt;/td&gt;
&lt;td&gt;Replaced as part of broader cloud migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local development (Kafka era)&lt;/td&gt;
&lt;td&gt;spotify/docker-kafka (Docker)&lt;/td&gt;
&lt;td&gt;Combined Kafka + ZooKeeper image; now archived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal container orchestration&lt;/td&gt;
&lt;td&gt;Helios&lt;/td&gt;
&lt;td&gt;Spotify’s precursor to Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute infrastructure&lt;/td&gt;
&lt;td&gt;Google Compute Engine, Regional Managed Instance Groups&lt;/td&gt;
&lt;td&gt;Post-migration; approximately 2,500 VMs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deduplication&lt;/td&gt;
&lt;td&gt;Google Dataproc, later Google Dataflow&lt;/td&gt;
&lt;td&gt;Multi-week lookback deduplication using event message identifiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensitive data handling&lt;/td&gt;
&lt;td&gt;Google Dataflow&lt;/td&gt;
&lt;td&gt;In-flight encryption for events containing personal data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Igor Maravić&lt;/td&gt;
&lt;td&gt;Software Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Led the event delivery migration; authored the three-part “Road to the Cloud” blog series; presented at QCon New York 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neville Li&lt;/td&gt;
&lt;td&gt;Data Infrastructure Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Created Scio, Spotify’s Scala API for Apache Beam; presented at QCon New York 2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinshuk Mishra&lt;/td&gt;
&lt;td&gt;Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Published the Storm/Kafka personalization architecture (2015)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matt Brown&lt;/td&gt;
&lt;td&gt;Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Co-authored the Cassandra personalization post (2015)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bartosz Janota&lt;/td&gt;
&lt;td&gt;Data Infrastructure Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Co-authored “Life in the Cloud” retrospective (2019)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robert Stephenson&lt;/td&gt;
&lt;td&gt;Senior Product Manager, Spotify&lt;/td&gt;
&lt;td&gt;Co-authored the 2019 and 2021 EDI articles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flavio Santos&lt;/td&gt;
&lt;td&gt;Data Infrastructure Engineer, Spotify&lt;/td&gt;
&lt;td&gt;Co-authored the EDI v3 migration article (2021)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Operational burden compounds with scale.&lt;/strong&gt; Spotify’s Kafka setup functioned well at 700K events/second but required increasing amounts of custom work (the Grouper, manual EOF recovery, tight Puppet-based configuration) to hold together. If you are building on Kafka, account for the operational investment required as throughput grows, especially if you are running across multiple datacenters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-datacenter Kafka without native replication requires custom solutions.&lt;/strong&gt; Spotify built the Grouper specifically because Kafka 0.7 lacked reliable cross-datacenter capabilities. If you are running Kafka across regions today, verify which replication mechanisms you are relying on and test their failure modes explicitly — Spotify’s MirrorMaker evaluation is a reminder that silent data loss is a realistic failure mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-event-type isolation changes the operational model significantly.&lt;/strong&gt; Spotify’s shared Kafka pipeline had no QoS controls, which meant any event type could affect all others. The isolation-per-event-type pattern they adopted post-migration (one topic per event type, independent SLOs) is applicable with Kafka: topic-per-event-type design increases operational surface area but limits blast radius when a single consumer or producer misbehaves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managed services trade operational control for operational simplicity.&lt;/strong&gt; Spotify’s migration to Pub/Sub removed a significant operational burden and enabled 11x throughput growth without a proportional increase in infrastructure work. If you are evaluating self-managed Kafka against a managed alternative, Spotify’s trajectory is a concrete reference point for what that trade looks like at 700K+ events/second.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decouple deployment risk from data continuity.&lt;/strong&gt; Spotify’s dual consumer group pattern for Storm rollouts — where old and new topology versions consumed the same topic simultaneously — is a practical technique for any system where consumer logic changes frequently. It removes message loss as a rollback cost and lets you validate new consumers against live data before cutting over.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-further-reading-and-ctas&quot;&gt;Sources, further reading and CTAs&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Igor Maravić, &lt;a href=&quot;https://engineering.atspotify.com/2016/02/spotifys-event-delivery-the-road-to-the-cloud-part-i/&quot;&gt;Spotify’s Event Delivery – The Road to the Cloud (Part I)&lt;/a&gt;, Spotify Engineering, February 2016&lt;/li&gt;
&lt;li&gt;Igor Maravić, &lt;a href=&quot;https://engineering.atspotify.com/2016/03/spotifys-event-delivery-the-road-to-the-cloud-part-ii/&quot;&gt;Spotify’s Event Delivery – The Road to the Cloud (Part II)&lt;/a&gt;, Spotify Engineering, March 2016&lt;/li&gt;
&lt;li&gt;Kinshuk Mishra, &lt;a href=&quot;https://engineering.atspotify.com/2015/01/how-spotify-scales-apache-storm/&quot;&gt;How Spotify Scales Apache Storm&lt;/a&gt;, Spotify Engineering, January 2015&lt;/li&gt;
&lt;li&gt;Kinshuk Mishra and Matt Brown, &lt;a href=&quot;https://engineering.atspotify.com/2015/01/personalization-at-spotify-using-cassandra&quot;&gt;Personalization at Spotify using Cassandra&lt;/a&gt;, Spotify Engineering, January 2015&lt;/li&gt;
&lt;li&gt;Neville Li, &lt;a href=&quot;https://engineering.atspotify.com/2017/10/big-data-processing-at-spotify-the-road-to-scio-part-1/&quot;&gt;Big Data Processing at Spotify: The Road to Scio (Part 1)&lt;/a&gt;, Spotify Engineering, October 2017&lt;/li&gt;
&lt;li&gt;Bartosz Janota and Robert Stephenson, &lt;a href=&quot;https://engineering.atspotify.com/2019/11/spotifys-event-delivery-life-in-the-cloud/&quot;&gt;Spotify’s Event Delivery – Life in the Cloud&lt;/a&gt;, Spotify Engineering, November 2019&lt;/li&gt;
&lt;li&gt;Flavio Santos and Robert Stephenson, &lt;a href=&quot;https://engineering.atspotify.com/2021/10/changing-the-wheels-on-a-moving-bus-spotify-event-delivery-migration/&quot;&gt;Changing the Wheels on a Moving Bus&lt;/a&gt;, Spotify Engineering, October 2021&lt;/li&gt;
&lt;li&gt;Tino Tereshko, &lt;a href=&quot;https://cloud.google.com/blog/products/gcp/spotifys-journey-to-cloud-why-spotify-migrated-its-event-delivery-system-from-kafka-to-google-cloud-pubsub&quot;&gt;Spotify’s journey to cloud: why Spotify migrated its event delivery system from Kafka to Google Cloud Pub/Sub&lt;/a&gt;, Google Cloud Blog, March 2016&lt;/li&gt;
&lt;li&gt;Igor Maravić and Neville Li, &lt;a href=&quot;https://qconnewyork.com/ny2016/ny2016/presentation/streaming-data-Spotify.html&quot;&gt;Handling Streaming Data in Spotify&lt;/a&gt;, QCon New York 2016&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Continue reading:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you want to monitor and inspect your own Kafka clusters — consumer lag, topic throughput, partition health — give &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; a try. It connects to any Kafka cluster in minutes and is available for a free 30-day trial. You can deploy it via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How The New York Times uses Apache Kafka in production</title><link>https://factorhouse.io/articles/the-new-york-times-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/the-new-york-times-kafka-architecture/</guid><description>A deep-dive into The New York Times&apos; Kafka publishing pipeline — covering the Monolog architecture, single-partition design, Kafka Streams usage, and the engineering decisions behind treating Kafka as a permanent content store.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The New York Times is one of the largest English-language news publishers in the world, with a digital subscriber base in the millions and content operations spanning text, photography, video, and interactive journalism. Its publishing infrastructure must serve a website, iOS and Android applications, search, and personalisation systems — all simultaneously and with very low latency from the moment a journalist hits publish.&lt;/p&gt;
&lt;p&gt;The Times operated multiple legacy content management systems for decades, each built independently with different APIs and schemas. Content from the 1920s was digitised via OCR; content from the late 1990s lived in a CMS that bore no resemblance to the one used a decade later. By 2017, the organisation had reached a point where the inconsistency between these systems was creating real engineering cost: every downstream service had to understand each upstream API individually, schema changes caused inconsistencies, and bootstrapping a new system against the historical content archive meant making individual API calls at a scale that created unpredictable load.&lt;/p&gt;
&lt;p&gt;The trigger for adopting &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; was the decision to build a unified publishing pipeline. Kafka’s two distinguishing properties — infinite event retention and globally ordered consumption — made it the only viable platform for treating the content log as a permanent store rather than a transient queue.&lt;/p&gt;
&lt;h3 id=&quot;timeline&quot;&gt;Timeline&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-2017&lt;/td&gt;
&lt;td&gt;Multiple legacy CMS systems with inconsistent APIs; no unified content schema; full content archive inaccessible at scale without heavy API load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;Publishing Pipeline launched with Kafka as the Monolog; 166+ years of content archive stored in Kafka with infinite retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;September 2017&lt;/td&gt;
&lt;td&gt;Boerge Svingen, Director of Engineering, publishes detailed technical writeup on the Confluent blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2017&lt;/td&gt;
&lt;td&gt;Svingen presents “The Source of Truth” at Kafka Summit NYC 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;the-new-york-times-kafka-use-cases&quot;&gt;The New York Times’ Kafka use cases&lt;/h2&gt;
&lt;h3 id=&quot;content-publishing-and-distribution&quot;&gt;Content publishing and distribution&lt;/h3&gt;
&lt;p&gt;The primary use case is the publishing pipeline. When any piece of content is ready for publication, the producing service writes it to the Monolog — a single-partition Kafka topic that acts as the canonical record of everything the Times has ever published. Downstream consumers read from this topic to power their own systems.&lt;/p&gt;
&lt;p&gt;The downstream services include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The website and native iOS/Android applications, which need published content available with minimal latency after a journalist publishes&lt;/li&gt;
&lt;li&gt;The Elasticsearch cluster powering site search, which requires a denormalised view of content for indexing&lt;/li&gt;
&lt;li&gt;Personalisation systems, which need to reprocess recent content to build recommendations&lt;/li&gt;
&lt;li&gt;Content list services, both manually curated and query-driven&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before the Monolog, each of these systems maintained its own connection to upstream APIs. After the Monolog, they all consume from Kafka, with no knowledge of or dependency on the producing CMS.&lt;/p&gt;
&lt;h3 id=&quot;derived-views-the-denormalized-log-and-skinny-log&quot;&gt;Derived views: the Denormalized Log and Skinny Log&lt;/h3&gt;
&lt;p&gt;Two additional Kafka topics serve specific downstream needs:&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Denormalized Log&lt;/strong&gt; is a multi-partition topic created by the Denormalizer service (described in the architecture section). It republishes each top-level asset together with all of its resolved dependencies — so Elasticsearch ingestion nodes receive a complete, self-contained payload rather than having to chase references. It is partitioned by top-level asset URI, allowing parallel ingestion.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Skinny Log&lt;/strong&gt; carries lightweight processing notifications — signals that an asset has been processed and is available — without the full content payload. Downstream systems use it for cache invalidation and for tracking service level objective (SLO) metrics: end-to-end latency from publish event to downstream availability.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;p&gt;The New York Times’ Kafka deployment is not high-frequency by the standards of financial services or ride-sharing platforms. The content corpus is editorial: text, images, and associated metadata. As of 2017, the full archive — every published asset since 1851 — totalled less than 100GB and fit in a single Kafka topic on a single disk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total content corpus size&lt;/td&gt;
&lt;td&gt;Under 100GB (as of 2017)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical span&lt;/td&gt;
&lt;td&gt;All content since 1851 — 166+ years of archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monolog partitions&lt;/td&gt;
&lt;td&gt;1 (single partition for total ordering)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Denormalized Log partitions&lt;/td&gt;
&lt;td&gt;Multiple, partitioned by top-level asset URI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention policy&lt;/td&gt;
&lt;td&gt;Infinite&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The small corpus size is a deliberate consequence of the data model: normalisation means each asset is stored once, with references handled via URI rather than duplication. The single-partition Monolog fits comfortably on a single disk, which makes full log replay from offset 0 a practical operational tool rather than a theoretical capability.&lt;/p&gt;
&lt;h2 id=&quot;the-new-york-times-kafka-architecture&quot;&gt;The New York Times’ Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;the-monolog&quot;&gt;The Monolog&lt;/h3&gt;
&lt;p&gt;The Monolog is a single-partition Kafka topic with infinite retention. It holds every published asset in chronological order, with one critical constraint: assets are topologically sorted so that every dependency appears in the log before any asset that references it. An image shared across multiple articles is written once; each article references that image by URI. A consumer reading from offset 0 will always encounter an image before any article that embeds it.&lt;/p&gt;
&lt;p&gt;Assets are serialised as Protocol Buffer v3 (proto3) binaries. Every asset is identified by a structured URI: for example, &lt;code&gt;nyt://article/577d0341-9a0a-46df-b454-ea0718026d30&lt;/code&gt;. This uniform identifier scheme applies regardless of asset type, making the reference graph consistent and resolvable.&lt;/p&gt;
&lt;h3 id=&quot;the-gateway-service&quot;&gt;The gateway service&lt;/h3&gt;
&lt;p&gt;No producer writes directly to the Monolog. All writes pass through a gateway service that validates each asset against the protobuf schema before allowing it into the log. A custom linter enforces forward and backward compatibility on every schema change, ensuring that new field additions or type modifications do not silently break existing consumers. Schema enforcement at the write boundary keeps the log clean from the start rather than requiring downstream consumers to handle malformed messages defensively.&lt;/p&gt;
&lt;h3 id=&quot;the-denormalizer&quot;&gt;The Denormalizer&lt;/h3&gt;
&lt;p&gt;The Denormalizer is a Java application built on the Kafka Streams API. It consumes the Monolog and maintains a local state store of the latest version of every asset, along with the reference graph linking assets together.&lt;/p&gt;
&lt;p&gt;When a dependency is updated — for example, an image caption is corrected — the Denormalizer identifies every top-level asset that references that image and re-publishes those top-level assets to the Denormalized Log. This means downstream consumers such as Elasticsearch always receive the complete, current bundle of a top-level asset and all its dependencies in a single message, without needing to chase references themselves.&lt;/p&gt;
&lt;h3 id=&quot;elasticsearch-ingestion&quot;&gt;Elasticsearch ingestion&lt;/h3&gt;
&lt;p&gt;Each Elasticsearch ingestion node runs a Kafka Streams application that reads a partition of the Denormalized Log. It assembles JSON documents from the protobuf payloads and writes them to specific Elasticsearch shards. Because the Denormalized Log is partitioned by top-level asset URI, each ingestion node owns a deterministic subset of the content, enabling parallel ingestion without coordination between nodes.&lt;/p&gt;
&lt;h3 id=&quot;producer-and-consumer-architecture&quot;&gt;Producer and consumer architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Producers:&lt;/strong&gt; All content systems write through the gateway service. The gateway performs schema validation and then appends the asset to the single-partition Monolog. Producers are decoupled from all downstream consumers — they have no knowledge of how many consumers exist or what they do with the content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumers:&lt;/strong&gt; Each downstream system maintains its own consumer group and offset position in the relevant log (Monolog, Denormalized Log, or Skinny Log). Because Kafka retains events indefinitely, a consumer can always replay from offset 0 to rebuild its data store from scratch — which means stateless, immutable service deployments become practical.&lt;/p&gt;
&lt;h3 id=&quot;graphql-layer&quot;&gt;GraphQL layer&lt;/h3&gt;
&lt;p&gt;A GraphQL schema is automatically generated from the protobuf definitions. This means the Kafka data model and the consumer API contract stay in sync without manual schema maintenance. The GraphQL schema exposes the same typed structure as the protobuf messages, giving API consumers a self-documenting interface derived directly from the canonical data model.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;single-partition-topic-for-total-causal-ordering&quot;&gt;Single-partition topic for total causal ordering&lt;/h3&gt;
&lt;p&gt;The decision to use a single partition for the Monolog is the most deliberate architectural departure from typical Kafka practice. Multi-partition topics offer parallelism and higher throughput, but they cannot guarantee global ordering across partitions. The Monolog requires total ordering: a referenced asset must always be visible before the asset that references it. With multiple partitions, an article could appear in one partition before its referenced image appears in another, breaking consumers that try to resolve the dependency graph in order. A single partition makes this impossible.&lt;/p&gt;
&lt;p&gt;This is a reasonable trade-off given the data volume. Editorial content does not arrive at the rates that would make a single partition a throughput bottleneck.&lt;/p&gt;
&lt;h3 id=&quot;topological-sort-of-the-dependency-graph&quot;&gt;Topological sort of the dependency graph&lt;/h3&gt;
&lt;p&gt;Before an asset is written to the Monolog, it is sorted topologically within its dependency graph. The Times described it as ensuring “you always see a referenced asset before the asset doing the referencing.” This is not handled by Kafka itself — it is enforced by the producing system before the write. The result is a log where consumers can process messages in sequence without defensive look-ahead or buffering.&lt;/p&gt;
&lt;h3 id=&quot;immutable-service-deployments-via-log-replay&quot;&gt;Immutable service deployments via log replay&lt;/h3&gt;
&lt;p&gt;Because Kafka retains the full content history indefinitely, any downstream data store can be rebuilt from scratch by replaying from offset 0. The Times uses this property to enable immutable, stateless service deployments. Rather than running in-place database migrations when a schema or data change needs to be reflected downstream, a team can destroy its data store and replay the log. This eliminates a class of errors that arise from incremental state mutation over time.&lt;/p&gt;
&lt;h3 id=&quot;derived-logs-as-first-class-pipeline-artefacts&quot;&gt;Derived logs as first-class pipeline artefacts&lt;/h3&gt;
&lt;p&gt;The Denormalized Log and Skinny Log are not implementation details or optimisation hacks — they are first-class outputs of the pipeline, each designed to serve a specific downstream need. The Denormalized Log removes the reference-resolution burden from consumers that need complete assets. The Skinny Log gives lightweight notification consumers a low-overhead signal without the full payload. Designing explicit derived logs keeps individual consumer applications simple and reduces coupling between the core Monolog and the specific requirements of each downstream system.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model:&lt;/strong&gt; Kafka and ZooKeeper run on Google Cloud Platform (GCP) Compute instances. Application services — the gateway, the Denormalizer, and consumer applications — run in containers on Google Kubernetes Engine (GKE).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Service communication:&lt;/strong&gt; Inter-service communication uses gRPC over Cloud Endpoints. Kafka Consumer API connections are made over SSL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema governance:&lt;/strong&gt; The custom protobuf linter that runs at write time is the primary schema governance mechanism. It checks forward and backward compatibility on every schema change before allowing it into the gateway, which means breaking changes are caught before they reach the log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SLO tracking:&lt;/strong&gt; The Skinny Log provides the signal for measuring end-to-end publish latency — from the moment an asset is written to the Monolog to the moment downstream systems confirm it is available. This gives the team an observable measure of pipeline health tied to a user-facing outcome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operational lesson:&lt;/strong&gt; Boerge Svingen noted in the Software Engineering Daily interview that self-managing Kafka added meaningful operational burden. He indicated that a managed Kafka service would significantly reduce this overhead — a consideration worth weighing for teams planning similar log-based architectures.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;legacy-api-fragmentation&quot;&gt;Legacy API fragmentation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Each of the Times’ legacy CMS systems had its own API, developed independently with no shared schema. Consumers had to maintain separate integration logic for each upstream system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Decades of independent CMS development without a unifying data contract.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Monolog and its gateway enforce a single protobuf schema across all producers. Every piece of content — regardless of which CMS originated it — must conform to the same schema before it enters the log. Consumers only need to understand one format.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Downstream services are decoupled from CMS internals entirely. A CMS change does not require downstream teams to update their integration code.&lt;/p&gt;
&lt;h3 id=&quot;historical-content-access-at-scale&quot;&gt;Historical content access at scale&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Services that needed to bootstrap against historical content had to make individual API calls per asset. At 166 years of archives, this created unpredictable and unmanageable load on upstream APIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; API-based access is not designed for bulk historical retrieval. Each call adds latency and load that compounds at archive scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Log replay from Kafka offset 0. Because the Monolog retains all content indefinitely, any consumer can replay from the beginning to build or rebuild its data store without making API calls to upstream systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; A new downstream system can bootstrap against the full content archive by reading the Monolog from offset 0, with no coordination required with upstream teams and no risk of overloading upstream APIs.&lt;/p&gt;
&lt;h3 id=&quot;in-place-state-mutation-and-schema-drift&quot;&gt;In-place state mutation and schema drift&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Services that maintained their own permanent state had to implement migration logic for each schema change, leading to inconsistencies between systems that had migrated at different times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Distributed mutable state creates divergence when schema changes are applied incrementally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Log replay enables a different approach: instead of migrating state in place, a service can rebuild its data store from the log after a schema change. The Denormalizer and Elasticsearch ingestion nodes are designed with this assumption — they are stateless with respect to the log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Schema changes become a log replay operation rather than a coordinated migration across multiple live services.&lt;/p&gt;
&lt;h3 id=&quot;cloud-messaging-services-as-kafka-alternatives&quot;&gt;Cloud messaging services as Kafka alternatives&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The team evaluated Google Pub/Sub, AWS SNS/SQS, and Amazon Kinesis as potential alternatives to self-managed Kafka.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Cloud-native messaging services are appealing for operational simplicity, but the Monolog requires two properties: infinite event retention and globally ordered consumption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; None of the evaluated cloud messaging services satisfied both requirements. Apache Kafka was the only platform that did, which drove the decision to self-manage it on GCP despite the operational overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; A deliberate trade-off: greater operational burden in exchange for the architectural properties that make the log-as-source-of-truth model possible.&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tool / technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Core event log — Monolog, Denormalized Log, Skinny Log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams (Java)&lt;/td&gt;
&lt;td&gt;Denormalizer; Elasticsearch ingestion nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Protocol Buffers v3 (proto3)&lt;/td&gt;
&lt;td&gt;Message format for all Kafka topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API layer&lt;/td&gt;
&lt;td&gt;GraphQL (auto-generated from protobuf)&lt;/td&gt;
&lt;td&gt;Consumer-facing content API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service communication&lt;/td&gt;
&lt;td&gt;gRPC, Cloud Endpoints&lt;/td&gt;
&lt;td&gt;Inter-service RPC; Kafka Consumer API over SSL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud platform&lt;/td&gt;
&lt;td&gt;Google Cloud Platform (GCP)&lt;/td&gt;
&lt;td&gt;Kafka and ZooKeeper on Compute instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes / GKE&lt;/td&gt;
&lt;td&gt;Gateway service, Denormalizer, consumer applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Elasticsearch&lt;/td&gt;
&lt;td&gt;Full-text search index, populated from the Denormalized Log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster coordination&lt;/td&gt;
&lt;td&gt;ZooKeeper&lt;/td&gt;
&lt;td&gt;Kafka broker coordination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema governance&lt;/td&gt;
&lt;td&gt;Custom protobuf linter&lt;/td&gt;
&lt;td&gt;Forward/backward compatibility enforcement at write time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boerge Svingen&lt;/td&gt;
&lt;td&gt;Director of Engineering, The New York Times&lt;/td&gt;
&lt;td&gt;Primary author of the publishing pipeline architecture; author of the Confluent blog post; presenter at Kafka Summit NYC 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Martin Kleppmann&lt;/td&gt;
&lt;td&gt;Researcher and author&lt;/td&gt;
&lt;td&gt;Reviewed the Confluent blog post; cited for foundational log-based architecture concepts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Michael Noll&lt;/td&gt;
&lt;td&gt;Confluent&lt;/td&gt;
&lt;td&gt;Reviewed the Confluent blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mike Kaminski&lt;/td&gt;
&lt;td&gt;Not specified&lt;/td&gt;
&lt;td&gt;Reviewed the Confluent blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A single-partition topic is sometimes the right choice.&lt;/strong&gt; If your use case requires total causal ordering across all events — not just within a partition — a single-partition topic removes the risk of cross-partition ordering violations. This only works if your throughput is low enough that a single partition is not a bottleneck, which is true for content-publishing patterns and similar workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce schema at the write boundary, not the read boundary.&lt;/strong&gt; The Times’ gateway validates every asset against a protobuf schema before it enters the Monolog. Consumers never need to handle malformed messages because they cannot enter the log. Moving schema enforcement upstream reduces defensive complexity in every consumer you build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design explicit derived logs for specific consumer needs.&lt;/strong&gt; Rather than burdening individual consumers with reference resolution or full-payload overhead, create purpose-built derived topics — a Denormalized Log for systems that need complete assembled assets, a Skinny Log for lightweight notification consumers. Each derived log keeps consumer applications simpler and reduces coupling to the source topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infinite retention enables immutable deployments.&lt;/strong&gt; If Kafka retains the full event history, downstream data stores become fully reproducible from the log. You can destroy and rebuild any materialised view from offset 0, which simplifies schema migrations and removes the need for coordinated in-place migration logic across services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluate managed Kafka against your operational capacity.&lt;/strong&gt; The Times self-managed Kafka on GCP and found the overhead significant. If your team does not have deep Kafka operational experience, a managed service may be worth the cost — especially if the architectural properties you need (infinite retention, ordered consumption) are available in the managed offering you are evaluating.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;h3 id=&quot;primary-sources&quot;&gt;Primary sources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Boerge Svingen, “Publishing with Apache Kafka at The New York Times,” Confluent Blog, September 6, 2017. &lt;a href=&quot;https://www.confluent.io/blog/publishing-apache-kafka-new-york-times/&quot;&gt;https://www.confluent.io/blog/publishing-apache-kafka-new-york-times/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Boerge Svingen, “The Source of Truth: Why the New York Times Stores Every Piece of Content Ever Published in Kafka,” Kafka Summit NYC 2017 (slides). &lt;a href=&quot;https://www.slideshare.net/ConfluentInc/kafka-summit-nyc-2017-the-source-of-truth-why-the-new-york-times-stores-every-piece-of-content-ever-published-in-kafka&quot;&gt;https://www.slideshare.net/ConfluentInc/kafka-summit-nyc-2017-the-source-of-truth-why-the-new-york-times-stores-every-piece-of-content-ever-published-in-kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Boerge Svingen, Software Engineering Daily interview, October 30, 2017. &lt;a href=&quot;https://softwareengineeringdaily.com/2017/10/30/kafka-at-ny-times-with-boerge-svingen/&quot;&gt;https://softwareengineeringdaily.com/2017/10/30/kafka-at-ny-times-with-boerge-svingen/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-kpow-with-your-kafka-cluster&quot;&gt;Try Kpow with your Kafka cluster&lt;/h3&gt;
&lt;p&gt;If you are running a Kafka publishing pipeline and want visibility into consumer lag, topic throughput, and offset positions across your Monolog and derived topics, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; connects to any Kafka cluster and gives you that observability in a single interface. You can try it free for 30 days — deploy via Docker, Helm, or JAR, and connect to your cluster in minutes.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Uber uses Apache Kafka in production</title><link>https://factorhouse.io/articles/uber-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/uber-kafka-architecture/</guid><description>A deep-dive into Uber&apos;s Kafka architecture - covering use cases, scale, engineering decisions, and key contributors. From one region to trillions of messages a day.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Uber’s &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Kafka&lt;/a&gt; deployment is one of the most extensively documented in the industry, with the company publishing more than a dozen engineering blog posts and conference talks describing how its Streaming Platform team has evolved the infrastructure over a decade. By 2021, Uber was processing trillions of messages per day across tens of thousands of topics, with throughput that had grown from roughly one million to twelve million messages per second over five years. Apache Kafka sits at the centre of nearly every real-time system Uber operates, from matching riders with drivers to billing advertisers to detecting fraud.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Uber operates a global ride-hailing, food delivery, and freight platform across more than 70 countries. At peak, thousands of trips are being coordinated simultaneously, each generating a continuous stream of GPS updates, payment events, and state changes from driver and rider applications. That volume, combined with strict latency requirements for matching and pricing, pushed Uber toward an event-driven architecture early in its growth.&lt;/p&gt;
&lt;p&gt;Kafka was adopted in early 2015, beginning with a small cluster in a single region. Within a year, the platform was auditing approximately one trillion messages per day. By 2021, that figure had grown to trillions, and the team had built a suite of internal tools - uReplicator, Chaperone, uForwarder, and uGroup - on top of it. The timeline below traces the major milestones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timeline&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Early 2015&lt;/td&gt;
&lt;td&gt;Kafka adopted; small cluster in one region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;November 2015&lt;/td&gt;
&lt;td&gt;uReplicator deployed to production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;January 2016&lt;/td&gt;
&lt;td&gt;Chaperone auditing approximately 1 trillion messages per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2016&lt;/td&gt;
&lt;td&gt;uReplicator open-sourced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;December 2016&lt;/td&gt;
&lt;td&gt;Chaperone blog post published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 2017&lt;/td&gt;
&lt;td&gt;Hadoop Summit: real-time infrastructure scaled to trillions of events per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February 2018&lt;/td&gt;
&lt;td&gt;Dead letter queue / reliable reprocessing blog post published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 2019&lt;/td&gt;
&lt;td&gt;DBEvents CDC framework blog post published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2019&lt;/td&gt;
&lt;td&gt;Kafka Summit SF: Kafka Cluster Federation and multi-region disaster recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;January 2020&lt;/td&gt;
&lt;td&gt;Kappa architecture blog post published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;December 2020&lt;/td&gt;
&lt;td&gt;Multi-region Kafka disaster recovery blog post published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 2021&lt;/td&gt;
&lt;td&gt;Consumer Proxy blog published; 200,000 partitions, 12 million messages per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;September 2021&lt;/td&gt;
&lt;td&gt;Exactly-once ad event processing blog published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 2021&lt;/td&gt;
&lt;td&gt;uGroup consumer management framework blog published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022-2023&lt;/td&gt;
&lt;td&gt;Kafka tiered storage deployed to production workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April 2024&lt;/td&gt;
&lt;td&gt;Kafka Summit London: Exactly-Once Stream Processing at Scale at Uber&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;July 2024&lt;/td&gt;
&lt;td&gt;Kafka tiered storage blog published&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February 2026&lt;/td&gt;
&lt;td&gt;uForwarder open-sourced; 1,000+ consumer services onboarded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;ubers-kafka-use-cases&quot;&gt;Uber’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Kafka underpins real-time operations across Uber’s core products and its internal data platform. The use cases below span multiple engineering teams and reflect how the system has expanded from transport to advertising and insurance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rider-driver matching and surge pricing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GPS location events from rider and driver applications flow into Kafka, where stream processing jobs analyse supply and demand in near real-time. The same event streams power dynamic pricing, which updates fares every few seconds. UberEats ETAs are also calculated from Kafka-sourced event streams. These pipelines operate with strict latency targets: the platform targets API latency below 5ms and 99.99% availability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fraud detection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Streaming analysis of transaction and session events runs continuously to identify fraud patterns. Bot detection and rider session analytics feed into similar pipelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Change data capture&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The DBEvents framework reads MySQL binary logs via an internal tool called StorageTapper and streams change events into Kafka. Cassandra CDC events follow the same path. Downstream, these events land in Uber’s Hadoop data lake via Apache Hudi, which supports upsert-capable writes for incremental updates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ad event processing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Impressions and clicks from UberEats advertising flow through a Kafka pipeline that handles ad pacing, budget management, and customer billing. This pipeline operates with exactly-once guarantees, described in more detail under Special techniques below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Async microservice queuing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More than 1,000 internal consumer services use Kafka as an asynchronous queue via the uForwarder Consumer Proxy rather than consuming directly from brokers. The proxy handles offset management and delivery, shielding application teams from Kafka consumer group mechanics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dead letter queues and retry pipelines&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Driver Injury Protection insurance product deducts per-mile premiums on each trip. When payment processing fails, events route through tiered retry topics with increasing delays before landing in a dead letter queue. Independent consumer groups maintain retry pipelines for each failure tier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data archival and real-time analytics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka events sink to Apache Pinot for real-time OLAP queries and to Apache Hive for warehouse analysis. This combination gives Uber both low-latency query access to recent data and long-term historical analysis from the same event stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kappa architecture backfill&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Streaming and batch jobs share a single codebase. Rather than replaying historical data into Kafka for backfill runs, Uber models Apache Hive as a streaming source in Spark Structured Streaming, allowing the same job logic to run against historical data without reloading it into the cluster.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Messages per day&lt;/td&gt;
&lt;td&gt;Trillions (multiple petabytes)&lt;/td&gt;
&lt;td&gt;Uber Engineering Blog, 2020-2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messages per second (2021)&lt;/td&gt;
&lt;td&gt;12 million&lt;/td&gt;
&lt;td&gt;Consumer Proxy blog, August 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messages per second (baseline)&lt;/td&gt;
&lt;td&gt;1 million&lt;/td&gt;
&lt;td&gt;Consumer Proxy blog (5 years prior)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics&lt;/td&gt;
&lt;td&gt;Tens of thousands&lt;/td&gt;
&lt;td&gt;Hadoop Summit 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topics audited by Chaperone&lt;/td&gt;
&lt;td&gt;20,000+&lt;/td&gt;
&lt;td&gt;Hadoop Summit 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partitions&lt;/td&gt;
&lt;td&gt;200,000&lt;/td&gt;
&lt;td&gt;Consumer Proxy blog, August 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer services on uForwarder&lt;/td&gt;
&lt;td&gt;1,000+&lt;/td&gt;
&lt;td&gt;uForwarder blog, February 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clusters&lt;/td&gt;
&lt;td&gt;Dozens, across multiple regions&lt;/td&gt;
&lt;td&gt;Kafka Cluster Federation talk, 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API latency target&lt;/td&gt;
&lt;td&gt;Less than 5ms&lt;/td&gt;
&lt;td&gt;Hadoop Summit 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability target&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;td&gt;Hadoop Summit 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;Throughput grew twelve-fold over five years. Much of that growth was absorbed by the Consumer Proxy rather than by adding partitions, since the proxy decouples partition count from consumer service concurrency.&lt;/p&gt;
&lt;h2 id=&quot;ubers-kafka-architecture&quot;&gt;Uber’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;cluster-topology&quot;&gt;Cluster topology&lt;/h3&gt;
&lt;p&gt;Uber operates two tiers of Kafka clusters across multiple regions. Regional Clusters receive all producer traffic; producers always publish to their local region. Aggregate Clusters hold cross-region replicas and provide a unified global view of each topic. Replication between tiers is handled by uReplicator, Uber’s in-house replacement for Kafka MirrorMaker.&lt;/p&gt;
&lt;p&gt;In addition to the regional/aggregate split, clusters are segmented by use case: separate clusters exist for logging, database changelogs, and high-reliability messaging. This isolation contains blast radius when one cluster has problems and allows retention and replication policies to be tuned per use case.&lt;/p&gt;
&lt;p&gt;A Kafka Cluster Federation layer presents multiple physical clusters as a single logical cluster. A Kafka Proxy handles metadata routing, and a central Metadata Service maintains the registry of which topics live on which physical clusters.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;Producers publish through a Local Agent, a persistence layer that buffers messages on the producer side before writing to brokers. This improves durability by ensuring messages survive transient broker unavailability without being dropped at the client. Multi-language producer support covers Java, Go, Python, Node.js, and C++. An internal Kafka REST Proxy provides an HTTP interface for non-JVM producers; through internal optimisation work, its throughput was raised from 7,000 to 45,000 QPS per box.&lt;/p&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Rather than having each microservice manage its own Kafka consumer group, Uber routes most consumer traffic through uForwarder, a push-based Consumer Proxy. uForwarder fetches messages from Kafka partitions and delivers them to consumer service endpoints via gRPC. This design separates partition count from processing concurrency: a consumer service can scale its processing threads independently of how many Kafka partitions the topic has. Offset commits are managed centrally by the proxy rather than by individual service instances.&lt;/p&gt;
&lt;p&gt;uForwarder also handles out-of-order offset commits. Rather than blocking a partition on a slow message, it acknowledges individual messages and commits only contiguous completed ranges. This allows parallel processing within a partition without the risk of losing acknowledgement state.&lt;/p&gt;
&lt;h3 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h3&gt;
&lt;p&gt;Apache Flink is the primary stream processing engine for stateful workloads, including fraud detection and ad event aggregation. Apache Samza runs alongside Flink for some pipelines. Apache Spark Structured Streaming is used for the Kappa architecture backfill jobs described above.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;CDC ingestion uses StorageTapper, Uber’s internal MySQL binlog reader, to produce change events into Kafka. Downstream, sinks connect to Apache Hive, HDFS, and Apache Pinot. The Chaperone audit system also consumes every Kafka message as part of the observability pipeline.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;uReplicator: custom cross-cluster replication&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber replaced Kafka MirrorMaker with uReplicator after MirrorMaker caused weekly production outages. The root cause was consumer group rebalancing: whenever a topic was added or changed, MirrorMaker would pause replication for five to ten minutes while rebalancing completed. uReplicator addresses this with Apache Helix for static partition assignment and a DynamicKafkaConsumer that eliminates rebalance-triggered pauses. New topics can be added to replication at runtime without restarting the cluster. uReplicator also applies header-based filters during replication to prevent cyclic data duplication across federated clusters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exactly-once ad event processing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber’s advertising billing pipeline uses a combination of Flink transactional producers, Kafka &lt;code&gt;read_committed&lt;/code&gt; consumer isolation, two-minute checkpoint intervals, and per-record UUIDs to achieve end-to-end exactly-once delivery. Deduplication at the sink layer uses Apache Pinot’s native upsert capability and Hive keyed on the same UUIDs. This pipeline was built specifically to avoid double-counting billable advertising events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka tiered storage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber implemented Kafka tiered storage using the KIP-405 pluggable storage interface. Recent log segments remain on broker disk for low-latency access. Older segments are offloaded to remote storage (HDFS, S3, GCS, or Azure) transparently, without the consumer needing to know which tier a message is fetched from. This decouples storage retention from broker capacity: longer retention periods no longer require adding brokers or running separate data pipelines to external storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kappa architecture backfill via Hive as a streaming source&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When a streaming job needs to backfill historical data, replaying that data into Kafka adds significant cluster load and can disrupt real-time consumers. Uber avoids this by modelling Apache Hive as a streaming source in Spark Structured Streaming. The same job code handles both real-time Kafka consumption and historical Hive reads, and windowing semantics are preserved across the two modes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dead letter queue topology&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber’s dead letter queue implementation uses a multi-tier retry topology: a main consumption topic feeds into retry topics with increasing delay intervals, and messages that exhaust retries land in a dead letter topic. Each tier uses Avro schemas and a leaky bucket pattern for flow control. Multiple independent consumer groups can maintain their own retry pipelines against the same underlying topics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;uGroup: consumer group visibility via &lt;code&gt;__consumer_offsets&lt;/code&gt; decoding&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Standard Kafka consumer group monitoring relies on active consumers reporting metrics. This misses consumer groups that are stopped or failing silently. uGroup is a streaming job that decodes Kafka’s internal &lt;code&gt;__consumer_offsets&lt;/code&gt; topic directly, making all consumer group activity visible regardless of consumer state. It also tracks offset state across regions to support disaster recovery failover.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;End-to-end auditing with Chaperone&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Chaperone is Uber’s audit system for Kafka pipelines. It consumes every message across all topics and uses ten-minute tumbling windows to compute count, p99 latency, and duplication metrics at four points in the pipeline: the proxy client, the proxy server, regional brokers, and aggregate brokers. It audits more than 20,000 topics and uses write-ahead logging and UUIDs to ensure that audit records themselves are written with exactly-once semantics. When a discrepancy appears, operators can pinpoint which pipeline tier introduced data loss or duplication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer group observability with uGroup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;uGroup emits lag metrics and stuck-partition alerts for all consumer groups, including those that are not currently running. During a multi-region failover, uGroup provides the offset state mapping needed for consumers to resume from the correct position in the target region.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema governance with Schema-Service and Heatpipe&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber maintains an in-house schema registry called Schema-Service that enforces backward-compatible Avro schema evolution. The Heatpipe library, used by producers, validates messages against the registered schema at ingestion time. This prevents malformed or schema-incompatible data from entering Kafka pipelines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topic ownership enforcement&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber requires ownership metadata at topic creation. Automated tooling infers ownership where possible. This is part of a broader data culture initiative to ensure every dataset - including Kafka topics - has an identifiable owner who can be contacted during an incident.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Centralised upgrades via Consumer Proxy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before uForwarder, each of Uber’s 1,000+ consumer services maintained its own Kafka client library, often across multiple languages. Upgrading Kafka client versions required coordinating changes across hundreds of services. With uForwarder, the Kafka consumer implementation is centralised in the proxy. Client upgrades happen in the proxy without requiring changes to the services it serves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uber runs Kafka as a self-managed deployment across its own infrastructure in multiple geographic regions.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka MirrorMaker caused weekly outages due to rebalancing delays of five to ten minutes&lt;/td&gt;
&lt;td&gt;Built uReplicator with Apache Helix for static partition assignment and a DynamicKafkaConsumer&lt;/td&gt;
&lt;td&gt;Rebalancing delays eliminated; dynamic topic whitelisting without cluster restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling partitions: 1 msg/sec throughput required one partition per consumer thread, which would require millions of partitions across 1,000+ services&lt;/td&gt;
&lt;td&gt;Consumer Proxy multiplexes a single partition across many service instances via gRPC&lt;/td&gt;
&lt;td&gt;Throughput scaled from 1 million to 12 million messages per second without proportional partition growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head-of-line blocking from slow or poisoned messages halted entire partitions&lt;/td&gt;
&lt;td&gt;Consumer Proxy detects stuck consumers and routes problem messages to DLQ; uForwarder adds active head-of-line blocking resolution&lt;/td&gt;
&lt;td&gt;Individual slow messages no longer stall partition processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backfilling streaming jobs required replaying data into Kafka, creating significant cluster load&lt;/td&gt;
&lt;td&gt;Kappa architecture models Hive as a streaming source in Spark Structured Streaming&lt;/td&gt;
&lt;td&gt;Historical backfill runs without additional Kafka load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ad billing double-counting risk from at-least-once delivery&lt;/td&gt;
&lt;td&gt;Flink transactions, Kafka read_committed, per-record UUIDs, and Pinot upserts&lt;/td&gt;
&lt;td&gt;End-to-end exactly-once delivery for billing events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage and compute were coupled: longer retention required adding broker capacity&lt;/td&gt;
&lt;td&gt;Kafka tiered storage (KIP-405) offloads old segments to S3, GCS, or HDFS&lt;/td&gt;
&lt;td&gt;Retention extended without adding brokers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka REST Proxy throughput was insufficient at 7,000 QPS per box&lt;/td&gt;
&lt;td&gt;Internal performance optimisations&lt;/td&gt;
&lt;td&gt;Throughput raised to 45,000 QPS per box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No visibility into consumer groups that weren’t actively running&lt;/td&gt;
&lt;td&gt;uGroup decodes __consumer_offsets directly&lt;/td&gt;
&lt;td&gt;Full consumer group visibility including offline consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offset mapping during multi-region failover with out-of-order replication&lt;/td&gt;
&lt;td&gt;Active/Passive mode with periodic cross-region offset synchronisation and offset mapping tables&lt;/td&gt;
&lt;td&gt;Consumers can resume from the correct position after a regional failover&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Self-managed, multi-region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Schema-Service (internal) + Heatpipe&lt;/td&gt;
&lt;td&gt;Avro backward compatibility enforced at ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Apache Flink, Apache Samza, Apache Spark Structured Streaming&lt;/td&gt;
&lt;td&gt;Flink primary for stateful workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-cluster replication&lt;/td&gt;
&lt;td&gt;uReplicator (open-source, Uber-built)&lt;/td&gt;
&lt;td&gt;Replaces MirrorMaker; Apache Helix for partition assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer proxy&lt;/td&gt;
&lt;td&gt;uForwarder (open-source, Uber-built)&lt;/td&gt;
&lt;td&gt;Push-based; gRPC delivery; 1,000+ services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors / CDC&lt;/td&gt;
&lt;td&gt;StorageTapper (MySQL binlog reader, internal)&lt;/td&gt;
&lt;td&gt;CDC to Kafka; downstream to Hive and Pinot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time OLAP&lt;/td&gt;
&lt;td&gt;Apache Pinot&lt;/td&gt;
&lt;td&gt;Native upsert for exactly-once deduplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data lake / warehouse&lt;/td&gt;
&lt;td&gt;Apache Hadoop, HDFS, Apache Hive&lt;/td&gt;
&lt;td&gt;CDC and event archival; also used as streaming source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upsert storage&lt;/td&gt;
&lt;td&gt;Apache Hudi&lt;/td&gt;
&lt;td&gt;Incremental CDC updates on HDFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiered storage backends&lt;/td&gt;
&lt;td&gt;HDFS, Amazon S3, Google Cloud Storage, Azure&lt;/td&gt;
&lt;td&gt;KIP-405 pluggable interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring / auditing&lt;/td&gt;
&lt;td&gt;Chaperone (internal), uGroup (internal)&lt;/td&gt;
&lt;td&gt;End-to-end audit; consumer group lag and DR offset tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster coordination&lt;/td&gt;
&lt;td&gt;Apache Helix, Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;uReplicator partition assignment; cluster state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serialisation&lt;/td&gt;
&lt;td&gt;Apache Avro&lt;/td&gt;
&lt;td&gt;Enforced by Heatpipe + Schema-Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transport (Consumer Proxy)&lt;/td&gt;
&lt;td&gt;gRPC / Protobuf&lt;/td&gt;
&lt;td&gt;uForwarder to consumer service endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP producer interface&lt;/td&gt;
&lt;td&gt;Kafka REST Proxy (internal)&lt;/td&gt;
&lt;td&gt;Optimised to 45,000 QPS per box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Languages&lt;/td&gt;
&lt;td&gt;Java, Go, Python, Node.js, C++&lt;/td&gt;
&lt;td&gt;Multi-language producer support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Contribution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chinmay Soman&lt;/td&gt;
&lt;td&gt;Software Engineer, Streaming Platform&lt;/td&gt;
&lt;td&gt;Led uReplicator design; authored the uReplicator blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yuanchi Ning, Xiang Fu, Hongliang Xu&lt;/td&gt;
&lt;td&gt;Streaming Platform engineers&lt;/td&gt;
&lt;td&gt;Co-built uReplicator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Xiaobing Li&lt;/td&gt;
&lt;td&gt;Software Engineer, Core Infrastructure&lt;/td&gt;
&lt;td&gt;Co-authored Chaperone blog post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ankur Bansal&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Streaming Team&lt;/td&gt;
&lt;td&gt;Co-authored Chaperone; presented at Hadoop Summit 2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mingmin Chen&lt;/td&gt;
&lt;td&gt;Director of Engineering, SSD Team&lt;/td&gt;
&lt;td&gt;Hadoop Summit 2017; co-authored DR blog; uGroup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yupeng Fu&lt;/td&gt;
&lt;td&gt;Principal Software Engineer, SSD/Streaming Team&lt;/td&gt;
&lt;td&gt;Disaster recovery blog; uGroup; ad events; Kafka Cluster Federation talk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Xiaoman Dong&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Streaming Data&lt;/td&gt;
&lt;td&gt;Kafka Cluster Federation talk; uGroup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ovais Tariq&lt;/td&gt;
&lt;td&gt;Sr. Manager, Core Storage&lt;/td&gt;
&lt;td&gt;Led DBEvents CDC framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amey Chaugule&lt;/td&gt;
&lt;td&gt;Senior Software Engineer, Marketplace Experimentation&lt;/td&gt;
&lt;td&gt;Authored Kappa Architecture blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qichao Chu, George Teo, Haitao Zhang, Zhifeng Chen&lt;/td&gt;
&lt;td&gt;Streaming Data Team&lt;/td&gt;
&lt;td&gt;Co-authored Consumer Proxy blog; led uForwarder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jacob Tsafatinos, Yuriy Bondaruk, Yupeng Fu, James Kwon&lt;/td&gt;
&lt;td&gt;Ads Platform / Ads Billing&lt;/td&gt;
&lt;td&gt;Co-authored exactly-once ad events blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ning Xia&lt;/td&gt;
&lt;td&gt;Software Engineer, Payments Team&lt;/td&gt;
&lt;td&gt;Authored dead letter queue blog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abhijeet Kumar, Kamal Chandraprakash, Satish Duggana&lt;/td&gt;
&lt;td&gt;Kafka Team&lt;/td&gt;
&lt;td&gt;Co-authored Kafka tiered storage blog; Satish Duggana is an Apache Kafka committer and PMC member&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roshan Naik, Si Lao&lt;/td&gt;
&lt;td&gt;Uber Engineering&lt;/td&gt;
&lt;td&gt;Presented Exactly-Once Stream Processing at Scale at Kafka Summit London 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Replication tooling matters at scale.&lt;/strong&gt; MirrorMaker’s consumer group rebalancing caused weekly outages for Uber. If you are replicating across clusters or regions, understand how your replication tool handles topic changes and partition rebalancing before it becomes a production problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partition count and consumer concurrency are separate concerns.&lt;/strong&gt; Uber’s Consumer Proxy approach shows that you do not have to create more partitions to scale consumer throughput. A proxy or multiplexing layer can decouple the two, which is relevant if you have many small consumers or need to avoid partition-count overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exactly-once requires a coordinated strategy across producers, consumers, and sinks.&lt;/strong&gt; Uber’s ad billing pipeline combines Flink transactions, Kafka &lt;code&gt;read_committed&lt;/code&gt; isolation, and per-record UUIDs with Pinot upserts. No single piece provides the guarantee on its own. If you need exactly-once for a high-stakes pipeline, plan the deduplication strategy at every tier from the start.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tiered storage changes the broker-sizing conversation.&lt;/strong&gt; Decoupling log retention from broker disk means you can extend retention without adding brokers. If your current retention policy is constrained by storage cost, tiered storage is worth evaluating before the next round of broker capacity planning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Centralising consumer infrastructure simplifies client upgrades.&lt;/strong&gt; Coordinating a Kafka client upgrade across hundreds of services in multiple languages is operationally expensive. If you are managing many consumer services, a shared consumer proxy or library layer reduces the coordination overhead significantly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Chinmay Soman et al. - uReplicator: Uber Engineering’s Robust Apache Kafka Replicator - Uber Engineering Blog, 2016-08-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Xiaobing Li, Ankur Bansal - Chaperone: Audit Apache Kafka End-to-End - Uber Engineering Blog, 2016-12-08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Ankur Bansal, Mingmin Chen - How Uber Scaled Its Real-Time Infrastructure to Trillion Events Per Day - Hadoop Summit, 2017-06-14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Ovais Tariq, Nishith Agarwal - DBEvents: Uber’s Ingestion Framework for Database Events - Uber Engineering Blog, 2019-03-14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Yupeng Fu, Xiaoman Dong - Kafka Cluster Federation at Uber - Kafka Summit SF 2019&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Amey Chaugule - Kappa Architecture: Uber’s Approach to Unifying Streaming and Batch - Uber Engineering Blog, 2020-01-23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Yupeng Fu, Mingmin Chen - Disaster Recovery for Multi-Region Kafka at Uber - Uber Engineering Blog, 2020-12-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Qichao Chu, George Teo, Haitao Zhang, Zhifeng Chen - Kafka Async Queuing with Consumer Proxy - Uber Engineering Blog, 2021-08-31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Jacob Tsafatinos et al. - Real-Time Exactly-Once Ad Event Processing with Apache Flink, Kafka, and Pinot - Uber Engineering Blog, 2021-09-23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Qichao Chu et al. - Introducing uGroup: Uber’s Consumer Management Framework - Uber Engineering Blog, 2021-10-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Abhijeet Kumar et al. - Kafka Tiered Storage at Uber - Uber Engineering Blog, 2024-07-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Zhifeng Chen, Haifeng Chen - Introducing uForwarder - Uber Engineering Blog, 2026-02-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Roshan Naik, Si Lao - Exactly-Once Stream Processing at Scale in Uber - Kafka Summit London 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Ning Xia - Reliable Reprocessing: Dead Letter Queues for Apache Kafka - Uber Engineering Blog, 2018-02-16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;If you want to explore your own Kafka topics and consumer groups with the kind of visibility Uber has built internally, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; offers a free 30-day trial and connects to any Kafka cluster in minutes.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>How Walmart uses Apache Kafka in production</title><link>https://factorhouse.io/articles/walmart-kafka-architecture/</link><guid isPermaLink="true">https://factorhouse.io/articles/walmart-kafka-architecture/</guid><description>A deep-dive into Walmart&apos;s Kafka architecture — covering real-time inventory, fraud detection, the Customer Data Platform, and the Messaging Proxy Service handling trillions of messages per day.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Walmart has been running &lt;a href=&quot;/articles/kafka-architecture&quot;&gt;Apache Kafka&lt;/a&gt; in production since at least 2016, and the scale of the deployment today reflects a decade of architectural iteration. As of June 2024, Walmart’s Kafka infrastructure processes trillions of messages per day, serves 25,000+ consumers across public and private clouds, and maintains 99.99% availability. The engineering problems Kafka solves at Walmart range from near-real-time search indexing and replenishment planning across 5,000+ stores to fraud detection on every online transaction and a Customer Data Platform ingesting around 40 billion events per day.&lt;/p&gt;
&lt;h2 id=&quot;company-overview&quot;&gt;Company overview&lt;/h2&gt;
&lt;p&gt;Walmart is the world’s largest retailer by revenue, operating roughly 10,500 stores across 19 countries and a significant e-commerce business. Its technology organisation, Walmart Global Tech, manages the platform infrastructure that supports in-store systems, e-commerce, supply chain, and customer-facing applications simultaneously.&lt;/p&gt;
&lt;p&gt;Kafka arrived at Walmart before 2016, initially on shared bare-metal clusters that replaced Apache Flume for log collection and tracking-pixel feeds. By late 2016, engineers Ning Zhang and Anil Kumar documented the first architectural shift: moving from shared clusters to a self-serving model where teams deployed dedicated clusters via OneOps, Walmart’s internal OpenStack-based platform. That shift established the architectural pattern Walmart has built on ever since: many purpose-built Kafka clusters rather than a single shared monolith.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key milestones&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-2016:&lt;/strong&gt; Kafka deployed on shared bare-metal clusters, replacing Flume for log collection and tracking pixel ingestion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nov 2016:&lt;/strong&gt; Migration to self-serving dedicated clusters via OneOps; 7 datacentres, 5 local clusters, 2 aggregation clusters&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oct 2016:&lt;/strong&gt; Kafka becomes the backbone for the near-real-time search index and hundreds of microservices in the item-setup pipeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nov 2017:&lt;/strong&gt; Kafka + Apache Druid analytics cluster reaches nearly 1 billion+ events per day&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jun 2019:&lt;/strong&gt; Inventory system ingesting 500 million events per day across 5,000+ stores; Deepak Goyal presents Kafka Streams extensions at Kafka Summit NYC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020–2021:&lt;/strong&gt; Navinder Pal Singh Brar presents KIP-535 and KIP-562 at Kafka Summit; both merged into Apache Kafka&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 2022:&lt;/strong&gt; Replenishment system reaches 85 GB/min throughput across 18 brokers processing close to 100 million SKUs in under 3 hours&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jul 2022:&lt;/strong&gt; Cassandra CDC pipeline with Debezium and Redis Bloom filters deployed at 60,000 changes per second&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jun 2024:&lt;/strong&gt; Messaging Proxy Service launched; trillions of messages per day, 25,000+ consumers, 99.99% availability&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;walmarts-kafka-use-cases&quot;&gt;Walmart’s Kafka use cases&lt;/h2&gt;
&lt;p&gt;Walmart uses Kafka across multiple systems, each solving a distinct operational problem. The use cases described here are drawn from published engineering posts by named Walmart engineers between 2016 and 2024.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Near-real-time search index&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Anil Kumar, a Global eCommerce Engineer at Walmart Labs, described Kafka as “the backbone for our New Near Real Time (NRT) Search Index, where changes are reflected on the site in seconds” in a 2016 Confluent post. The system processes billions of pricing and inventory updates per day. Before Kafka, search index latency was measured in hours; the Kafka-backed pipeline reduced that to seconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Item-setup pipeline and microservices backbone&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka connects hundreds of microservices in Walmart’s item-setup pipeline, covering product normalisation, offers validation, pricing, inventory, and logistics data. Teams in different geographies operate autonomously because Kafka decouples producers from consumers: each team publishes to its own topics without coordinating with downstream consumers. The architecture uses many small clusters with hundreds of topics rather than one shared cluster, limiting the blast radius of any individual failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time inventory management&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Suman Pattnaik, Director of Engineering at Walmart, described a real-time inventory system in a 2020 Confluent post that replaced batch-based inventory processes. The system ingests from more than 10 sources of event streaming data and maintains a denormalised canonical view of inventory positions across stores, e-commerce, and distribution centres.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-time replenishment&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The replenishment platform, described by Pattnaik in a 2022 Confluent post, spans 5,000+ stores, 150+ distribution centres, and 1,000+ vendors across 24+ countries. Change data capture feeds a denormalised view that passes through a planning engine holding inventory positions, forecasts, and safety stocks. The system processed close to 100 million SKUs in under three hours at 85 GB/min throughput as of 2022.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customer Data Platform (CDP)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Customer Backbone team, led by Navinder Pal Singh Brar (Staff Engineer) and Deepak Goyal, built a multi-tenant platform on Kafka Streams that ingests customer activity events (search, add-to-cart, transactions), builds customer identity and state using RocksDB, and triggers ML models (bid models, fraud detection, omnichannel reorder) per-event or in batch. The CDP processes around 40 billion events per day and serves processed knowledge at under 10 ms (95th-percentile) latency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fraud detection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Walmart runs fraud detection on every online transaction. The fraud detection application uses Kafka Streams and was the primary driver behind Walmart’s open-source contributions KIP-535 and KIP-562 — described in detail in the challenges section below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event stream analytics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A Druid-based analytics cluster fed from Kafka via Apache Storm ingests nearly 1 billion+ events per day (2 TB of raw data) and serves sub-second OLAP query latencies, replacing Hadoop/Hive/Presto workflows that previously took hours. Amaresh Nayak documented this pipeline on the Walmart Global Tech Blog in November 2017.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cassandra CDC&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Scott Harvester and Nitin Chhabra documented a pipeline in July 2022 that uses Debezium to capture change data from Cassandra 4.x and publish it to Kafka, supporting high-volume e-commerce databases that generate 60,000 data changes per second.&lt;/p&gt;
&lt;h2 id=&quot;scale-and-throughput&quot;&gt;Scale and throughput&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Messages per day (platform-wide, 2024):&lt;/strong&gt; Trillions (Ravinder Matte et al., Walmart Global Tech Blog, Jun 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka consumers (2024):&lt;/strong&gt; 25,000+ (Ravinder Matte et al., Walmart Global Tech Blog, Jun 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform availability (2024):&lt;/strong&gt; 99.99% (Ravinder Matte et al., Walmart Global Tech Blog, Jun 2024)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customer Data Platform events/day:&lt;/strong&gt; ~40 billion (Navinder Pal Singh Brar, Strata Data Conference NY, Sep 2019)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDP p95 serving latency:&lt;/strong&gt; &amp;lt;10 ms (Navinder Pal Singh Brar, Strata Data Conference NY, Sep 2019)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replenishment throughput (2022):&lt;/strong&gt; 85 GB/min (Suman Pattnaik, Confluent Blog, May 2022)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replenishment brokers:&lt;/strong&gt; 18 brokers; 500+ partitions per topic (Suman Pattnaik, Confluent Blog, May 2022)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replenishment SKUs processed per run:&lt;/strong&gt; Close to 100 million in &amp;lt;3 hours (Suman Pattnaik, Confluent Blog, May 2022)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inventory events/day (2019):&lt;/strong&gt; 500 million (projected to 650 million) (Pattnaik and Subburaj, Kafka Summit SF 2019)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Druid analytics events/day (2017):&lt;/strong&gt; Nearly 1 billion+ (2 TB raw) (Amaresh Nayak, Walmart Global Tech Blog, Nov 2017)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cassandra CDC changes/second:&lt;/strong&gt; 60,000 (Harvester and Chhabra, Walmart Global Tech Blog, Jul 2022)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Early topology (2016):&lt;/strong&gt; 7 geographic datacentres; 5 local + 2 aggregation clusters (Ning Zhang, Walmart Global Tech Blog, Nov 2016)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;walmarts-kafka-architecture&quot;&gt;Walmart’s Kafka architecture&lt;/h2&gt;
&lt;h3 id=&quot;multi-cluster-topology&quot;&gt;Multi-cluster topology&lt;/h3&gt;
&lt;p&gt;Walmart has consistently favoured many purpose-built clusters over a shared monolith. The early 2016 architecture ran 5 local clusters and 2 aggregation clusters across 7 geographic datacentres. Producers wrote to local clusters only; consumers listened to both local and mirrored topics simultaneously. Patched MirrorMaker with complete topic renaming replicated data between sites, designed to degrade gracefully on datacentre failure and resume from the point of failure on recovery.&lt;/p&gt;
&lt;p&gt;The item-setup pipeline used a separate “many small clusters with hundreds of topics” model (Anil Kumar, 2016). Each team maintained its own cluster, limiting cross-team coordination and blast radius.&lt;/p&gt;
&lt;h3 id=&quot;wcnp-and-hybrid-cloud-current&quot;&gt;WCNP and hybrid cloud (current)&lt;/h3&gt;
&lt;p&gt;Consumer applications now run as container images on WCNP (Walmart Cloud Native Platform), an enterprise-grade Kubernetes-based multi-cloud container orchestration framework spanning private cloud and public cloud (Azure and Google Cloud). The MPS architecture introduced in 2024 targets stateless consumer services that auto-scale on WCNP without touching Kafka partition counts.&lt;/p&gt;
&lt;h3 id=&quot;kafka-streams-as-a-distributed-nosql-database&quot;&gt;Kafka Streams as a distributed NoSQL database&lt;/h3&gt;
&lt;p&gt;The Customer Backbone team uses Kafka Streams not just for stream processing but as the stateful data store for the Customer Data Platform. Each customer profile is maintained in RocksDB. Enriched profiles feed downstream Kafka Streams applications for recommendations and fraud detection, creating a graph of event-driven microservices. This pattern requires custom extensions to make Kafka Streams operationally stable at scale (see Special Techniques below).&lt;/p&gt;
&lt;h3 id=&quot;replenishment-architecture&quot;&gt;Replenishment architecture&lt;/h3&gt;
&lt;p&gt;The replenishment system uses 18 Kafka brokers with 500+ partitions per topic. CDC feeds a denormalised view that passes through a planning engine. The system operates in an active-passive replication model between datacentres and includes a fallback REST service to a database if Kafka becomes unavailable.&lt;/p&gt;
&lt;h3 id=&quot;cassandra-cdc-pipeline&quot;&gt;Cassandra CDC pipeline&lt;/h3&gt;
&lt;p&gt;Debezium 1.9 reads Cassandra 4.x commit logs continuously and publishes changes to Kafka. A Redis + RedisBloom (Bloom filter) deduplication layer, partitioned hourly, handles the 9x record fan-out caused by 3-region, replication-factor-3 deployments. Walmart’s internal Data Acquisition Tool (DAQ) orchestrates the pipeline on Azure.&lt;/p&gt;
&lt;h3 id=&quot;druid-analytics-pipeline&quot;&gt;Druid analytics pipeline&lt;/h3&gt;
&lt;p&gt;Kafka delivers events to Apache Storm (using Trident), which enriches them via a custom log scraper and writes to Druid. Druid pre-aggregates (rollup) at ingestion and stores data in inverted-index, columnar format for sub-second OLAP queries.&lt;/p&gt;
&lt;h3 id=&quot;producer-architecture&quot;&gt;Producer architecture&lt;/h3&gt;
&lt;p&gt;For the replenishment system, Suman Pattnaik documented the following producer configuration in 2022:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom partitioner using a murmur hash function to ensure each item-store combination lands on a single partition&lt;/li&gt;
&lt;li&gt;&lt;code&gt;linger.ms&lt;/code&gt; tuning for batching&lt;/li&gt;
&lt;li&gt;&lt;code&gt;batch.size&lt;/code&gt; set to 16,000 bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acks=all&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;consumer-architecture&quot;&gt;Consumer architecture&lt;/h3&gt;
&lt;p&gt;Consumer configuration for the replenishment system includes: &lt;code&gt;max.poll.records&lt;/code&gt; and &lt;code&gt;max.poll.interval.ms&lt;/code&gt; explicitly tuned, auto-commit disabled, and session timeout and heartbeat interval explicitly set.&lt;/p&gt;
&lt;p&gt;As of 2024, many consumer workloads are decoupled from direct Kafka consumption via the Messaging Proxy Service (described below).&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-ecosystem&quot;&gt;Kafka Connect ecosystem&lt;/h3&gt;
&lt;p&gt;Kafka Connect is used to sink topics to HDFS for long-term storage, with &lt;code&gt;max.poll.interval.ms&lt;/code&gt;, &lt;code&gt;max.poll.records&lt;/code&gt;, and &lt;code&gt;session.timeout.ms&lt;/code&gt; tuned for the HDFS connector. Debezium (acting as a Kafka Connect source) handles Cassandra CDC ingestion.&lt;/p&gt;
&lt;h2 id=&quot;special-techniques-and-engineering-innovations&quot;&gt;Special techniques and engineering innovations&lt;/h2&gt;
&lt;h3 id=&quot;messaging-proxy-service-mps&quot;&gt;Messaging Proxy Service (MPS)&lt;/h3&gt;
&lt;p&gt;The most significant architectural innovation in Walmart’s recent Kafka history is the Messaging Proxy Service, published by Ravinder Matte, Vilas Athavale, Sid Anand, and colleagues in June 2024.&lt;/p&gt;
&lt;p&gt;The standard approach to scaling Kafka consumers is to increase partition count to match parallelism. The problem with this at Walmart’s scale is that consumer group rebalances become frequent and disruptive: pod scaling events, rolling deployments, or processing delays exceeding &lt;code&gt;max.poll.interval.ms&lt;/code&gt; all trigger rebalances that cause consumer lag and SLA violations.&lt;/p&gt;
&lt;p&gt;MPS inserts an HTTP proxy layer between Kafka and consumer services:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A single reader thread polls Kafka and writes to a bounded in-memory PendingQueue&lt;/li&gt;
&lt;li&gt;A writer thread pool exposes stateless REST endpoints that consumer pods call&lt;/li&gt;
&lt;li&gt;An Order Iterator preserves per-key ordering across the proxy boundary&lt;/li&gt;
&lt;li&gt;A Dead Letter Queue handles poison-pill messages&lt;/li&gt;
&lt;li&gt;A separate offset commit thread manages Kafka offsets, decoupling commit timing from processing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consumer pods become stateless services that can auto-scale on Kubernetes without triggering Kafka rebalances. Consumer count and partition count are fully decoupled. According to the Walmart engineering team, this eliminated most rebalances in production, with the exception of rare cluster restarts or network events.&lt;/p&gt;
&lt;h3 id=&quot;cold-bootstrap-for-kafka-streams-recovery&quot;&gt;Cold Bootstrap for Kafka Streams recovery&lt;/h3&gt;
&lt;p&gt;Deepak Goyal presented this technique at Kafka Summit NYC 2019. Kafka Streams recovers stateful tasks by replaying changelog topics, but for the Customer Data Platform’s large RocksDB stores, those changelogs held gigabytes of data, making standby recovery very slow.&lt;/p&gt;
&lt;p&gt;Cold Bootstrap replaces changelog replay with a direct RocksDB snapshot copy: when a node fails, the standby copies the active node’s RocksDB snapshot directly via JSch, then resumes from the saved offset. This achieves zero event loss with significantly faster recovery, and eliminates the need for indefinite changelog topic retention.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-repartitioning-in-kafka-streams&quot;&gt;Dynamic repartitioning in Kafka Streams&lt;/h3&gt;
&lt;p&gt;Goyal’s team added support for elastic repartitioning of Kafka Streams state across new partitions at runtime, enabling scaling to any number of partitions and nodes without a full restart. This was not available in stock Kafka Streams at the time.&lt;/p&gt;
&lt;h3 id=&quot;rackaz-aware-task-assignment&quot;&gt;Rack/AZ-aware task assignment&lt;/h3&gt;
&lt;p&gt;Active and standby Kafka Streams tasks for the same partition are explicitly assigned to different racks or availability zones, improving resilience without additional hardware.&lt;/p&gt;
&lt;h3 id=&quot;availability-first-kafka-streams-kip-535-and-kip-562&quot;&gt;Availability-first Kafka Streams (KIP-535 and KIP-562)&lt;/h3&gt;
&lt;p&gt;Navinder Pal Singh Brar presented this work at Kafka Summit 2020. The fraud detection application requires Kafka Streams reads on every transaction, but Kafka Streams’ default behaviour blocks reads during consumer rebalances. At Walmart’s transaction volume, this was incompatible with latency requirements.&lt;/p&gt;
&lt;p&gt;Walmart contributed two KIPs to the Apache Kafka project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KIP-535:&lt;/strong&gt; Read from replica standbys and read while rebalancing — explicitly trading some consistency for higher availability&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KIP-562:&lt;/strong&gt; Read from the store of a specific partition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both were merged into Apache Kafka. Brar holds four US patents related to Kafka Streams and was named a Confluent Community Catalyst.&lt;/p&gt;
&lt;h3 id=&quot;custom-partitioner-for-dirty-write-prevention&quot;&gt;Custom partitioner for dirty-write prevention&lt;/h3&gt;
&lt;p&gt;The replenishment system uses a custom partitioner based on a murmur hash function, ensuring each item-store combination always lands on a single partition and is consumed by a single consumer. This prevents database deadlocks from concurrent writes that would occur if the same item-store pair were processed by multiple consumers simultaneously.&lt;/p&gt;
&lt;h3 id=&quot;bloom-filter-deduplication-for-cassandra-cdc&quot;&gt;Bloom-filter deduplication for Cassandra CDC&lt;/h3&gt;
&lt;p&gt;Each Cassandra write in a 3-region, RF=3 cluster generates 9 CDC records. Out-of-order delivery and partial column updates compound the problem. Walmart uses Redis-backed RedisBloom Bloom filters partitioned hourly, with error rates tested at 1 in 1 million, to deduplicate records before they reach downstream consumers. Production configuration achieved 623,000 deduplicated records per minute across 3 nodes.&lt;/p&gt;
&lt;p&gt;Walmart also enhanced Debezium to process Cassandra commit logs without waiting for log file completion, reducing CDC latency further.&lt;/p&gt;
&lt;h2 id=&quot;operating-kafka-at-scale&quot;&gt;Operating Kafka at scale&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment model&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Walmart’s Kafka infrastructure spans self-managed clusters on private cloud (via OneOps historically, WCNP currently) and public cloud (Azure and Google Cloud). The deployment is hybrid: Kafka brokers remain within Walmart’s infrastructure while consumer workloads run on WCNP across clouds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring stack (2016)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ning Zhang documented the monitoring stack in December 2016. Walmart forked Yahoo’s Kafka Manager and added custom monitoring graphs. jmxtrans bridges Kafka JMX metrics to Graphite and Ganglia backends; Grafana is layered on top of Graphite for dashboards. Alerts covered: Kafka process down, under-replicated partitions, leader loss, low disk space, and high CPU utilisation.&lt;/p&gt;
&lt;p&gt;No sourced material describes how the monitoring stack evolved post-2016 when Walmart moved to WCNP and Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Producer and consumer tuning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For the replenishment system, Pattnaik documented explicit tuning of &lt;code&gt;linger.ms&lt;/code&gt;, &lt;code&gt;batch.size&lt;/code&gt; (16,000 bytes), and &lt;code&gt;acks=all&lt;/code&gt; on the producer side, with &lt;code&gt;max.poll.records&lt;/code&gt;, &lt;code&gt;max.poll.interval.ms&lt;/code&gt;, and session timeout configured on the consumer side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker hardware&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For the inventory system, Suman Pattnaik specified that brokers use multiple disks with RAID configurations for &lt;code&gt;log.dirs&lt;/code&gt; storage, with consumer counts aligned to partition counts and CPU/memory maintained at around 50% utilisation headroom.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Developer experience&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The self-serving OneOps model (documented by Ning Zhang in 2016) gave teams a GUI for deploying dedicated Kafka clusters, covering operations, monitoring, auto-repair, auto-replacement, and auto-scaling. Anil Kumar’s team built internal tooling for tracking pipeline flows, SLA metrics, message send/receive latencies per producer and consumer, and alerting on backlogs and throughput degradation. A Kafka REST proxy was deployed so that non-JVM services could produce and consume without native Kafka client libraries.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-how-they-solved-them&quot;&gt;Challenges and how they solved them&lt;/h2&gt;
&lt;h3 id=&quot;consumer-rebalancing-causing-lag-at-scale&quot;&gt;Consumer rebalancing causing lag at scale&lt;/h3&gt;
&lt;p&gt;Ravinder Matte’s team described consumer group rebalancing as “the most common challenge in operationalising Kafka consumers at scale” in the June 2024 post. At Walmart’s scale — 25,000+ consumers — rebalances triggered by pod scaling, rolling deployments, or processing delays exceeding &lt;code&gt;max.poll.interval.ms&lt;/code&gt; caused high consumer lag that disrupted SLAs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Messaging Proxy Service decouples Kafka partition consumption from consumer services. Most rebalances are now eliminated except for rare cluster restarts or network events.&lt;/p&gt;
&lt;h3 id=&quot;slow-kafka-streams-standby-recovery&quot;&gt;Slow Kafka Streams standby recovery&lt;/h3&gt;
&lt;p&gt;Changelog topics holding gigabytes of state for the Customer Data Platform caused very slow recovery when a Kafka Streams node failed, as the standby had to replay the entire log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Cold Bootstrap — the standby copies the active node’s RocksDB snapshot directly via JSch, then resumes from the saved offset. This avoids replaying the changelog, achieves zero event loss, and eliminates the need for indefinite changelog retention. (Deepak Goyal, Kafka Summit NYC 2019.)&lt;/p&gt;
&lt;h3 id=&quot;inability-to-elastically-scale-kafka-streams&quot;&gt;Inability to elastically scale Kafka Streams&lt;/h3&gt;
&lt;p&gt;Kafka Streams lacked native repartitioning support, which prevented the Customer Data Platform from scaling its stateful applications without full restarts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Walmart added dynamic repartitioning to Kafka Streams, redistributing state across new partitions at runtime. This was later contributed to the community via the KIP process. (Deepak Goyal, Kafka Summit NYC 2019.)&lt;/p&gt;
&lt;h3 id=&quot;availability-vs-consistency-in-fraud-detection&quot;&gt;Availability vs. consistency in fraud detection&lt;/h3&gt;
&lt;p&gt;Kafka Streams’ default behaviour blocks reads during consumer rebalances. For a fraud detection application running on every transaction, this was incompatible with latency SLAs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Walmart contributed KIP-535 and KIP-562 to Apache Kafka, explicitly choosing availability over strict consistency for this workload. “You’re basically trading consistency for availability,” Brar stated in a TechTarget interview. (Navinder Pal Singh Brar, Kafka Summit 2020.)&lt;/p&gt;
&lt;h3 id=&quot;shared-cluster-resource-contention&quot;&gt;Shared cluster resource contention&lt;/h3&gt;
&lt;p&gt;Early shared bare-metal clusters suffered from capacity competition between tenants, no authentication or isolation, and buggy clients exhausting resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Migrated to dedicated clusters via OneOps, giving each team its own Kafka cluster with GUI-driven operations, auto-repair, and auto-scaling. (Ning Zhang, November 2016.)&lt;/p&gt;
&lt;h3 id=&quot;cassandra-cdc-record-fan-out&quot;&gt;Cassandra CDC record fan-out&lt;/h3&gt;
&lt;p&gt;A single Cassandra write in a 3-region, RF=3 deployment generates 9 CDC records, arriving out of order and containing only changed columns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Debezium 1.9 in continuous log processing mode, combined with Redis-backed RedisBloom Bloom filters partitioned hourly, deduplicates records at 60,000 changes per second in production. Walmart also enhanced Debezium to process commit logs without waiting for file completion. (Scott Harvester and Nitin Chhabra, July 2022.)&lt;/p&gt;
&lt;h3 id=&quot;poison-pill-messages-causing-head-of-line-blocking&quot;&gt;Poison-pill messages causing head-of-line blocking&lt;/h3&gt;
&lt;p&gt;Unprocessable messages blocked partition consumption in direct consumer deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The Messaging Proxy Service routes failed messages to a Dead Letter Queue with retry logic, isolating them from the main processing path. (Ravinder Matte et al., June 2024.)&lt;/p&gt;
&lt;h2 id=&quot;full-tech-stack&quot;&gt;Full tech stack&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Message broker&lt;/td&gt;
&lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;td&gt;Kafka 0.10.1.0 documented in 2016; current version not specified in sourced material&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream processing&lt;/td&gt;
&lt;td&gt;Kafka Streams, Apache Storm (Trident), Apache Spark Streaming&lt;/td&gt;
&lt;td&gt;Kafka Streams: Customer Data Platform, fraud detection, inventory; Storm: Druid ingestion; Spark: inventory and A/B testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State store&lt;/td&gt;
&lt;td&gt;RocksDB&lt;/td&gt;
&lt;td&gt;Local state store for Kafka Streams applications in the CDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectors&lt;/td&gt;
&lt;td&gt;Kafka Connect (HDFS sink), Debezium 1.9 (Cassandra CDC source)&lt;/td&gt;
&lt;td&gt;HDFS connector for long-term storage; Debezium enhanced for continuous log processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-datacenter replication&lt;/td&gt;
&lt;td&gt;Kafka MirrorMaker (patched)&lt;/td&gt;
&lt;td&gt;Internal patches added complete topic renaming to prevent name collisions across sites (2016)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP interface&lt;/td&gt;
&lt;td&gt;Kafka REST Proxy&lt;/td&gt;
&lt;td&gt;Enables non-JVM producers and consumers (2016)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;Apache Druid&lt;/td&gt;
&lt;td&gt;Sub-second OLAP queries on nearly 1 billion+ events/day; pre-aggregation at ingestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational database&lt;/td&gt;
&lt;td&gt;Apache Cassandra&lt;/td&gt;
&lt;td&gt;Primary operational store for inventory and customer data; CDC source for Debezium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deduplication&lt;/td&gt;
&lt;td&gt;Redis, RedisBloom&lt;/td&gt;
&lt;td&gt;Bloom-filter deduplication for Cassandra CDC fan-out at 60,000 changes/second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage sinks&lt;/td&gt;
&lt;td&gt;Apache HDFS, Apache Hudi&lt;/td&gt;
&lt;td&gt;HDFS via Kafka Connect for long-term retention; Hudi for lakehouse table format (as of 2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring (2016)&lt;/td&gt;
&lt;td&gt;Kafka Manager (Yahoo fork, enhanced), jmxtrans, Graphite, Ganglia, Grafana&lt;/td&gt;
&lt;td&gt;Custom monitoring graphs added to Kafka Manager; Grafana layered on Graphite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster coordination&lt;/td&gt;
&lt;td&gt;Apache ZooKeeper&lt;/td&gt;
&lt;td&gt;Referenced in 2019 material; current coordination mechanism not specified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment / infra (legacy)&lt;/td&gt;
&lt;td&gt;OneOps (OpenStack-based)&lt;/td&gt;
&lt;td&gt;Self-serve Kafka cluster deployment with GUI, auto-repair, and auto-scaling (pre-WCNP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment / infra (current)&lt;/td&gt;
&lt;td&gt;WCNP (Walmart Cloud Native Platform)&lt;/td&gt;
&lt;td&gt;Kubernetes-based multi-cloud orchestration spanning Azure and Google Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal proxy&lt;/td&gt;
&lt;td&gt;Messaging Proxy Service (MPS)&lt;/td&gt;
&lt;td&gt;Internal HTTP proxy decoupling Kafka consumption from consumer pod scaling (2024)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client libraries&lt;/td&gt;
&lt;td&gt;Spring Kafka, Akka Streams&lt;/td&gt;
&lt;td&gt;Spring Kafka in replenishment system; Akka Streams for early reactive microservice consumption (2016)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public cloud&lt;/td&gt;
&lt;td&gt;Azure, Google Cloud&lt;/td&gt;
&lt;td&gt;Both used for WCNP workloads; Azure hosts Cassandra CDC pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;key-contributors&quot;&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ning Zhang&lt;/strong&gt; (Software Engineer, Walmart Global Tech): Authored two foundational 2016 posts on Walmart’s Kafka ecosystem and multi-datacenter architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anil Kumar&lt;/strong&gt; (Global eCommerce Engineer, Walmart Labs): Authored the 2016 Confluent post on Kafka for item setup and near-real-time search indexing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suman Pattnaik&lt;/strong&gt; (Director of Engineering, Walmart): Architected real-time inventory and replenishment platforms; co-presented at Kafka Summit SF 2019; authored Confluent blog posts (2020, 2022)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prasanna Subburaj&lt;/strong&gt; (Engineer, Walmart): Co-presented “When Kafka meets the scaling and reliability needs of world’s largest retailer” at Kafka Summit SF 2019&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deepak Goyal&lt;/strong&gt; (Engineer, Walmart Labs, Customer Backbone team): Developed Cold Bootstrap, dynamic repartitioning, and rack-aware task assignment for Kafka Streams; presented at Kafka Summit NYC 2019&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navinder Pal Singh Brar&lt;/strong&gt; (Staff Engineer, Walmart Global Tech, Customer Data Platform): Founding member of Walmart’s CDP; contributed KIP-535 and KIP-562 to Apache Kafka; holds 4 US patents; Confluent Community Catalyst&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Amaresh Nayak&lt;/strong&gt; (Engineer, Walmart Global Tech): Authored the 2017 post on Druid + Kafka event stream analytics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scott Harvester&lt;/strong&gt; (Engineer, Walmart Global Tech): Co-authored the 2022 Cassandra CDC solution post&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nitin Chhabra&lt;/strong&gt; (Engineer, Walmart Global Tech): Co-authored the 2022 Cassandra CDC post&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ravinder Matte&lt;/strong&gt; (Engineer, Walmart Global Tech): Lead author of the 2024 Messaging Proxy Service post&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chris Kasten&lt;/strong&gt; (VP of Walmart Cloud): Discussed Walmart’s real-time platform in Kafka Summit SF 2019 keynote Q&amp;amp;A&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;key-takeaways-for-your-own-kafka-implementation&quot;&gt;Key takeaways for your own Kafka implementation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decouple consumers from partitions before you hit rebalance problems.&lt;/strong&gt; Walmart’s Messaging Proxy Service (2024) addressed a challenge that many large-scale Kafka deployments encounter: consumer group rebalances at pod-scale. If you’re running containerised consumers on Kubernetes, consider whether a proxy layer that absorbs Kafka consumption and exposes stateless REST endpoints would reduce operational complexity before the problem becomes acute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consider Kafka Streams changelog recovery carefully when your state is large.&lt;/strong&gt; Walmart’s Cold Bootstrap approach (replacing changelog replay with direct RocksDB snapshot copy via JSch) addressed a real recovery bottleneck. If your Kafka Streams state stores are measured in gigabytes, the default changelog replay strategy may result in slow standby recovery. Understanding this trade-off early lets you design around it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Many small clusters often work better than one large shared cluster.&lt;/strong&gt; From 2016, Walmart operated purpose-built clusters per team and per pipeline rather than a shared monolith. This limits blast radius, allows per-cluster tuning, and avoids cross-team coordination overhead in operations like broker upgrades and partition reassignments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom partitioners are worth the complexity when ordering or write-isolation matters.&lt;/strong&gt; Walmart’s murmur-hash partitioner, which routes each item-store combination to a single partition, prevented database deadlocks that would otherwise occur from concurrent writes. If your consumers write to a keyed data store, a custom partitioner aligned to the store’s key can eliminate a class of concurrency bugs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-source contributions can close gaps faster than internal workarounds.&lt;/strong&gt; Walmart’s KIP-535 and KIP-562 contributions (availability-first Kafka Streams for fraud detection) were eventually merged into Apache Kafka. Contributing upstream meant the solution became part of the core, reducing the maintenance burden of carrying a private fork.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sources-and-further-reading&quot;&gt;Sources and further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Ning Zhang, “&lt;a href=&quot;https://medium.com/walmartglobaltech/tech-transformation-real-time-messaging-at-walmart-8787f5ab19e8&quot;&gt;Tech Transformation: Real-time Messaging at Walmart&lt;/a&gt;,” Walmart Global Tech Blog, November 2016&lt;/li&gt;
&lt;li&gt;Ning Zhang, “&lt;a href=&quot;https://medium.com/walmartglobaltech/kafka-ecosystem-on-walmarts-cloud-983570dff1f2&quot;&gt;Kafka Ecosystem on Walmart’s Cloud&lt;/a&gt;,” Walmart Global Tech Blog, December 2016&lt;/li&gt;
&lt;li&gt;Anil Kumar, “&lt;a href=&quot;https://www.confluent.io/blog/apache-kafka-item-setup/&quot;&gt;Apache Kafka for Item Setup at Walmart&lt;/a&gt;,” Confluent Blog (Walmart Labs), October 2016&lt;/li&gt;
&lt;li&gt;Amaresh Nayak, “&lt;a href=&quot;https://medium.com/walmartglobaltech/event-stream-analytics-at-walmart-with-druid-dcf1a37ceda7&quot;&gt;Event Stream Analytics at Walmart with Druid&lt;/a&gt;,” Walmart Global Tech Blog, November 2017&lt;/li&gt;
&lt;li&gt;Suman Pattnaik and Prasanna Subburaj, “&lt;a href=&quot;https://www.slideshare.net/slideshow/when-kafka-meets-the-scaling-and-reliability-needs-of-worlds-largest-retailer-a-walmart-story-suman-pattnaik-prasanna-subburaj-walmart-kafka-summit-sf-2019/179754163&quot;&gt;When Kafka Meets the Scaling and Reliability Needs of World’s Largest Retailer&lt;/a&gt;,” Kafka Summit SF 2019&lt;/li&gt;
&lt;li&gt;Deepak Goyal, “&lt;a href=&quot;https://videos.confluent.io/watch/6DvdkkKuDco4WKJvzVYhyU&quot;&gt;Kafka Streams at Scale: Walmart’s Approach&lt;/a&gt;,” Kafka Summit NYC 2019&lt;/li&gt;
&lt;li&gt;Navinder Pal Singh Brar, “&lt;a href=&quot;https://conferences.oreilly.com/strata/strata-ny-2019/public/schedule/detail/77038.html&quot;&gt;Multi-tenant Kafka Streams CDP at Walmart&lt;/a&gt;,” Strata Data Conference New York, September 2019&lt;/li&gt;
&lt;li&gt;Suman Pattnaik, “&lt;a href=&quot;https://www.confluent.io/blog/walmart-real-time-inventory-management-using-kafka/&quot;&gt;Real-time Inventory Management at Walmart Using Kafka&lt;/a&gt;,” Confluent Blog, May 2020&lt;/li&gt;
&lt;li&gt;Navinder Pal Singh Brar, “&lt;a href=&quot;https://www.confluent.io/resources/kafka-summit-2020/availability-first-kafka-streams-at-walmart-scale/&quot;&gt;Availability-first Kafka Streams at Walmart Scale&lt;/a&gt;,” Kafka Summit 2020&lt;/li&gt;
&lt;li&gt;Sean Michael Kerner (citing Navinder Pal Singh Brar), “&lt;a href=&quot;https://www.techtarget.com/searchdatamanagement/news/252488259/Kafka-users-Northrop-Grumman-Walmart-highlight-event-streaming&quot;&gt;Kafka Users Northrop Grumman, Walmart Highlight Event Streaming&lt;/a&gt;,” TechTarget, August 2020&lt;/li&gt;
&lt;li&gt;Suman Pattnaik, “&lt;a href=&quot;https://www.confluent.io/blog/how-walmart-uses-kafka-for-real-time-omnichannel-replenishment/&quot;&gt;How Walmart Uses Kafka for Real-time Omnichannel Replenishment&lt;/a&gt;,” Confluent Blog, May 2022&lt;/li&gt;
&lt;li&gt;Scott Harvester and Nitin Chhabra, “&lt;a href=&quot;https://medium.com/walmartglobaltech/walmarts-cassandra-cdc-solution-6fc650031a3&quot;&gt;Walmart’s Cassandra CDC Solution&lt;/a&gt;,” Walmart Global Tech Blog, July 2022&lt;/li&gt;
&lt;li&gt;Samuel Guleff, “&lt;a href=&quot;https://medium.com/walmartglobaltech/lakehouse-at-fortune-1-scale-480bcb10391b&quot;&gt;Lakehouse at Fortune 1 Scale&lt;/a&gt;,” Walmart Global Tech Blog, May 2023&lt;/li&gt;
&lt;li&gt;Ravinder Matte, Vilas Athavale, Sid Anand et al., “&lt;a href=&quot;https://medium.com/walmartglobaltech/reliably-processing-trillions-of-kafka-messages-per-day-23494f553ef9&quot;&gt;Reliably Processing Trillions of Kafka Messages Per Day&lt;/a&gt;,” Walmart Global Tech Blog, June 2024&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re monitoring a Kafka environment at scale, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; offers a free 30-day trial that connects to any Kafka cluster in minutes and deploys via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Kafka</category><author>Factor House</author></item><item><title>Data governance for Kafka: introducing lineage support in Factor Platform</title><link>https://factorhouse.io/articles/data-governance-for-kafka-introducing-lineage-support-in-factor-platform/</link><guid isPermaLink="true">https://factorhouse.io/articles/data-governance-for-kafka-introducing-lineage-support-in-factor-platform/</guid><description>Learn how Factor Platform brings OpenLineage metadata into your Kafka environment, making data ownership, PII classification, and lineage visible by default.</description><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Factor House, many of the teams we work with operate in highly regulated industries. &lt;a href=&quot;/case-studies/nord-lb&quot;&gt;NORD/LB&lt;/a&gt;, a German commercial bank, is a good illustration of why banking tends to be the most demanding of these. As an institution subject to both BCBS 239 and DORA, they are required to maintain clear, demonstrable data lineage across their environment: knowing where data originates, who owns it, and how it flows through their systems. Running Kafka at scale, they needed that lineage to be visible and actionable within the tools used to operate their streaming infrastructure, not isolated in a separate catalog that engineers rarely consult during day-to-day work.&lt;/p&gt;
&lt;p&gt;This gap prompted us to build data governance support into &lt;a href=&quot;/products/factor-platform&quot;&gt;Factor Platform&lt;/a&gt;, our unified management layer for streaming infrastructure. This article explains what we’ve built, why it matters for regulated organisations, and the approach we took to make it practical.&lt;/p&gt;
&lt;h2 id=&quot;the-regulatory-context&quot;&gt;The regulatory context&lt;/h2&gt;
&lt;p&gt;European banks are subject to two frameworks that place direct requirements on data governance: BCBS 239 and DORA.&lt;/p&gt;
&lt;p&gt;BCBS 239, the Basel Committee’s principles for effective risk data aggregation and risk reporting, requires systemically important financial institutions to demonstrate clear data lineage, defined data ownership, and the ability to trace data from source to consumption. For banks running event-driven architectures, this means being able to answer questions like: where does this data come from, who owns it, and does it contain regulated information?&lt;/p&gt;
&lt;p&gt;DORA, the EU’s Digital Operational Resilience Act, adds further requirements around understanding ICT systems and their data dependencies as part of broader operational resilience obligations.&lt;/p&gt;
&lt;p&gt;Both frameworks assume that an organisation has a coherent view of its data. In practice, for teams running Kafka at scale, that view has historically been difficult to maintain.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-with-existing-approaches&quot;&gt;The problem with existing approaches&lt;/h2&gt;
&lt;p&gt;Most organisations with a data governance programme have invested in a data catalog. These catalogs accumulate metadata about datasets: ownership, classification, documentation, PII flags, and so on. That metadata is typically described using the &lt;a href=&quot;https://openlineage.io/&quot;&gt;OpenLineage standard&lt;/a&gt;, a vendor-neutral specification for representing datasets, jobs, and their relationships.&lt;/p&gt;
&lt;p&gt;The challenge is that this metadata tends to live in a separate system from the Kafka tooling. Engineers operating Kafka can browse topics and schemas, but the governance context (who owns this schema, whether it contains PII, whether it meets the organisation’s data quality standards) isn’t visible alongside it. The two systems are not integrated, and bridging them has typically required additional infrastructure and dedicated integration work.&lt;/p&gt;
&lt;h2 id=&quot;our-approach-the-schema-as-the-source-of-truth&quot;&gt;Our approach: the schema as the source of truth&lt;/h2&gt;
&lt;p&gt;Factor Platform’s lineage support is built around a straightforward observation: Kafka schemas already exist and are version-controlled. Rather than requiring a separate metadata store or additional infrastructure, we treat the schema itself as the place where governance metadata lives.&lt;/p&gt;
&lt;p&gt;You annotate your existing Avro or JSON Schema definitions with lineage metadata - ownership, tags, documentation, PII classification, domain, and any custom attributes your organisation requires - using whatever field structure makes sense for your environment. Factor Platform then uses kJQ expressions to map that embedded metadata to valid OpenLineage Dataset facets, producing a structured, OpenLineage-conforming description of each dataset from your Schema Registry.&lt;/p&gt;
&lt;p&gt;This means there is no additional system to run, no separate pipeline to maintain, and no duplication of your schema definitions. The metadata travels with the schema, which is where it belongs.&lt;/p&gt;
&lt;p&gt;Mappings can be configured through the Factor Platform UI using a guided wizard, or through the API. Once in place, Factor Platform evaluates them on each schema observation cycle and surfaces the results across the interface. Datasets are versioned, so you have a full history of changes over time, including when an owner was added, when a tag changed, and when a new schema version introduced a new PII field.&lt;/p&gt;
&lt;h2 id=&quot;what-this-makes-possible&quot;&gt;What this makes possible&lt;/h2&gt;
&lt;p&gt;Bringing OpenLineage metadata into your Kafka operational environment changes what you can see and act on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Identifying PII-tagged data.&lt;/strong&gt; Column-level mappings allow individual fields within a schema to carry their own lineage metadata. A field like email or date_of_birth can be annotated with a PII flag and a classification. Factor Platform surfaces this when you browse schemas, so engineers and compliance teams can see at a glance which topics carry regulated fields.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding ownership gaps.&lt;/strong&gt; Ownership is a required facet in many governance frameworks. When a schema has no owner mapping, or when the owner field is null or malformed, Factor Platform reports it as a dataset quality issue. This gives you a filtered view of all schemas in your environment that are missing the governance properties your organisation requires, surfacing them before an auditor does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catching schema quality issues early.&lt;/strong&gt; Dataset mappings can mark fields as required. If an expected metadata field is absent or returns an unexpected value, Factor Platform flags it. This shifts schema governance from a periodic manual review process to a continuous, automated check that runs on every observation cycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filtering and operating with context.&lt;/strong&gt; Once lineage metadata is extracted, it becomes a first-class filter in the Factor Platform UI. You can browse schemas by domain, by owner, by tag, or by any custom attribute your mapping configuration exposes. For a team managing hundreds of schemas across multiple clusters, this makes governance questions significantly more practical to answer quickly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a17f8924d077d0205f99270_factor-platform-governance-filters.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Applying filters in Factor Platform using OpenLineage metadata&lt;/p&gt;
&lt;h2 id=&quot;whats-coming-next&quot;&gt;What’s coming next&lt;/h2&gt;
&lt;p&gt;The current release focuses on Schema Registry as the lineage source. In future releases, Factor Platform will also consume OpenLineage events from other producers in your stack, including Flink jobs and Iceberg tables, and will produce OpenLineage events that you can feed into other systems in your governance toolchain.&lt;/p&gt;
&lt;h2 id=&quot;speak-to-our-team&quot;&gt;Speak to our team&lt;/h2&gt;
&lt;p&gt;Factor Platform is currently in &lt;a href=&quot;#try-platform&quot;&gt;early access&lt;/a&gt;. If you’re working in a regulated environment and want to understand how lineage support could work for your Kafka setup, &lt;a href=&quot;https://lp.factorhouse.io/meetings/chad-harris&quot;&gt;book a time&lt;/a&gt; with our team.&lt;/p&gt;
</content:encoded><category>Product</category><author>Chad Harris</author></item><item><title>Accelerating incident response: advanced filters, streaming search, and AI-powered queries</title><link>https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries/</link><guid isPermaLink="true">https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries/</guid><description>Fix streaming data failures faster. Learn how Kpow uses advanced kJQ filtering, BYO AI, and Streaming Search to slash incident response times.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In the first part of this series, we explored how establishing foundational data inspection allows teams to turn raw, unparsed data dumps into readable, shaped payloads. However, achieving visibility is only the first half of the equation. When a production incident occurs, engineering teams must optimize for speed.&lt;/p&gt;
&lt;p&gt;Finding a rare anomaly across millions of Kafka messages requires high-velocity search capabilities. This article explores the root causes of friction during critical investigations and demonstrates how Kpow combines server-side filtering, artificial intelligence, and continuous scanning to accelerate incident response.&lt;/p&gt;
&lt;p&gt;This is Part 2 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-data-management-with-kpow&quot;&gt;Kafka Data Management with Kpow: Unlocking Engineering Productivity&lt;/a&gt; series. You can read the full strategy in the main series article and access the associated posts as they become available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow&quot;&gt;Foundational Kafka Data Inspection: Shaping Payloads and Optimizing Visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows&quot;&gt;Triage, Repair, and Replay: Integrated Kafka Remediation Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies&quot;&gt;Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-friction-in-incident-response&quot;&gt;Problem: Friction in Incident Response&lt;/h2&gt;
&lt;p&gt;Application teams frequently face a Velocity Gap during production outages. High-volume topics process thousands of events per second. Locating a specific, rare failure requires sifting through millions of messages. To find these precise events, developers must construct complex search logic to match timestamps, evaluate nested array values, or isolate specific error codes.&lt;/p&gt;
&lt;p&gt;Writing this logic introduces a steep syntax learning curve. Figuring out the exact code to filter deeply nested data takes time, which directly delays incident response. When engineers are forced to focus on how to query the data rather than analyzing the data itself, resolution times increase dramatically.&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-manual-search-workflows&quot;&gt;Limitations of Manual Search Workflows&lt;/h2&gt;
&lt;p&gt;Standard operational workflows often fail under the pressure of an active incident. Engineers using basic consumer scripts are forced to manually advance offsets, repeatedly executing commands and hoping to catch the right event before consumer timeouts occur.&lt;/p&gt;
&lt;p&gt;This trial-and-error approach scales poorly. During an outage, developers struggle with basic JSON parsers that cannot handle complex type conversions or temporal math. To bypass the learning curve, teams rely on sharing massive text files of old, brittle query commands. Relying on static cheat sheets rather than dynamic search tools adds severe friction to the resolution process.&lt;/p&gt;
&lt;h2 id=&quot;advanced-filtering-ai-and-streaming-search-in-kpow&quot;&gt;Advanced Filtering, AI, and Streaming Search in Kpow&lt;/h2&gt;
&lt;p&gt;Kpow eliminates the friction of manual polling and syntax struggles by providing a high-velocity search engine. By integrating advanced kJQ filtering, artificial intelligence, and automated query progression, Kpow transforms incident response into a frictionless workflow.&lt;/p&gt;
&lt;h3 id=&quot;precision-with-advanced-kjq-filtering&quot;&gt;Precision with Advanced kJQ Filtering&lt;/h3&gt;
&lt;p&gt;To isolate highly specific events, Kpow provides &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/kjq/manual&quot;&gt;advanced kJQ filtering&lt;/a&gt;. This JQ-like language executes directly on the server, allowing Kpow to rapidly slice through data before it ever reaches the browser. kJQ natively supports complex logical operators, type casting, and precise ISO 8601 duration mathematics.&lt;/p&gt;
&lt;p&gt;For example, an engineer investigating un-audited, high-value provisional trades over the last two hours can execute the following advanced filter:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.value.partner.network == &quot;VISA&quot; and .value.trade.status == &quot;provisional&quot; and .value.trade.compliance.audit == false and .value.trade.price | to-double &gt; 40 and .timestamp &gt; now - pt120m&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This single query isolates specific nested string values, evaluates a boolean field (&lt;em&gt;compliance.audit&lt;/em&gt;), casts a string-based price to a double for mathematical comparison, and applies a strict 120-minute temporal window using record metadata.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a0573d90fb7f77e43950d17_advanced-filter.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;eliminating-the-syntax-barrier-with-ai&quot;&gt;Eliminating the Syntax Barrier with AI&lt;/h3&gt;
&lt;p&gt;While advanced kJQ is powerful, writing complex syntax during a high-pressure outage can be daunting. To make this functionality instantly accessible to all engineers, Kpow integrates &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/ai-model&quot;&gt;Bring Your Own AI (BYO AI)&lt;/a&gt; capabilities supporting AWS Bedrock, OpenAI, Anthropic, and Ollama.&lt;/p&gt;
&lt;p&gt;Operators can bypass the syntax learning curve entirely by using natural language prompts. If an engineer needs to find specific transactions, they simply type their request into the AI prompt interface:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Show me all trades where partner network is VISA&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kpow’s AI integration instantly translates this natural language request into a schema-aware, syntactically validated kJQ filter. This allows developers to interrogate complex data structures effortlessly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a057445121598ca4099db86_ai-filter.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;continuous-discovery-with-streaming-search&quot;&gt;Continuous Discovery with Streaming Search&lt;/h3&gt;
&lt;p&gt;Finding a rare event often means searching beyond the initial batch of results. Instead of forcing users to manually click to continue polling, Kpow offers &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/streaming-search&quot;&gt;Streaming Search&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By enabling the Streaming Search option, Kpow automatically and continuously progresses the query. The search runs persistently until the defined result limits are reached or the topic partitions are completely exhausted. This replaces manual polling with automated discovery, allowing engineers to define their search criteria via natural language or kJQ while Kpow handles the continuous data scanning.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a05746dfcfbe445fa8378ad_streaming-search.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In modern streaming architectures, minimizing the time to resolution requires highly automated, intelligent search capabilities. By combining advanced server-side data slicing with continuous streaming search, Kpow eliminates the need for manual offset tracking. Furthermore, by integrating AI models, Kpow removes the syntax learning curve, allowing any developer to instantly generate complex filters using natural language.&lt;/p&gt;
&lt;p&gt;Once the problematic event is isolated, teams must take action to correct the pipeline. In the next part of this series, &lt;strong&gt;Triage, Repair, and Replay: Integrated Kafka Remediation Workflows&lt;/strong&gt;, we will explore how Kpow enables teams to safely manage consumer offsets, re-inject dead-letter queue messages, and repair data payloads directly from the UI.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Defense in depth: unifying RBAC and data policies for transparent governance</title><link>https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies/</link><guid isPermaLink="true">https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies/</guid><description>Balance Kafka velocity and compliance. Learn how Kpow uses RBAC and Data Policies for safe, self-service production debugging without manual tickets.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Throughout this series, we have explored how to shape data for foundational visibility, accelerate investigations with AI, and integrate workflows to repair broken pipelines. However, granting developers the power to inspect and modify production data introduces significant risk.&lt;/p&gt;
&lt;p&gt;Balancing engineering velocity with regulatory compliance is a major enterprise challenge. Giving application teams access to production topics often exposes Personally Identifiable Information (PII) or sensitive financial records. This article explores the friction inherent in generic security models and demonstrates how Kpow unifies access control and payload redaction to safely empower developers.&lt;/p&gt;
&lt;p&gt;This is Part 4 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-data-management-with-kpow&quot;&gt;Kafka Data Management with Kpow: Unlocking Engineering Productivity&lt;/a&gt; series. You can read the full strategy in the main series article and access the associated posts below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow&quot;&gt;Foundational Kafka Data Inspection: Shaping Payloads and Optimizing Visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries&quot;&gt;Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows&quot;&gt;Triage, Repair, and Replay: Integrated Kafka Remediation Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4:&lt;/strong&gt; Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-compliance-risk-in-production-environments&quot;&gt;Problem: Compliance Risk in Production Environments&lt;/h2&gt;
&lt;p&gt;As organizations scale their streaming infrastructure, developers inevitably need to debug production issues. A failed transaction or a stalled consumer requires application teams to look at the exact data causing the problem.&lt;/p&gt;
&lt;p&gt;This creates a severe “Compliance Gap”. Production topics contain sensitive data. Regulatory constraints (such as GDPR, HIPAA, or SOC2) strictly prohibit exposing unmasked PII, financial records, or secure tokens to broad engineering teams. Organizations must find a way to let developers fix their applications without violating these critical privacy laws.&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-manual-access-control&quot;&gt;Limitations of Manual Access Control&lt;/h2&gt;
&lt;p&gt;To mitigate compliance risks, platform administrators typically enforce a total lockdown. They block all direct access to production Kafka clusters for anyone outside of a small group of trusted infrastructure engineers.&lt;/p&gt;
&lt;p&gt;While this solves the security problem, it creates a massive operational bottleneck. Developers are forced into disconnected, manual ticketing processes. When an incident occurs, an application engineer must submit a ticket and wait for a platform engineer to manually execute CLI scripts, extract the relevant records, sanitize the logs by hand, and return the safe data. This bureaucratic process destroys developer velocity and wastes valuable platform engineering time.&lt;/p&gt;
&lt;h2 id=&quot;unified-declarative-governance-with-kpow&quot;&gt;Unified Declarative Governance with Kpow&lt;/h2&gt;
&lt;p&gt;Kpow eliminates this operational bottleneck by introducing transparent governance. By utilizing a “Defense in Depth” approach, Kpow secures both administrative actions and data payloads using a shared, declarative YAML resource taxonomy.&lt;/p&gt;
&lt;h3 id=&quot;tier-1-action-level-control-with-granular-rbac&quot;&gt;Tier 1: Action-Level Control with Granular RBAC&lt;/h3&gt;
&lt;p&gt;The first layer of defense controls what actions a user can perform. Kpow provides highly granular &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;Role Based Access Control (RBAC)&lt;/a&gt;, allowing administrators to map Identity Provider roles to precise permissions.&lt;/p&gt;
&lt;p&gt;Platform administrators can safely grant a &lt;code&gt;TOPIC_INSPECT&lt;/code&gt; action to application teams (such as a &lt;code&gt;kafka-readers&lt;/code&gt; role) so they can view data. Simultaneously, administrators can enforce an implicit deny or explicit &lt;code&gt;Stage&lt;/code&gt; effect on sensitive mutations like &lt;code&gt;TOPIC_CREATE&lt;/code&gt; or &lt;code&gt;TOPIC_PRODUCE&lt;/code&gt;. If a user with read-only access attempts to execute an unauthorized infrastructure change, Kpow actively prevents the action and logs the attempt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a163ca9f6d3038c009c7c2d_rbac-deny.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;tier-2-payload-level-control-with-data-policies&quot;&gt;Tier 2: Payload-Level Control with Data Policies&lt;/h3&gt;
&lt;p&gt;Action-level access is necessary but insufficient on its own. If a developer is permitted to inspect a topic, they still cannot be allowed to see raw financial data. Kpow solves this by layering &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-policies&quot;&gt;Data Policies&lt;/a&gt; directly on top of inspection rules.&lt;/p&gt;
&lt;p&gt;Data Policies apply nested redaction to specific fields automatically. For example, consider an &lt;code&gt;orders&lt;/code&gt; topic containing a sensitive &lt;code&gt;order_id&lt;/code&gt; and a financial &lt;code&gt;amount&lt;/code&gt;. Administrators can define a policy that applies a &lt;code&gt;ShowFirst4&lt;/code&gt; redaction to the &lt;code&gt;order_id&lt;/code&gt; and a &lt;code&gt;Full&lt;/code&gt; redaction to the &lt;code&gt;amount&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;When an authorized developer inspects the topic to debug a stalled order, Kpow automatically renders the masked record. The developer can verify the operational status of the payload without ever exposing the restricted values.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a163cd508622ee55b1d350c_data-policy.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ensuring-fail-safe-protection&quot;&gt;Ensuring Fail-Safe Protection&lt;/h3&gt;
&lt;p&gt;To guarantee regulatory compliance, Kpow builds fail-safe mechanics into its transparent governance model. If a schema evolves and a previously simple string field becomes a complex nested object, Kpow utilizes a conservative fallback mechanism. Rather than risking exposure by failing to apply a partial redaction, the system automatically defaults to a &lt;code&gt;Full&lt;/code&gt; redaction for that field.&lt;/p&gt;
&lt;p&gt;Furthermore, Kpow intentionally disables String SerDes options in the UI when Data Policies are active. This architectural choice prevents users from bypassing JSON or Avro deserialization to read the raw, unmasked bytes directly from the topic.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Combining granular RBAC with automated Data Policies completely breaks the disconnected ticketing bottleneck. Platform administrators no longer need to act as manual data extractors. By mathematically guaranteeing data security at both the action and payload levels, platform teams can finally offer safe, self-service debugging in production environments.&lt;/p&gt;
&lt;p&gt;This concludes our four-part series on enterprise Kafka data management. By closing the Visibility, Velocity, Remediation, and Compliance gaps, organizations can eliminate operational friction. Unifying data inspection, AI-powered search, pipeline repair, and transparent governance ultimately transforms Kafka from a complex infrastructure burden into an engine that unlocks true engineering productivity.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Foundational Kafka data inspection: shaping payloads and optimizing visibility</title><link>https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow/</link><guid isPermaLink="true">https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow/</guid><description>Stop fighting complex Kafka serialization. Learn how Kpow uses Auto SerDes, kJQ, and transparent queries to streamline data inspection.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;While Kafka provides massive throughput, extracting actionable signal from high-volume streams presents a significant operational challenge that can easily disrupt enterprise engineering velocity. Among these challenges, the most significant is the “Visibility Gap”. This inherent opacity of streaming data leaves application teams struggling to parse complex payloads and locate specific events during critical incidents.&lt;/p&gt;
&lt;p&gt;Turning raw Kafka payloads into readable, searchable data is the first step toward faster operational response. This article explores the root causes of data opacity and demonstrates how Kpow provides a structured, UI-driven approach to foundational data inspection to close the “Visibility Gap”.&lt;/p&gt;
&lt;p&gt;This is Part 1 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-data-management-with-kpow&quot;&gt;Kafka Data Management with Kpow: Unlocking Engineering Productivity&lt;/a&gt; series. You can read the full strategy in the main series article and access the associated posts as they become available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; Foundational Kafka Data Inspection: Shaping Payloads and Optimizing Visibility &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries&quot;&gt;Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows&quot;&gt;Triage, Repair, and Replay: Integrated Kafka Remediation Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies&quot;&gt;Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-opacity-in-high-volume-topics&quot;&gt;Problem: Opacity in High-Volume Topics&lt;/h2&gt;
&lt;p&gt;Application teams often lack direct insight into their topic data. In a typical microservices environment, a single topic might process thousands of messages per second containing deeply nested JSON or Avro structures. During a routine investigation, developers face overwhelming visual noise when trying to read these massive payloads.&lt;/p&gt;
&lt;p&gt;Furthermore, teams suffer from a lack of search context regarding partition-level scanning progress. When searching across millions of messages for a specific correlation ID, developers have no idea how far along a partition scan has progressed or if they have completely exhausted the available offsets. This lack of feedback turns event discovery into an opaque, uncertain process.&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-disconnected-cli-tools&quot;&gt;Limitations of Disconnected CLI Tools&lt;/h2&gt;
&lt;p&gt;To solve these challenges, engineers often rely on standard open-source consumer scripts. This approach immediately introduces significant configuration overhead. Because enterprise environments utilize various data formats, engineers are forced to manually supply complex schema registry flags and deserialization properties for every single query they execute.&lt;/p&gt;
&lt;p&gt;Once the consumer is finally configured, the output creates another bottleneck. Terminal screens are flooded with raw, unparsed payloads. Without the ability to shape or filter the output effectively, it becomes nearly impossible to quickly identify the relevant fields required for an investigation.&lt;/p&gt;
&lt;h2 id=&quot;streamlined-inspection-with-kpow&quot;&gt;Streamlined Inspection with Kpow&lt;/h2&gt;
&lt;p&gt;Kpow eliminates the friction of CLI-based investigations by providing a powerful &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview&quot;&gt;data inspect engine&lt;/a&gt; for querying and filtering topic data. This enables application teams to query topics directly from the browser while shaping the data for maximum readability.&lt;/p&gt;
&lt;h3 id=&quot;instant-schema-inference-with-auto-serdes&quot;&gt;Instant Schema Inference with Auto SerDes&lt;/h3&gt;
&lt;p&gt;To remove manual configuration overhead, Kpow features an intelligent Auto SerDes option. Instead of manually specifying deserializers and registry URLs, application teams simply select a topic. Kpow automatically analyzes the stream, infers the correct format (such as Avro, Protobuf, or JSON), and deserializes the payload correctly. This capability provides immediate data inspection without requiring prior knowledge of topic serialization formats.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a02801881abf50ac8b210e2_data-01.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;filtering-and-shaping-payloads-with-kjq-and-projection-expressions&quot;&gt;Filtering and Shaping Payloads with kJQ and Projection Expressions&lt;/h3&gt;
&lt;p&gt;Once data is accessible, finding the right events and improving payload readability become critical. Kpow addresses the search challenge by introducing &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/kjq/manual&quot;&gt;kJQ filters&lt;/a&gt;, a fast, JQ-like filtering language built specifically for Kafka topics. Designed for high performance, kJQ can easily scan tens of thousands of messages per second, allowing developers to rapidly search and isolate specific events directly on the server.&lt;/p&gt;
&lt;p&gt;After isolating the relevant records, users can refine the visual output using &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/projections/tutorial&quot;&gt;Projection Expressions&lt;/a&gt;. This feature enables developers to extract specific fields from a record by providing a comma-separated list of kJQ object identifiers.&lt;/p&gt;
&lt;p&gt;You can apply projection expressions to both the key and value fields of Kafka records. This is especially useful when dealing with extremely large payloads where you only need a subset of fields. For example, applying &lt;em&gt;&lt;code&gt;.value.user_id, .value.transaction.status&lt;/code&gt;&lt;/em&gt; to a massive JSON payload collapses the result into a clean, targeted subset of data. This shapes the output for immediate human readability and eliminates visual clutter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a02805875ece32858325a1f_data-02.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;transparent-query-context&quot;&gt;Transparent Query Context&lt;/h3&gt;
&lt;p&gt;To solve the problem of blind polling, Kpow surfaces detailed execution metrics directly in the UI. By expanding the &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview#results-metadata-table&quot;&gt;Results Metadata Table&lt;/a&gt; within the query results toolbar, users gain instant feedback on partition-level scanning progress.&lt;/p&gt;
&lt;p&gt;The table displays the query start and end offsets, the exact number of scanned records, and the remaining offsets available beyond the query window. This query transparency guarantees that engineers always know exactly how much of the topic has been searched and whether a partition is fully exhausted.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a02808036070508d3c25634_data-03.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;optimizing-inspection-for-enterprise-scale&quot;&gt;Optimizing Inspection for Enterprise Scale&lt;/h3&gt;
&lt;p&gt;To support fast, concurrent searches across large engineering teams, administrators can precisely tune Kpow’s underlying &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview#configuration&quot;&gt;query infrastructure&lt;/a&gt; to match their specific cluster capacity and user demand.&lt;/p&gt;
&lt;p&gt;Rather than relying on a rigid, one-size-fits-all approach, platform teams can adjust core configuration settings to balance developer velocity against broker load:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Worker Concurrency:&lt;/strong&gt; Administrators can scale the number of active workers handling search requests, allowing multiple users to execute complex queries simultaneously without experiencing queuing bottlenecks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer Thread Pools:&lt;/strong&gt; By increasing the number of consumer threads assigned to each worker, teams can enable parallel partition scanning to significantly improve overall query throughput.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Protection:&lt;/strong&gt; Configurable query timeouts ensure that long-running searches do not monopolize cluster resources, while tunable poll durations allow administrators to optimize performance for topics containing exceptionally large messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In modern streaming architectures, the ability to inspect and search live data quickly is no longer optional. Kpow reduces the time between detecting an issue and understanding it. By eliminating configuration friction and providing tools to shape nested payloads, Kpow transforms Kafka data inspection into a streamlined, self-service workflow. Developers no longer need to wrestle with terminal outputs or guess schema formats. With transparent query context and optimized concurrent workers, application teams gain the baseline insight required to maintain complex streaming architectures.&lt;/p&gt;
&lt;p&gt;Once foundational readability is established, teams can tackle more complex, high-pressure scenarios. In the next part of this series, &lt;strong&gt;Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries&lt;/strong&gt;, we will explore how Kpow accelerates investigations with advanced kJQ filtering, automates continuous data scanning, and leverages AI to generate complex query syntax instantly.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Kafka Data Management with Kpow: Unlocking Engineering Productivity</title><link>https://factorhouse.io/articles/kafka-data-management-with-kpow/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-data-management-with-kpow/</guid><description>Enterprise Kafka adoption promises massive scalability and decoupled agility. However, interacting with complex streaming data at scale often bogs developers down in manual operational friction. By identifying four critical friction points across visibility, velocity, remediation, and compliance, this article introduces a comprehensive data management strategy to eliminate bottlenecks and unlock engineering productivity with Kpow.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Event-driven architectures promise massive scalability and decoupled agility. In reality, the day-to-day experience of building and maintaining these systems often reveals underlying friction. While infrastructure teams have mastered the art of keeping brokers online, application teams frequently face obstacles when trying to access, debug, or correct the actual payloads flowing through their topics.&lt;/p&gt;
&lt;p&gt;This disconnect creates a drag on engineering velocity. When a transaction fails in production, a downstream database crashes, or a poison pill stalls a consumer, developers need instant access to structured data. Instead, teams are slowed down by manual data configuration, forced to build custom tools to find specific events in high-volume streams, or blocked by restrictive permissions that prevent them from accessing the data they need.&lt;/p&gt;
&lt;p&gt;Organizations require a structural shift away from fragmented command-line tasks toward a frictionless, self-service developer experience. This article outlines a comprehensive strategy to identify and mitigate the four critical gaps causing friction in enterprise Kafka data management, demonstrating how Kpow serves as the unified engine to unlock engineering productivity.&lt;/p&gt;
&lt;p&gt;To see how these concepts translate into practical workflows, follow along with our upcoming four-part technical guide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow&quot;&gt;Foundational Kafka Data Inspection: Shaping Payloads and Optimizing Visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries&quot;&gt;Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows&quot;&gt;Triage, Repair, and Replay: Integrated Kafka Remediation Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies&quot;&gt;Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;four-critical-gaps-in-enterprise-data-management&quot;&gt;Four Critical Gaps in Enterprise Data Management&lt;/h2&gt;
&lt;p&gt;Unlocking developer productivity requires identifying and mitigating the operational bottlenecks that occur when teams interact with Kafka data.&lt;/p&gt;
&lt;h3 id=&quot;visibility-gap-opacity-of-high-volume-data&quot;&gt;Visibility Gap: Opacity of High-Volume Data&lt;/h3&gt;
&lt;p&gt;Application teams often lack immediate visibility into topic data. During an investigation, determining partition-level scanning progress can be difficult, and large, deeply nested JSON or Avro payloads are frequently hard to parse. Engineers often rely on standard CLI consumers, which require manual reconfiguration of schema registry properties for different topics and typically output unformatted payloads. Furthermore, configuring performant, secure inspection tools in containerized environments presents an ongoing challenge for platform administrators.&lt;/p&gt;
&lt;h3 id=&quot;velocity-gap-friction-of-incident-response&quot;&gt;Velocity Gap: Friction of Incident Response&lt;/h3&gt;
&lt;p&gt;Finding a specific event in a high-volume topic often requires sifting through millions of messages. Isolating a single failure buried within deeply nested JSON payloads requires developers to write complex search queries or command-line filters. Figuring out the exact code to filter this data takes time, which directly delays incident response. Standard workflows force teams to write custom parsing scripts, manually step through offsets, or rely on shared text files of old query commands. During a high-pressure outage, this trial-and-error approach to finding data adds severe friction to the resolution process.&lt;/p&gt;
&lt;h3 id=&quot;remediation-gap-fragmented-pipeline-repair&quot;&gt;Remediation Gap: Fragmented Pipeline Repair&lt;/h3&gt;
&lt;p&gt;Fixing data pipelines extends beyond identifying a schema mismatch that stalls a consumer. Teams must frequently triage Dead Letter Queues (DLQs) for validation errors, or rewind consumer offsets when a downstream database crashes. However, extracting this data, unblocking the consumer, and repairing the pipeline involve separate, fragmented workflows. Developers may write custom scripts to find errors or bulk-publish missing headers, while simultaneously coordinating with platform engineers to manually reset consumer offsets via CLI commands. This fragmentation introduces delays in restoring data pipelines and extracting clean data for reporting.&lt;/p&gt;
&lt;h3 id=&quot;compliance-gap-risk-of-exposed-payloads&quot;&gt;Compliance Gap: Risk of Exposed Payloads&lt;/h3&gt;
&lt;p&gt;Organizations face strict regulatory constraints regarding &lt;em&gt;Personally Identifiable Information (PII)&lt;/em&gt;. Providing application teams with unrestricted access to production topics introduces compliance risks. To mitigate this, platform administrators often restrict access, requiring developers to use ticketing systems to request data. Platform engineers must then manually extract and sanitize logs before returning the data, creating an operational bottleneck that slows down development and debugging.&lt;/p&gt;
&lt;h2 id=&quot;kpow-solution-unifying-data-workflows&quot;&gt;Kpow Solution: Unifying Data Workflows&lt;/h2&gt;
&lt;p&gt;Overcoming these gaps requires a unified platform that provides deep inspection capabilities, efficient search, integrated remediation, and robust data governance. Kpow addresses these requirements across four key operational dimensions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69eb0b7bf55e0775287fab46_kpow-solution.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;bridging-visibility-with-foundational-data-inspection&quot;&gt;Bridging Visibility with Foundational Data Inspection&lt;/h3&gt;
&lt;p&gt;Kpow brings structured clarity to cluster data through its &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview&quot;&gt;Data Inspect&lt;/a&gt; capabilities. By leveraging Auto SerDes, Kpow infers schemas automatically, eliminating the manual configuration overhead associated with traditional consumers. Users can apply &lt;em&gt;kJQ filters&lt;/em&gt; and &lt;em&gt;Projection Expressions&lt;/em&gt; to extract specific nested fields, simplifying data analysis. Additionally, the &lt;em&gt;Results Metadata Table&lt;/em&gt; provides transparent, partition-level query context and offset exhaustion metrics. Platform teams can also tune worker configuration variables to ensure stable, performant access across the enterprise.&lt;/p&gt;
&lt;h3 id=&quot;accelerating-velocity-with-ai-and-streaming-search&quot;&gt;Accelerating Velocity with AI and Streaming Search&lt;/h3&gt;
&lt;p&gt;To accelerate incident response, Kpow equips teams with powerful tools for high-velocity data analysis. Users can leverage advanced kJQ filtering for rapid, server-side data slicing, for example by evaluating date mathematics or extracting data from embedded JSON arrays. To make this advanced syntax instantly accessible and eliminate the traditional learning curve, Kpow integrates &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/ai-model&quot;&gt;Bring Your Own AI (BYO AI)&lt;/a&gt; capabilities supporting AWS Bedrock, OpenAI, Anthropic, and Ollama. Operators can simply use natural language prompts to automatically generate schema-aware, syntactically validated kJQ filters. Furthermore, &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/streaming-search&quot;&gt;Streaming Search&lt;/a&gt; provides continuous, automatic query progression until result limits are reached or topics are completely exhausted.&lt;/p&gt;
&lt;h3 id=&quot;closing-remediation-with-integrated-workflows&quot;&gt;Closing Remediation with Integrated Workflows&lt;/h3&gt;
&lt;p&gt;Kpow consolidates the remediation lifecycle for a wide range of pipeline failures. For serialization errors, users can leverage one of the deserialization options (such as &lt;code&gt;Poison only&lt;/code&gt;) to easily isolate malformed data. If a downstream database fails, Kpow incorporates &lt;a href=&quot;https://docs.factorhouse.io/kpow/management/groups#group-actions-overview&quot;&gt;consumer group actions&lt;/a&gt;, allowing operators to explicitly skip, clear, or reset offsets, for example, by rewinding to a specific timestamp to replay historical data. For DLQ triage, native UI routing allows teams to send queried results directly to &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-produce&quot;&gt;Data Produce&lt;/a&gt;. This enables operators to correct validation errors, append missing headers, and seamlessly re-inject messages, or generate safe tombstone records for targeted deletion. Throughout these workflows, Kpow supports data downloads in standard business formats for offline auditing as well.&lt;/p&gt;
&lt;h3 id=&quot;ensuring-compliance-with-defense-in-depth&quot;&gt;Ensuring Compliance with Defense in Depth&lt;/h3&gt;
&lt;p&gt;Kpow helps reduce reliance on manual ticketing through transparent data governance, framing security as a two-tiered model using a shared YAML resource taxonomy. In the first tier, granular &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;Role Based Access Control (RBAC)&lt;/a&gt; allows administrators to grant &lt;code&gt;TOPIC_INSPECT&lt;/code&gt; permissions while explicitly denying or staging sensitive &lt;code&gt;TOPIC_PRODUCE&lt;/code&gt; actions. In the second tier, &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-policies&quot;&gt;Data Policies&lt;/a&gt; layer onto inspection rules to apply nested redaction (such as &lt;code&gt;ShowLast4&lt;/code&gt; on a credit card field). This preventative control enables self-service debugging in production while maintaining PII protection.&lt;/p&gt;
&lt;h2 id=&quot;achieving-engineering-productivity&quot;&gt;Achieving Engineering Productivity&lt;/h2&gt;
&lt;p&gt;Improving engineering productivity requires reducing the operational friction between developers and their streaming data. It demands a strategy that provides clear visibility, accelerates search workflows, integrates data and pipeline repair, and transparently secures both administrative actions and sensitive payloads. By adopting a unified workflow for Kafka data management, organizations can minimize administrative bottlenecks, enable self-service operations, and better leverage their streaming architecture.&lt;/p&gt;
&lt;p&gt;Equipping teams with the right tools makes Kafka a more accessible and efficient data backbone. By streamlining data discovery, remediation, and compliance workflows, Kpow enables engineering organizations to focus their efforts on building resilient, real-time applications.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Kafka scaling best practices: An in-depth primer</title><link>https://factorhouse.io/articles/kafka-scaling-best-practices/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-scaling-best-practices/</guid><description>A practical guide to scaling Apache Kafka in production, covering partitioning strategy, consumer group design, broker sizing, KRaft migration, and more.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kafka is designed to scale, but it does not scale automatically. The decisions you make at the beginning, around partition counts, replication, consumer group design, and broker sizing, determine whether your cluster grows cleanly or accumulates operational debt that becomes expensive to unwind later.&lt;/p&gt;
&lt;p&gt;This article pulls from published engineering work at &lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber&lt;/a&gt;, &lt;a href=&quot;/articles/pinterest-kafka-architecture&quot;&gt;Pinterest&lt;/a&gt;, &lt;a href=&quot;/articles/cloudflare-kafka-architecture&quot;&gt;Cloudflare&lt;/a&gt;, &lt;a href=&quot;/articles/shopify-kafka-architecture&quot;&gt;Shopify&lt;/a&gt;, &lt;a href=&quot;/articles/wix-kafka-architecture&quot;&gt;Wix&lt;/a&gt;, and &lt;a href=&quot;/articles/robinhood-kafka-architecture&quot;&gt;Robinhood&lt;/a&gt;, alongside Apache KIPs and Confluent’s production documentation, to give you a grounded view of what scaling Kafka actually looks like in practice.&lt;/p&gt;
&lt;h2 id=&quot;how-to-scale-a-kafka-cluster&quot;&gt;How to scale a Kafka cluster&lt;/h2&gt;
&lt;p&gt;Scaling a Kafka cluster means increasing its capacity across four separate dimensions: brokers, partitions, consumers, and storage. Each one is independent, and each has its own ceiling and operational cost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adding brokers&lt;/strong&gt; increases raw throughput capacity, but by itself does nothing for existing topics. Kafka does not automatically rebalance partitions to new brokers. Only newly created topics land on a new broker; for everything already running, you need to trigger a reassignment explicitly, either via a manual kafka-reassign-partitions run or through a tool like Cruise Control. LinkedIn built Cruise Control specifically because manually managing partition distribution across a cluster of 4,000 brokers is not practical. Cruise Control treats broker load, disk utilisation, rack distribution, and leader balance as optimisation goals rather than manual inputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increasing partition counts&lt;/strong&gt; is the primary lever for producer and consumer throughput on a given topic, because partitions are the unit of parallelism. The standard formula is max(t/p, t/c), where t is target throughput, p is per-partition produce throughput, and c is per-partition consume throughput, measured on your hardware. The caveat is that for keyed topics you cannot increase partition count on a live topic without breaking key-to-partition routing, so the decision needs to be made before traffic arrives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scaling consumers&lt;/strong&gt; is bounded by partition count within a consumer group. You can add consumer instances freely, but any instance in excess of the partition count sits idle. For slow-consumer workloads or when partition count cannot increase, the options are Confluent’s Parallel Consumer for in-process parallelism, or a consumer proxy architecture. Uber’s uForwarder, Wix’s gRPC fan-out proxy, and Robinhood’s Kafkaproxy sidecar each converge on the same pattern: consume once per topic, fan out to downstream consumers, and decouple throughput scaling from partition counts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scaling storage&lt;/strong&gt; has historically meant scaling brokers proportionally. Tiered storage, now GA in Kafka 3.9 via KIP-405, changes this by offloading cold log segments to object storage. Uber has been running tiered storage in production for several years and describes faster broker recovery as one of the main operational wins: a new or replaced broker no longer needs to re-replicate terabytes of cold data to become current.&lt;/p&gt;
&lt;p&gt;Each of these levers interacts with the others. What follows covers the specific configuration decisions, documented trade-offs, and patterns from production deployments that determine whether scaling each dimension is clean or operationally expensive.&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-scale-ceiling-first&quot;&gt;Understanding the scale ceiling first&lt;/h2&gt;
&lt;p&gt;Before making configuration decisions, it helps to calibrate against what production Kafka clusters actually handle. &lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn&lt;/a&gt; operates more than 100 clusters with 4,000 brokers and roughly 7 million partitions, processing over 7 trillion messages per day. Pinterest pushes more than 800 billion messages per day at 15 million messages per second peak, across more than 2,000 brokers in AWS. Shopify peaked at 66 million messages per second during Black Friday and Cyber Monday.&lt;/p&gt;
&lt;p&gt;These numbers are useful not because your cluster needs to match them, but because the teams that built them have documented what broke along the way.&lt;/p&gt;
&lt;h2 id=&quot;partitioning-strategy&quot;&gt;Partitioning strategy&lt;/h2&gt;
&lt;h3 id=&quot;how-many-partitions-to-create&quot;&gt;How many partitions to create&lt;/h3&gt;
&lt;p&gt;The canonical sizing formula, from Jun Rao’s Confluent post, remains the standard starting point: measure single-partition producer throughput (&lt;code&gt;p&lt;/code&gt;) and single-partition consumer throughput (&lt;code&gt;c&lt;/code&gt;) on your hardware, then calculate the required partition count as &lt;code&gt;max(t/p, t/c)&lt;/code&gt; for your target throughput &lt;code&gt;t&lt;/code&gt;. Add headroom beyond that, because consumer parallelism is bounded by partition count.&lt;/p&gt;
&lt;p&gt;For ZooKeeper-backed clusters, Confluent’s documentation recommends staying below 4,000 partitions per broker and 200,000 per cluster. Beyond these thresholds, controller failover slows noticeably because the controller propagates &lt;code&gt;LeaderAndIsr&lt;/code&gt; information serially. KRaft removes this bottleneck. Confluent’s lab benchmarks on a 2-million-partition cluster show drastically faster controlled-shutdown and uncontrolled-failure recovery compared to the ZooKeeper equivalent.&lt;/p&gt;
&lt;p&gt;Even on KRaft, partition count is still bounded by per-broker file-descriptor limits, replica-fetcher threads, and metadata RAM. These are practical ceilings, not architectural ones.&lt;/p&gt;
&lt;h3 id=&quot;keyed-topics-and-the-repartitioning-problem&quot;&gt;Keyed topics and the repartitioning problem&lt;/h3&gt;
&lt;p&gt;For keyed topics, over-provision at creation time. Once a topic is in production, you cannot increase partition count without breaking the &lt;code&gt;hash(key) % num_partitions&lt;/code&gt; mapping that routes messages to partitions. Confluent’s documentation is explicit about this. If you anticipate growth, it is considerably easier to start with more partitions than you need today than to deal with the downstream consequences of repartitioning a live topic.&lt;/p&gt;
&lt;p&gt;There are also cases where the standard rule breaks down in the other direction. Netflix’s Real-Time Distributed Graph uses a separate Kafka topic per node and edge type so each can be tuned and scaled independently. The New York Times stores every article since 1851 in a single infinite-retention partition, accepting the per-topic throughput ceiling because publication rate is low. These are deliberate trade-offs, not mistakes.&lt;/p&gt;
&lt;h3 id=&quot;avoiding-partition-skew&quot;&gt;Avoiding partition skew&lt;/h3&gt;
&lt;p&gt;Key distribution is the most common cause of hot partitions. LinkedIn’s answer to this was Cruise Control, a goal-based rebalancer that considers rack awareness, capacity, leader balance, and disk utilisation in its optimisation proposals. It is now the de facto partition rebalancer in the open ecosystem and ships with AWS MSK, Strimzi, and Canonical’s Charmed Kafka.&lt;/p&gt;
&lt;p&gt;When adding brokers, be aware that Kafka does not automatically move existing partitions to new brokers. Only newly created partitions land on a new broker. You need to run Cruise Control or trigger a reassignment explicitly. When doing so, throttle the reassignment using &lt;code&gt;--throttle &amp;lt;bytes/sec&amp;gt;&lt;/code&gt; to avoid saturating broker network and causing ISR shrinkage.&lt;/p&gt;
&lt;h2 id=&quot;producer-configuration&quot;&gt;Producer configuration&lt;/h2&gt;
&lt;h3 id=&quot;compression&quot;&gt;Compression&lt;/h3&gt;
&lt;p&gt;Compression is one of the higher-leverage tuning options available on the producer side. The available codecs are &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt;, &lt;code&gt;snappy&lt;/code&gt;, &lt;code&gt;lz4&lt;/code&gt;, and &lt;code&gt;zstd&lt;/code&gt;. Confluent’s general recommendation is &lt;code&gt;lz4&lt;/code&gt; for performance and &lt;code&gt;zstd&lt;/code&gt; when you want better compression ratios without the CPU cost of &lt;code&gt;gzip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/cloudflare-kafka-architecture&quot;&gt;Cloudflare&lt;/a&gt; published concrete benchmarks on their real &lt;code&gt;rrdns.recordbatch&lt;/code&gt; workload: Snappy achieved around 43% of original size at 446 MB/s compression throughput, while LZ4 reached 40% of original size at 594 MB/s compression and 2,428 MB/s decompression. Zstandard at level 1 achieved 24% of original size at 409 MB/s compression. Cloudflare ultimately standardised on Snappy due to Go client compatibility issues with LZ4 at the time. Their post also found that combining Snappy with 1-second batching produced an 8x size reduction on a low-volume topic.&lt;/p&gt;
&lt;p&gt;One point worth noting: if your producer and topic compression codecs do not match, the broker is forced to decompress and recompress each batch, which burns CPU. Keep them aligned.&lt;/p&gt;
&lt;h3 id=&quot;batching-and-lingerms&quot;&gt;Batching and &lt;code&gt;linger.ms&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In Apache Kafka 4.0, the default &lt;code&gt;linger.ms&lt;/code&gt; was changed from 0 to 5 milliseconds. The upstream rationale is that in production the throughput and compression gains nearly always outweigh the additional latency. This is now the default rather than a tuning trick.&lt;/p&gt;
&lt;p&gt;Robinhood’s migration to WarpStream for logging workloads illustrates the cost trade-off at the extreme end. By pushing batch sizes up significantly and accepting an increase in average produce latency from 0.2 seconds to 0.45 seconds, they achieved a 45% net cost reduction. That trade-off may not make sense for your workloads, but it does demonstrate how sensitive cost can be to batch sizing when storage is S3.&lt;/p&gt;
&lt;p&gt;The counter-example is Shopify’s BFCM 2025 scale testing, which found that partition increases, not larger batches, were the lever needed to maintain data freshness during traffic spikes. Producer batching and partition count address different constraints.&lt;/p&gt;
&lt;h2 id=&quot;consumer-group-scaling&quot;&gt;Consumer group scaling&lt;/h2&gt;
&lt;h3 id=&quot;the-partition-to-consumer-ceiling&quot;&gt;The partition-to-consumer ceiling&lt;/h3&gt;
&lt;p&gt;Within a consumer group, at most one consumer instance can be assigned to a partition. Excess consumers sit idle. This creates a hard ceiling: maximum parallelism equals the &lt;a href=&quot;/articles/kafka-topic-partition-best-practices&quot;&gt;topic’s partition&lt;/a&gt; count. If you cannot or do not want to add partitions to a live keyed topic, you have a few options.&lt;/p&gt;
&lt;p&gt;Confluent’s Parallel Consumer (open-source, Apache 2.0) lets a single consumer thread pool process messages from one partition in parallel, with three ordering modes: &lt;code&gt;KEY&lt;/code&gt;, &lt;code&gt;UNORDERED&lt;/code&gt;, and &lt;code&gt;PARTITION&lt;/code&gt;. It is specifically aimed at slow-consumer workloads involving database calls or HTTP requests. The trade-off is that exactly-once semantics are more complex to manage.&lt;/p&gt;
&lt;p&gt;At larger scale, a consumer proxy becomes the convergent answer. &lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber’s&lt;/a&gt; uForwarder, &lt;a href=&quot;/articles/wix-kafka-architecture&quot;&gt;Wix’s&lt;/a&gt; push-based gRPC fan-out proxy, and &lt;a href=&quot;/articles/robinhood-kafka-architecture&quot;&gt;Robinhood’s&lt;/a&gt; Kafkaproxy sidecar all address the same fundamental problem: the partition count ceiling, head-of-line blocking, and fragmented multi-language client behaviour. Wix found they were consuming each produced byte roughly four times across different consumer groups and built a proxy that consumes each topic once and fans out via gRPC, reducing their Kafka bill by 30%.&lt;/p&gt;
&lt;h3 id=&quot;rebalancing&quot;&gt;Rebalancing&lt;/h3&gt;
&lt;p&gt;The legacy eager rebalancing protocol revokes all partitions from all consumers on every rebalance event. For large consumer groups, this can pause consumption for seconds to minutes.&lt;/p&gt;
&lt;p&gt;KIP-429, introduced in Kafka 2.4 and the default since Kafka 3.0, provides incremental cooperative rebalancing via &lt;code&gt;CooperativeStickyAssignor&lt;/code&gt;. Only the partitions that need to move are revoked during a rebalance; all others continue processing. In environments running Kafka 3.0 or later, there is little reason to stay on the eager assignor.&lt;/p&gt;
&lt;p&gt;For Kubernetes deployments specifically, static membership (&lt;code&gt;group.instance.id&lt;/code&gt;, KIP-345) is the other essential lever. A consumer that restarts with the same instance ID does not trigger a rebalance, provided it returns within &lt;code&gt;session.timeout.ms&lt;/code&gt;. This matters in practice because autoscaling events that add or remove consumers can otherwise cause repeated rebalances that destabilise consumption, which is exactly the problem Robinhood ran into before building their proxy.&lt;/p&gt;
&lt;h3 id=&quot;monitoring-consumer-lag&quot;&gt;Monitoring consumer lag&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn’s&lt;/a&gt; Burrow remains one of the better approaches for &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;lag monitoring&lt;/a&gt;. Rather than relying on static thresholds, it consumes the &lt;code&gt;__consumer_offsets&lt;/code&gt; topic, tracks a sliding window per partition, and issues status judgements based on whether consumer offsets are advancing relative to broker offsets. This makes it far more reliable for wildcard consumers and MirrorMaker than threshold-based alerting.&lt;/p&gt;
&lt;p&gt;Cloudflare’s pattern is also worth noting: they auto-create a high-lag alert for every topic at topic-creation time, using time-based lag rather than offset-based lag. Time-based lag measures the difference between when the last committed offset was produced and when it was consumed, which is more meaningful for SLOs than raw offset numbers.&lt;/p&gt;
&lt;h2 id=&quot;broker-infrastructure&quot;&gt;Broker infrastructure&lt;/h2&gt;
&lt;h3 id=&quot;sizing&quot;&gt;Sizing&lt;/h3&gt;
&lt;p&gt;Kafka brokers are not typically CPU-bound unless TLS, compression, or a high partition count is involved. Pinterest found that enabling TLS at scale changed the per-connection cost significantly enough to require raising their broker heap from 4 GB to 8 GB.&lt;/p&gt;
&lt;p&gt;Memory allocation should prioritise the OS page cache over the JVM heap. Kafka relies heavily on the page cache for storing and serving messages. The standard recommendation is 4 to 8 GB of heap, with the rest of available RAM left for the page cache. Never co-locate Kafka with other memory-hungry processes.&lt;/p&gt;
&lt;p&gt;For GC configuration, the LinkedIn and Confluent baseline uses G1GC with &lt;code&gt;Xms&lt;/code&gt; and &lt;code&gt;Xmx&lt;/code&gt; set to the same value to avoid heap-resize jitter, a &lt;code&gt;MaxGCPauseMillis&lt;/code&gt; of 20, and &lt;code&gt;InitiatingHeapOccupancyPercent&lt;/code&gt; of 35. Netflix switched to Generational ZGC on Java 21 for sub-millisecond pauses on large-heap brokers. That is worth considering if you have a clear reason to run heaps larger than 16 GB, but G1GC remains the sensible baseline.&lt;/p&gt;
&lt;p&gt;At the OS level: set &lt;code&gt;nofile&lt;/code&gt; to 100,000 or more, configure &lt;code&gt;vm.swappiness=1&lt;/code&gt; to prevent the kernel from swapping out page-cached log data, raise network buffers, and mount storage with &lt;code&gt;noatime,nodiratime&lt;/code&gt; on XFS or ext4.&lt;/p&gt;
&lt;h3 id=&quot;rack-aware-replication&quot;&gt;Rack-aware replication&lt;/h3&gt;
&lt;p&gt;Configure &lt;code&gt;broker.rack&lt;/code&gt; to your AZ identifier so that replicas are distributed across zones with RF=3. Then enable follower fetching via &lt;code&gt;replica.selector.class=org.apache.kafka.common.replica.RackAwareReplicaSelector&lt;/code&gt; and set &lt;code&gt;client.rack&lt;/code&gt; on consumers. This allows consumers to prefer same-AZ replicas and can significantly reduce cross-AZ network costs, which are frequently the dominant cost line in cloud Kafka deployments.&lt;/p&gt;
&lt;p&gt;KIP-881, available since Kafka 3.4, extends this further by allowing &lt;code&gt;RangeAssignor&lt;/code&gt; and &lt;code&gt;CooperativeStickyAssignor&lt;/code&gt; to use rack data when assigning partitions to consumers.&lt;/p&gt;
&lt;h3 id=&quot;tiered-storage&quot;&gt;Tiered storage&lt;/h3&gt;
&lt;p&gt;Tiered storage separates compute and storage by offloading older log segments to object stores such as S3, GCS, or Azure Blob. KIP-405 reached GA in Kafka 3.9 (November 2024). Uber has been running tiered storage in production for several years and reports that the key wins are independent compute/storage scaling and faster broker recovery, since a new broker no longer needs to re-replicate terabytes of cold data on joining the cluster.&lt;/p&gt;
&lt;p&gt;If you are evaluating greenfield analytics-style Kafka workloads at scale, tiered storage is worth considering up front rather than retrofitting later. The cost gravity in the ecosystem is moving toward S3-backed storage, and the options for doing so are broader than they were two years ago.&lt;/p&gt;
&lt;h2 id=&quot;zookeeper-to-kraft-migration&quot;&gt;ZooKeeper to KRaft migration&lt;/h2&gt;
&lt;p&gt;This is not a future consideration. ZooKeeper mode was deprecated in Kafka 3.5 and removed in Kafka 4.0. Support for ZooKeeper-based clusters ends roughly November 2025. Confluent Platform 8.0 removed ZooKeeper. If you are running any ZooKeeper-backed Kafka cluster, migration planning should already be underway.&lt;/p&gt;
&lt;p&gt;KRaft became production-ready in Apache Kafka 3.3 and reached full feature parity with ZooKeeper mode in Kafka 3.9. The migration path involves provisioning a dedicated KRaft controller quorum, running through a dual-write phase where metadata is written to both ZooKeeper and KRaft, then rolling brokers to KRaft mode one at a time before decommissioning the ZooKeeper ensemble.&lt;/p&gt;
&lt;p&gt;A few things to be aware of before starting: there is no downgrade path once migration is finalised. SASL/SCRAM for controllers is not supported during migration. Brokers in dual-write mode use additional CPU and memory. And combined controller/broker mode is not supported for production migration targets.&lt;/p&gt;
&lt;p&gt;Confluent Cloud completed the largest known migration, moving thousands of clusters to KRaft without breaching SLAs. One widely cited outcome from a 50-node financial services cluster: controller failover time dropped from 5 to 7 seconds to under 1 second.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-can-help&quot;&gt;How Kpow can help&lt;/h2&gt;
&lt;p&gt;Scaling a Kafka cluster involves a lot of moving parts, and visibility is frequently the gap. When you are troubleshooting a partition skew problem, investigating consumer lag across dozens of groups, or working through the state of a reassignment, having clear access to your cluster’s internal state saves significant time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a Kafka management and &lt;a href=&quot;/articles/best-practices-kafka-data-observability&quot;&gt;observability tool&lt;/a&gt; built by Factor House. It gives you a live view of consumer group lag, partition assignment, topic configuration, and broker health in a single interface. If you are running multiple clusters, it supports those as a unified view as well. For teams working through the scaling changes described in this article, having that visibility during and after configuration changes reduces the feedback loop considerably.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fa4b765d7582b338c7d942_kpow-consumer-lag.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Viewing Consumer Groups and their metrics in Kpow&lt;/p&gt;
&lt;p&gt;You can connect Kpow to any Kafka cluster in minutes and &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;try it free for 30 days&lt;/a&gt;, whether you are running on bare metal, Kubernetes, or a managed service.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The decisions that constrain Kafka at scale tend to compound. Partition counts that seemed reasonable at launch become structural limits. Consumer groups on legacy rebalancing protocols create operational instability under autoscaling. ZooKeeper clusters that were not migrated before support ended become a liability.&lt;/p&gt;
&lt;p&gt;Most of the patterns that work at scale, cooperative rebalancing, rack-aware follower fetching, KRaft, Cruise Control for partition balance, and tiered storage for cost, are well-documented and available without commercial dependencies. The engineering blogs from &lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber&lt;/a&gt;, &lt;a href=&quot;/articles/cloudflare-kafka-architecture&quot;&gt;Cloudflare&lt;/a&gt;, and others are primary sources worth reading in full if you are operating at significant scale or heading toward it.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Triage, repair, and replay: integrated Kafka remediation workflows</title><link>https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows/</link><guid isPermaLink="true">https://factorhouse.io/articles/triage-repair-and-replay-integrated-kafka-remediation-workflows/</guid><description>Fix broken Kafka data pipelines fast. Learn how Kpow replaces messy CLI scripts with an intuitive UI to isolate, repair, and re-inject data.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the second part of this series, we demonstrated how combining advanced filtering, artificial intelligence, and streaming search accelerates investigations to help teams respond to incidents quickly. However, locating bad data is only half the battle. Once an issue is identified, engineering teams must take action to fix the pipeline.&lt;/p&gt;
&lt;p&gt;Resolving a stalled consumer, recovering from a downstream database crash, or fixing a dead-letter queue (DLQ) message requires managing both the consumer state and the data payload itself. This article explores the friction inherent in generic recovery workflows and demonstrates how Kpow unifies data extraction, state management, and payload correction into a single integrated process.&lt;/p&gt;
&lt;p&gt;This is Part 3 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-data-management-with-kpow&quot;&gt;Kafka Data Management with Kpow: Unlocking Engineering Productivity&lt;/a&gt; series. You can read the full strategy in the main series article and access the associated posts as they become available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/foundational-kafka-data-inspection-in-kpow&quot;&gt;Foundational Kafka Data Inspection: Shaping Payloads and Optimizing Visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/accelerating-incident-response-advanced-filters-ai-powered-queries&quot;&gt;Accelerating Incident Response: Advanced Filters, Streaming Search, and AI-Powered Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; Triage, Repair, and Replay: Integrated Kafka Remediation Workflows &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/defense-in-depth-unifying-rbac-and-data-policies&quot;&gt;Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-fragmented-pipeline-repair&quot;&gt;Problem: Fragmented Pipeline Repair&lt;/h2&gt;
&lt;p&gt;Fixing data pipelines extends beyond simply identifying a schema mismatch. A single malformed record (a poison pill) can crash a consumer thread. This causes the thread to catch the error, seek back to the problematic offset, and retry in an infinite loop. Because other partitions process normally, aggregated metrics often mask the failure, allowing lag to spike silently on a single partition.&lt;/p&gt;
&lt;p&gt;Alternatively, a downstream sink failure might require a complete historical data replay. Resolving these diverse issues requires identifying the exact malformed payload, modifying consumer group offsets, and fixing the underlying data. Currently, these tasks exist in completely disconnected operational silos.&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-siloed-recovery&quot;&gt;Limitations of Siloed Recovery&lt;/h2&gt;
&lt;p&gt;Generic workflows introduce severe friction when pipelines break. Developers are forced to context-switch, often writing one-off scripts to hunt down serialization errors or extract records for analysis.&lt;/p&gt;
&lt;p&gt;Meanwhile, repairing the pipeline state requires administrative access. Developers must file tickets and wait for platform engineers to run the Kafka Consumer Group CLI commands manually to reset or skip offsets. This disconnected ticketing process creates dangerous race conditions. If the consumer application restarts before the manual CLI command executes, the command will fail or conflict with the active consumer state. Furthermore, this process only addresses the consumer offset, leaving the actual malformed data uncorrected and missing from the downstream system.&lt;/p&gt;
&lt;h2 id=&quot;integrated-remediation-with-kpow&quot;&gt;Integrated Remediation with Kpow&lt;/h2&gt;
&lt;p&gt;Kpow eliminates the Remediation Gap by consolidating data inspection, consumer state management, and payload correction into a single interface. This allows operators to seamlessly transition from finding an issue to completely resolving it.&lt;/p&gt;
&lt;h3 id=&quot;isolating-malformed-data&quot;&gt;Isolating Malformed Data&lt;/h3&gt;
&lt;p&gt;When a consumer stalls, operators can move directly from the stalled partition to the underlying data. By opening the Consumer Details tab, engineers can spot rapidly accumulating lag on a specific partition. Clicking the &lt;strong&gt;Inspect data&lt;/strong&gt; action instantly opens the Data Inspect UI, pre-filtered to the affected partition, where operators can quickly apply a kJQ filter to pinpoint the exact offset causing the blockage.&lt;/p&gt;
&lt;p&gt;Alternatively, if the problematic offset is unknown, Kpow provides powerful &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-inspect/overview#deserialization-options&quot;&gt;Deserialization Options&lt;/a&gt; to cut through the noise of healthy traffic. By setting the strategy to &lt;em&gt;Poison only&lt;/em&gt;, operators can scan the topic to instantly filter out all successfully parsed records, isolating only the malformed data (such as an unexpected string instead of a required integer) that crashed the consumer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a066c531b73447d63a28002_isolate-data.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;unblocking-consumers-with-group-actions&quot;&gt;Unblocking Consumers with Group Actions&lt;/h3&gt;
&lt;p&gt;Once the problematic offset is identified, operators must unblock the pipeline safely. Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow/management/groups&quot;&gt;Consumers&lt;/a&gt; UI provides robust state management capabilities. From the action menu, operators can execute a &lt;em&gt;Skip offset&lt;/em&gt; at the partition level to immediately bypass the single poison pill. Kpow also supports &lt;em&gt;Clear offset&lt;/em&gt; and &lt;em&gt;Reset offset&lt;/em&gt; actions, which allow engineers to rewind a group to a specific offset, timestamp, or precise datetime to replay data after a sink failure.&lt;/p&gt;
&lt;p&gt;Crucially, Kpow protects the integrity of the cluster through Scheduled Mutations. Rather than forcing manual CLI commands that clash with active consumers, Kpow safely schedules the offset change. The platform waits for the consumer group to scale down to an &lt;em&gt;EMPTY&lt;/em&gt; state before automatically executing the mutation, completely preventing state conflicts and race conditions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a066c6e07ba6a8a48d9eec0_unblock-consumer.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;repairing-and-re-injecting-payloads&quot;&gt;Repairing and Re-injecting Payloads&lt;/h3&gt;
&lt;p&gt;Skipping a poison pill unblocks the consumer, but the data is still lost to the downstream system. To achieve complete remediation, teams must correct the payload and re-inject it into the pipeline.&lt;/p&gt;
&lt;p&gt;Kpow enables this through native UI routing. By highlighting the isolated bad record in the Data Inspect view and selecting &lt;strong&gt;Produce&lt;/strong&gt; from the drop-down menu, operators route the message directly into the &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-produce&quot;&gt;Data Produce&lt;/a&gt; form. The form opens pre-populated with the record’s details.&lt;/p&gt;
&lt;p&gt;Developers can execute a quick manual edit to the JSON payload (for example, fixing an invalid &lt;em&gt;“amount”: “ONE THOUSAND DOLLARS”&lt;/em&gt; field by replacing it with numeric &lt;em&gt;100&lt;/em&gt;) and produce the corrected message. This visually proves that the data was not just skipped, but successfully repaired and re-injected. For offline auditing, Kpow also offers robust downloading options to extract the problematic records in CSV, JSON, or EDN formats.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a066c8f03cba9e240602e8c_repair-payload.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By unifying data extraction, consumer offset management, and payload correction, Kpow eliminates the need for manual scripts and cross-team ticketing during a production outage. Operators can seamlessly isolate a poison pill, safely skip the consumer offset using scheduled mutations, and repair the data payload in a single, cohesive workflow. This comprehensive approach restores data pipelines rapidly and guarantees that no critical messages are lost.&lt;/p&gt;
&lt;p&gt;While these remediation capabilities are incredibly powerful, they must be governed securely. In the final part of this series, &lt;strong&gt;Defense in Depth: Unifying RBAC and Data Policies for Transparent Governance&lt;/strong&gt;, we will explore how organizations can deploy these tools safely in production without exposing sensitive information or violating compliance constraints.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>AKHQ: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/akhq/</link><guid isPermaLink="true">https://factorhouse.io/articles/akhq/</guid><description>AKHQ review for 2026: features, known limitations, pricing, and the best alternatives for teams that need more than open-source tooling.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AKHQ is the most complete free Kafka UI available, covering multi-cluster management, Schema Registry, Kafka Connect, consumer group monitoring, ACL management, and LDAP/OIDC authentication in a single self-hosted tool.&lt;/li&gt;
&lt;li&gt;Memory behaviour under high-throughput message tailing or large consumer group counts is a documented operational risk, with multiple out-of-memory reports at heap sizes up to 14 GB and no published fix.&lt;/li&gt;
&lt;li&gt;The authentication layer is more mature than most free alternatives, but reaching fine-grained multi-cluster RBAC required Michelin to contribute code directly to the project; audit logging is absent throughout.&lt;/li&gt;
&lt;li&gt;Reddit practitioners consistently frame AKHQ as a starting point for small teams, with a recurring pattern of switching to commercial tooling once teams feel pain around auditability, self-service workflows, or scale.&lt;/li&gt;
&lt;li&gt;AKHQ has no data masking, no JMX metrics visualisation, no alerting, and no partition or replica management from the UI, making it unsuitable for regulated environments or operators who need those capabilities.&lt;/li&gt;
&lt;li&gt;For teams that need enterprise RBAC, data masking, audit logging, or commercially backed support, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is worth evaluating as an alternative.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-akhq&quot;&gt;What is AKHQ?&lt;/h2&gt;
&lt;p&gt;AKHQ (formerly KafkaHQ) is an open-source Kafka management UI maintained by Ludovic Dehon (&lt;code&gt;tchiotludo&lt;/code&gt;) under the Apache 2.0 licence. The project is hosted at &lt;a href=&quot;https://github.com/tchiotludo/akhq&quot;&gt;https://github.com/tchiotludo/akhq&lt;/a&gt;. It is self-hosted, JVM-based, and built on Micronaut.&lt;/p&gt;
&lt;p&gt;A single AKHQ deployment connects to one or more Kafka clusters and exposes topic browsing, live message tailing, message production, consumer group monitoring, Schema Registry management, Kafka Connect management, ACL management, and role-based access control with LDAP and OIDC integration. AWS MSK IAM authentication is also supported.&lt;/p&gt;
&lt;p&gt;AKHQ has no commercial edition, no hosted SaaS version, and no paid support tier. All functionality is available in the open-source release. Enterprise users including Michelin and La Redoute have contributed features directly to the project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;AKHQ&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;akhq-review&quot;&gt;AKHQ review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;AKHQ covers a broad surface area for a free tool. Named reviewers credit it with multi-cluster management, message browsing, live tailing, Schema Registry, Kafka Connect, ACL management, and RBAC in a single deployment. [German Osin, Medium / Towards Data Science, 2 Sep 2021]&lt;/p&gt;
&lt;p&gt;The functional ceiling appears in a consistent set of areas. AKHQ does not support dynamic topic configuration, partition increase, replica change, Kafka Streams topology visualisation, or JMX metrics visualisation. [German Osin, same source; Zeenia Gupta, Platformatory blog, 5 Sep 2024] Protobuf support is partial: the schema registry integration is limited to the deserialiser side, and at least one user reports complete deserialization failure when using Protobuf with Apicurio Registry even when the descriptor-file flow works. [Zeenia Gupta, Platformatory blog, 5 Sep 2024; GitHub issue #1185] A related Avro bug causes AKHQ to fall back to raw message output when it cannot match a schema ID, leaving messages unparsed in the UI. [GitHub issue #1918, 2024]&lt;/p&gt;
&lt;p&gt;ksqlDB integration is listed in AKHQ’s configuration options but is effectively non-functional against Confluent Cloud endpoints. Configuring a Confluent ksqlDB endpoint produces a null pointer exception on the HTTP client (&lt;code&gt;Cannot invoke io.vertx.core.http.HttpClientRequest.response(...) because request is null&lt;/code&gt;). [sbourell, GitHub issue #1954, 24 Sep 2024] A separate discussion thread reports that ksqlDB does not appear in the menus at all despite correct configuration. [GitHub Discussion #1486]&lt;/p&gt;
&lt;p&gt;There is also a production workflow friction point: the message production dialog closes after each message, requiring the operator to reopen it and refill all fields when producing multiple messages in sequence. [xarx00, GitHub Discussion #805, Sep 2021]&lt;/p&gt;
&lt;p&gt;Data masking is absent. For any team handling PII or operating under HIPAA, PCI-DSS, or GDPR requirements, this is a hard compliance gap.&lt;/p&gt;
&lt;p&gt;AKHQ also has no built-in support for dead-letter queue (DLT) retry patterns or inline message payload correction. These capabilities require separate tooling, and the gap has driven some teams toward purpose-built event-streaming platforms that handle error recovery alongside broker management. [r/apachekafka, GitHub: i_built_a_spring_boot_starter_for_kafka_message, 2025]&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Docker Compose and Helm installation are two of the most consistently praised aspects of AKHQ. Getting a full local stack including Kafka, Schema Registry, Kafka Connect, and AKHQ running requires a single &lt;code&gt;docker compose pull&lt;/code&gt; followed by &lt;code&gt;docker compose up&lt;/code&gt;, giving access to everything at &lt;code&gt;localhost:8080&lt;/code&gt;. [AKHQ docs, akhq.io/docs/] Connections, users, groups, and schema registry links are defined in YAML and deployed via Helm, making the configuration GitOps-friendly. [Factor House, same URL above, 2026] Community feedback echoes this: one practitioner on r/apachekafka described it as “open source, can be installed on prem using their docker image, and it’s very lightweight.” [Drazul_, r/apachekafka, 2025]&lt;/p&gt;
&lt;p&gt;AKHQ 0.24.0 has been successfully deployed on Red Hat OpenShift 4 using Helm with Kafka 3.3.1; Rogerio Santos at Red Hat describes it as “an excellent complement to AMQ streams.” [Rogerio Santos, Red Hat Developer, 26 Jul 2023]&lt;/p&gt;
&lt;p&gt;The operational risk is memory. A documented memory pattern on AWS ECS at 4 GB RAM shows “an ascending stair shape” with little or no deallocation between scans. [arnaud-ly, GitHub issue #1141, 5 Jul 2022] High-throughput topic tailing triggers a &lt;code&gt;Direct buffer memory&lt;/code&gt; out-of-memory error even at 14 GB heap, which points to a missing backpressure mechanism rather than a simple heap sizing problem. [MichalPopielski, GitHub issue #1206, 13 Sep 2022] Neither issue has a published resolution. For clusters with many consumer groups, the AKHQ documentation itself recommends enabling &lt;code&gt;HIDE_EMPTY&lt;/code&gt; and setting &lt;code&gt;skip-consumer-groups: true&lt;/code&gt; as a workaround to keep startup responsive. [AKHQ docs, referenced in GitHub issue #271]&lt;/p&gt;
&lt;p&gt;Security scan failures have also been reported against the official Docker image at version 0.24.0, related to vulnerabilities in bundled Java libraries. [GitHub issue #1771]&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;AKHQ supports LDAP, OIDC, HTTP basic authentication, and external role and attribute claim mapping. MSK IAM support was contributed and merged. [Zeenia Gupta, Platformatory blog, 5 Sep 2024; GitHub issue #810] This is a broader authentication story than most free Kafka UIs offer, and it is one of the primary reasons practitioners choose AKHQ over Kafdrop.&lt;/p&gt;
&lt;p&gt;The limits become apparent under enterprise conditions. Michelin built their own resource-level RBAC layer on top of AKHQ because the default authorisation mechanisms were not granular enough for multi-cluster, multi-team use. Their contribution modified “all AKHQ features and authorization mechanisms (LDAP, OIDC, external claims)” and introduced an &lt;code&gt;AKHQSecured&lt;/code&gt; annotation for fine-grained control. [Alexis Souquiere, Michelin engineering blog, 5 Mar 2024] An internal survey at Michelin in 2023 “revealed widespread dissatisfaction” with small issues that accumulated over time, and the team considered building an alternative before choosing to contribute instead. [Alexis Souquiere, same source]&lt;/p&gt;
&lt;p&gt;Active auth bugs affect current releases. An Okta OIDC redirect loop sends authenticated users back to &lt;code&gt;/ui/login&lt;/code&gt; when they click a topic linked to a restricted consumer group, affecting AKHQ 0.25.1. [ndemyanchuk-booking, GitHub issue #2131, 25 Mar 2025] A separate regression sends users to the login page despite a 200 OK on the initial login request. [GitHub issue #2055] LDAPS configuration also carries documented friction. [GitHub issues #887, #1041]&lt;/p&gt;
&lt;p&gt;Audit logging is absent. Teams evaluating AKHQ alongside commercial alternatives will find that “AKHQ and Kafbat have basic RBAC but neither provides self-service workflows, team ownership models, audit logging, or the enterprise API that multi-team organisations need.” [Factor House, same URL above, 2026] Without audit logs, there is no record of who reset a consumer offset, modified a broker configuration, or produced a message - a meaningful constraint for any team operating under change-management or compliance requirements.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;The UI is functional and covers its stated scope, but it is consistently described as less polished and less responsive than &lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt; or modern &lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat&lt;/a&gt;. The structural complaints are recurring: search filters reset when navigating between views, and content reloads from the server rather than from a cached client-side state, which creates a sluggish experience when switching between topics or schemas. [xarx00, GitHub Discussion #805, Sep 2021] The maintainer acknowledged performance and navigation debt in the same thread, noting that schema registry caching would be a prerequisite for any direct topic-to-schema navigation improvement.&lt;/p&gt;
&lt;p&gt;German Osin’s characterisation from 2021 remains consistent with more recent accounts: AKHQ “is not the most convenient” and users “will definitely need to allocate some time” to learn it. [German Osin, Medium / Towards Data Science, 2 Sep 2021] Comparison reviewers describe Kafbat’s UI as “more modern” than AKHQ’s. [Factor House, 2026] The Reddit community surfaces the same friction directly: one engineer summarised it as “you get what you pay for… since you pay nothing, you get a clunky UI/UX. It does its job, but leaves more to be desired (especially after you use Conduktor).” [roastedsun, r/apachekafka, 2025] A platform architect put it in practical terms: “if it’s a startup and you just want visibility and basic management I’d try Kafka UI or AKHQ first. If you’re feeling pain around ‘who changed what’ and ‘how do we let devs self-serve without blowing stuff up’ then look harder at Conduktor or &lt;a href=&quot;/articles/lenses&quot;&gt;Lenses&lt;/a&gt;.” [TellersTech, r/apachekafka, 2025]&lt;/p&gt;
&lt;p&gt;On the positive side, a practitioner walkthrough from March 2026 frames AKHQ as “a control tower for the entire Kafka ecosystem,” removing the need to SSH into servers and run kafka shell scripts. [Naveen Mittal, Medium, 7 Mar 2026] Thoughtworks placed AKHQ in “Trial” status on their Technology Radar in 2022, citing topic browsing, Avro and Protobuf deserialization, and consumer group visibility as useful capabilities for teams working to understand data flows. [Thoughtworks Technology Radar, 29 Mar 2022] Day-to-day debugging tasks - message scanning, data discovery, and consumer group inspection - receive the most consistent praise from practitioners, and some larger engineering organisations run AKHQ alongside a commercial tool as a lightweight secondary viewer. [arcanumoid, r/apachekafka, 2025; r/apachekafka, urn8f3 thread, 2022]&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Schema Registry and Kafka Connect are well-supported and among the most cited reasons to choose AKHQ over lighter tools like Kafdrop. MSK IAM was added through a community contribution. Multi-cluster support is documented in production: Michelin uses AKHQ across on-premise factory clusters and cloud deployments to supervise topics, consumer groups, and connectors. [Alexis Souquiere, Michelin engineering blog, 5 Mar 2024] Rogerio Santos at Red Hat singles out multi-cluster support as a key advantage: “One of the features of AKHQ is its support for configuring multiple clusters, making it a convenient central GUI for managing multiple Kafka clusters.” [Red Hat Developer, 26 Jul 2023]&lt;/p&gt;
&lt;p&gt;The ksqlDB gap is the notable exception. The integration exists in configuration and was listed as a 0.24.0 feature contributed by 30+ contributors, but it does not function reliably against Confluent Cloud endpoints, and at least two community threads report it being absent from the UI despite correct configuration. [sbourell, GitHub issue #1954, 2024; GitHub Discussion #1486; akhq.io LinkedIn post on the 0.24.0 release]&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;AKHQ has no commercial support tier, no SLA, and no enterprise offering. The project is functionally a single-maintainer effort led by Ludovic Dehon, with major feature contributions from a small set of enterprise users. La Redoute’s distributed OSPO experience building an open-source Kafka product is documented; Michelin is a recognised core contributor. [La Redoute engineering blog, summarised via search result; Alexis Souquiere, Michelin engineering blog, 5 Mar 2024]&lt;/p&gt;
&lt;p&gt;Open issues can wait months for a response. The memory leak reported in issue #1141 dates to July 2022 with no published fix or maintainer comment. The Confluent ksqlDB issue #1954 was labelled “wait for reply” with no visible maintainer response in the thread. Documentation covers the happy path well but is thin on production tuning guidance and auth edge cases, with HTTPS Schema Registry configuration appearing as a recurring community discussion topic. [GitHub Discussion #575]&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;AKHQ suits individual engineers and small platform teams that need real authentication (LDAP/OIDC), Schema Registry and Connect management, MSK IAM support, and multi-cluster visibility in one self-hosted tool at zero licensing cost. It is a credible production choice for OpenShift shops running AMQ Streams, and for teams who want GitOps-friendly YAML configuration and a mature Helm chart. Engineers replacing ad hoc CLI workflows - SSH sessions and kafka shell scripts - will find AKHQ a meaningful step forward. Some larger organisations also run it as a lightweight secondary viewer alongside a commercial platform, using it for quick message browsing while relying on the commercial tool for access control and governance.&lt;/p&gt;
&lt;p&gt;It stops being a sufficient fit when teams grow beyond roughly five engineers sharing a cluster, when compliance requirements mandate data masking or audit logging, when operators need JMX metrics or alerting built into the UI, or when the cluster runs Confluent ksqlDB. Teams handling PII or operating in regulated industries should treat the absence of data masking as a hard constraint rather than a roadmap item. The operational overhead of maintaining a web-based server has also pushed some engineers toward desktop-native Kafka clients that require no Docker daemon to run; tools like KafkIO and Swifka have emerged to serve that preference. [r/apachekafka, rkquik thread, 2025; r/apachekafka, rash5y thread, 2025]&lt;/p&gt;
&lt;h2 id=&quot;akhq-pricing&quot;&gt;AKHQ pricing&lt;/h2&gt;
&lt;p&gt;AKHQ is free and open-source under the Apache 2.0 licence. There is no paid edition, no hosted SaaS option, and no commercial support offering.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open source&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Full feature set; self-hosted only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;No trial period applies. The full project is available at &lt;a href=&quot;https://github.com/tchiotludo/akhq&quot;&gt;https://github.com/tchiotludo/akhq&lt;/a&gt; with no feature restrictions or registration requirements.&lt;/p&gt;
&lt;h2 id=&quot;akhq-competitors-and-alternatives&quot;&gt;AKHQ competitors and alternatives&lt;/h2&gt;
&lt;p&gt;AKHQ competes with a range of self-hosted and commercial &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;Kafka management tools&lt;/a&gt;, from lightweight topic browsers like Kafdrop to full governance platforms like Kpow. The right fit depends on how much operational maturity, compliance capability, and support you need beyond what the open-source tier provides.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small teams needing LDAP/OIDC, Schema Registry, Connect, and MSK IAM at zero cost&lt;/td&gt;
&lt;td&gt;OSS (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Topics, messages, Schema Registry, Connect, consumer groups, ACLs, multi-cluster, MSK IAM&lt;/td&gt;
&lt;td&gt;Docker Compose, Helm, GitOps YAML; JVM memory issues at scale&lt;/td&gt;
&lt;td&gt;LDAP, OIDC, basic, external claims; no audit logging&lt;/td&gt;
&lt;td&gt;Functional; dated; filter state resets on navigation&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafbat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams wanting an actively developed OSS alternative with a more modern UI&lt;/td&gt;
&lt;td&gt;OSS (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Similar to AKHQ; actively maintained AKHQ fork&lt;/td&gt;
&lt;td&gt;Docker, Helm&lt;/td&gt;
&lt;td&gt;LDAP, OIDC&lt;/td&gt;
&lt;td&gt;More modern than AKHQ&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafdrop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight topic browsing with minimal setup&lt;/td&gt;
&lt;td&gt;OSS (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Topic browse, message view, consumer groups&lt;/td&gt;
&lt;td&gt;Docker; minimal dependencies&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Simple, read-focused&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conduktor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-team enterprises needing data masking, field-level encryption, and audit logs&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topics, Schema Registry, Connect, data masking, audit logs, ksqlDB, self-service workflows&lt;/td&gt;
&lt;td&gt;Docker or Kubernetes; requires PostgreSQL; SaaS available&lt;/td&gt;
&lt;td&gt;SSO, RBAC, audit logging; SAML on Enterprise tier&lt;/td&gt;
&lt;td&gt;Polished, React-based&lt;/td&gt;
&lt;td&gt;Community tier free; Team from ~$1,200/seat/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams needing enterprise RBAC, WCAG-compliant UI, and commercially backed support without per-seat pricing&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topics, messages, Schema Registry, Connect, RBAC, MSK/GCP/Azure support&lt;/td&gt;
&lt;td&gt;Stateless; Docker or Kubernetes; straightforward deployment&lt;/td&gt;
&lt;td&gt;Advanced RBAC; SSO&lt;/td&gt;
&lt;td&gt;WCAG-compliant; high performance at scale&lt;/td&gt;
&lt;td&gt;Per-cluster pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lenses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams needing SQL-based data exploration and a developer experience layer across Kafka&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;SQL over topics, data observability, developer portal&lt;/td&gt;
&lt;td&gt;Self-hosted or SaaS&lt;/td&gt;
&lt;td&gt;RBAC, SSO&lt;/td&gt;
&lt;td&gt;Developer-focused; SQL interface prominent&lt;/td&gt;
&lt;td&gt;Contact for pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Redpanda-native teams or those wanting a lightweight browser for any Kafka-compatible cluster&lt;/td&gt;
&lt;td&gt;OSS core / commercial&lt;/td&gt;
&lt;td&gt;Topic browsing, consumer groups, Schema Registry; more limited Connect management&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes&lt;/td&gt;
&lt;td&gt;Basic; enterprise auth on paid tiers&lt;/td&gt;
&lt;td&gt;Clean, modern&lt;/td&gt;
&lt;td&gt;OSS core free; enterprise pricing on request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader comparison across all major Kafka UI tools, see the &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Factor House Kafka UI tools comparison&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-akhq&quot;&gt;Frequently asked questions about AKHQ&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-akhq-cost-and-is-there-a-free-tier&quot;&gt;How much does AKHQ cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;AKHQ is fully free under the Apache 2.0 licence. There is no paid tier, no SaaS option, and no commercial support offering. All features are available in the open-source release with no restrictions or registration required.&lt;/p&gt;
&lt;h3 id=&quot;when-is-akhq-a-better-choice-than-the-alternatives&quot;&gt;When is AKHQ a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;AKHQ is the strongest free option when you need LDAP/OIDC authentication, Schema Registry and Connect management, MSK IAM support, and multi-cluster visibility in one self-hosted tool. It has a more mature auth story than Kafdrop and broader ecosystem coverage than &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda Console’s&lt;/a&gt; open-source tier.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-akhq&quot;&gt;When are the alternatives a better choice than AKHQ?&lt;/h3&gt;
&lt;p&gt;Consider a commercial alternative when you need data masking, audit logging, JMX metrics, alerting, or working ksqlDB integration. AKHQ’s single-maintainer governance model and documented out-of-memory behaviour under high-throughput tailing also make alternatives worth evaluating for large-scale or regulated deployments.&lt;/p&gt;
&lt;h3 id=&quot;is-akhq-actively-maintained&quot;&gt;Is AKHQ actively maintained?&lt;/h3&gt;
&lt;p&gt;Active, but with a single primary maintainer. Ludovic Dehon leads development, with contributions from enterprise users including Michelin and La Redoute. Some issues sit without a maintainer response for months. There is no commercial entity behind the project and no SLA.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>CMAK: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/cmak/</link><guid isPermaLink="true">https://factorhouse.io/articles/cmak/</guid><description>CMAK is a free, open-source Kafka admin tool from Yahoo. This review covers features, KRaft limitations, security gaps, and the best alternatives for 2026.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CMAK (Cluster Manager for Apache Kafka) is a free, Apache 2.0-licensed tool originally built by Yahoo that provides multi-cluster management, partition reassignment, preferred-replica election, and optional JMX polling from a browser-based interface.&lt;/li&gt;
&lt;li&gt;The project is functionally stalled: the last stable release (3.0.0.6) was tagged on 29 April 2022, and multiple pull requests and feature requests have gone unanswered since.&lt;/li&gt;
&lt;li&gt;CMAK has a hard ZooKeeper dependency with no KRaft support. Apache Kafka 4.0, released on 18 March 2025, operates entirely without ZooKeeper, making CMAK incompatible with all clusters running that version.&lt;/li&gt;
&lt;li&gt;Security is limited to LDAP basic auth and coarse feature flags: there is no SAML/OIDC, no per-topic RBAC, and no audit log.&lt;/li&gt;
&lt;li&gt;For teams running KRaft clusters, needing message browsing, or requiring enterprise-grade access control, alternatives such as &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt;, Kafdrop, &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda Console&lt;/a&gt;, or &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; are worth evaluating.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-cmak&quot;&gt;What is CMAK?&lt;/h2&gt;
&lt;p&gt;CMAK (Cluster Manager for Apache Kafka), originally named Kafka Manager, is an open-source Kafka administration tool built at Yahoo and released under the Apache 2.0 licence. It provides a browser-based interface for managing one or more Kafka clusters from a single view.&lt;/p&gt;
&lt;p&gt;The tool is built on Scala and the Play framework, and it requires a direct connection to a ZooKeeper ensemble to function. Its core feature set reflects the operational tasks Yahoo built it for: registering and monitoring clusters, creating and modifying topics, managing partitions, triggering preferred-replica elections, and polling JMX metrics from brokers.&lt;/p&gt;
&lt;p&gt;CMAK is hosted at &lt;a href=&quot;https://github.com/yahoo/CMAK&quot;&gt;github.com/yahoo/CMAK&lt;/a&gt;. No commercial distribution, hosted offering, or paid support tier exists.&lt;/p&gt;
&lt;h2 id=&quot;cmak-review&quot;&gt;CMAK review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;CMAK’s feature set is narrowly focused on cluster operations. Its official README enumerates the following capabilities: &lt;code&gt;KMClusterManagerFeature&lt;/code&gt;, &lt;code&gt;KMTopicManagerFeature&lt;/code&gt;, &lt;code&gt;KMPreferredReplicaElectionFeature&lt;/code&gt;, and &lt;code&gt;KMReassignPartitionsFeature&lt;/code&gt;. In practice, this covers multi-cluster registration, topic creation and configuration, partition reassignment, preferred-replica election, and optional JMX metric polling at the broker and topic level.&lt;/p&gt;
&lt;p&gt;Practitioner reviews describe these features as effective for their intended purpose. A 2022 Towards Data Science overview states: “For the most part, CMAK is primarily an Ops tool. It is also really good at partition reassignment.” The same review notes that “multi-cluster management, dynamic topic configuration, partition creation, and replica change will cover most of your tasks.” User webodmin, writing on GitHub Issue #776 in 2023, described CMAK as “very useful and convenient” — this from someone who had run it in production until a KRaft migration made it unusable.&lt;/p&gt;
&lt;p&gt;What CMAK does not cover is equally significant. The tool has no message-browsing UI, no schema registry integration, no Kafka Connect management, and no ksqlDB or Flink support. These absences reflect a scope deliberately limited to administrative operations rather than data-plane visibility.&lt;/p&gt;
&lt;p&gt;One limitation that affects day-to-day operations is CMAK’s use of an internal caching layer rather than live data from broker APIs. Changes made to the cluster are not reflected in the UI immediately, because the interface polls for metadata on a fixed interval rather than subscribing to live state. This causes delays between an administrative action and its confirmation on-screen, which can mislead operators into believing a partition reassignment or offset reset has not taken effect. Tchiotludo, the author of AKHQ, cited this behaviour explicitly when describing why he built an alternative: “Kafka Manager have some kind of cache and you don’t see your change on real-time. During develop, it was confusing to don’t see your modifications” (r/apachekafka, 2019).&lt;/p&gt;
&lt;p&gt;A related visibility gap appears during broker migrations. An operations specialist on r/devops noted that CMAK would report hours of catch-up replication completing in seconds, with no way to verify accuracy without querying the ZooKeeper shell directly: “CMAK would indicate hours of catch-up replication (by replicas) being completed in seconds whose correctness I could never figure out without going to the ZK shell” (Gathering opinions on Kafka management tools, 2021). For teams doing broker replacements or rebalances in production, this makes CMAK unreliable as a source of truth during the migration window.&lt;/p&gt;
&lt;p&gt;One area where CMAK retains some value is as a learning tool. Its visual taxonomy of brokers, partitions, and replicas maps closely to Kafka’s internal model, and some practitioners recommend it specifically for building familiarity with core concepts. A systems engineer on r/apachekafka wrote: “Spend some time to understand CMAK, it is really good, and the functions on the page can help you check the knowledge points or keywords you missed when reading the document” (Suggestions for learning Kafka, 2025). This applies to ZooKeeper-era clusters; the tool provides no equivalent value on KRaft deployments where it cannot connect at all.&lt;/p&gt;
&lt;p&gt;The most consequential functional gap as of 2025-2026 is the absence of KRaft support. CMAK connects to Kafka through a ZooKeeper endpoint. GitHub Issue #776 has accumulated requests from named users since 2022, including akamensky, webodmin, ceeeekay, meethigher, and azhurbilo, all of whom confirm CMAK is unusable on ZK-less deployments. Maintainer patelh acknowledged the problem in 2022: “Yep, we’ll phase it out but we’ll need some place to store metadata. Either directly in Kafka or RDBMS. Still to be designed.” As of May 2026, no implementation has shipped.&lt;/p&gt;
&lt;p&gt;Practitioners hitting this wall in production confirm that the failure is hard and immediate. User dekanov on r/apachekafka wrote after standing up a test cluster: “I have just started my first Kraft test cluster. First problem is that it is not compatible with my Kafka Manager, which I can not get rid of entirely” (Running Kraft in production, 2023). The broader community has largely absorbed this as settled. Haarolean, a contributor to the Kafbat UI project, stated plainly on r/dataengineering: “CMAK is inactive for a couple years already” (Kafbat UI for Apache Kafka v1.0 is out, 2024).&lt;/p&gt;
&lt;p&gt;Apache Kafka 4.0, released 18 March 2025, is the first major release to operate entirely without Apache ZooKeeper. CMAK is incompatible with any cluster running that version or later.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;CMAK is distributed as source code requiring an sbt/Scala build. Docker images exist but are community-maintained (sheepkiller, TrivadisPF, intropro, hjben) rather than published by Yahoo. Kubernetes deployment is handled by the third-party &lt;code&gt;eshepelyuk/cmak-operator&lt;/code&gt; Helm chart, not the upstream project.&lt;/p&gt;
&lt;p&gt;Build friction is a recurring complaint. GitHub Issue #927 (December 2023, filed by zamek42) reports a compatibility problem on OpenJDK 17: “No JVMCI compiler found, but I use openjdk Java 17.” Issue #922 (July 2023, filed by SimonMikolajek) flags build dependency problems on current sbt/Scala toolchains.&lt;/p&gt;
&lt;p&gt;For larger clusters with JMX enabled, the README requires manual thread-pool tuning: &lt;code&gt;cmak.broker-view-thread-pool-size=&amp;lt;3 * number_of_brokers&amp;gt;&lt;/code&gt; and a corresponding update-interval formula. There is no documented autoscaling path.&lt;/p&gt;
&lt;p&gt;Operational reliability has also been questioned. A widely-cited issue (#550, originally filed in 2018 by mimani) describes long-running instances hanging after 20-30 days with &lt;code&gt;RejectedExecutionException&lt;/code&gt; errors caused by thread-pool exhaustion. This pattern has not been addressed in any subsequent release.&lt;/p&gt;
&lt;p&gt;The last binary release is 3.0.0.6, tagged 29 April 2022 by contributor mcjyang. No stable release has followed in the three years since.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;CMAK supports LDAP basic authentication and a coarse feature-flag model. The feature flags can be enabled or disabled globally but provide no per-user, per-cluster, or per-topic granularity.&lt;/p&gt;
&lt;p&gt;The README warns explicitly: “Warning, you need to have SSL configured with CMAK (pka Kafka Manager) to ensure your credentials aren’t passed unencrypted… Note: LDAP is unencrypted and insecure.” SSL must be configured manually; the default setup transmits credentials in plaintext.&lt;/p&gt;
&lt;p&gt;There is no SAML or OIDC integration. GitHub Issue #933 (September 2024, naganaidu-rezi) asks: “how to configure custom SAML for Kafka Manager?” — a direct signal that SSO is wanted and absent. In practice, teams that require SSO and MFA have had to place CMAK behind a reverse proxy wired to an identity provider. A DevOps engineer on r/devops described their setup: “We use it with OKTA to enable SSO and MFA for Spark UI, Kafka Manager, Kibana and Kubernetes dashboard” (How do you handle apps that do not have built-in SSO support?, 2021). This approach works but adds operational overhead, requires a correctly configured proxy, and still does not provide per-topic or per-cluster access controls — it secures the front door without addressing what happens inside. Issue #926 (October 2023, xiaobao623) reports that enabling ZooKeeper ACLs breaks CMAK’s connection entirely. Issue #932 (August 2024, qianghong000) covers TLS-to-ZK configuration complexity. There is no audit log.&lt;/p&gt;
&lt;p&gt;For teams with compliance requirements, the access-control model is not enterprise-grade. It was designed for small, internally-trusted environments.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;Practitioners describe the CMAK UI as functional and direct for operators already familiar with Kafka’s internal model. The 2022 Towards Data Science overview calls it “a good and fairly straightforward UI” that handles Ops tasks without unnecessary friction for experienced users.&lt;/p&gt;
&lt;p&gt;The consistent criticism is that the UI is dated by comparison to modern single-page-application tools. Redpanda’s 2024 Kafka tools guide (vendor-authored, included here only for the descriptive claim it independently corroborates) describes it as “less intuitive and visually dated compared to newer tools… [lacking] advanced visualizations and interactive features.” The absence of a message-browsing view is the most frequently cited specific gap: almost every practitioner comparison uses Kafdrop’s topic-content screen as the counterexample.&lt;/p&gt;
&lt;p&gt;There is no documented accessibility support.&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;CMAK’s ecosystem coverage is minimal. It connects to Kafka through ZooKeeper; it has no native integration with Schema Registry, Kafka Connect, ksqlDB, Flink, or any cloud-managed Kafka service.&lt;/p&gt;
&lt;p&gt;The ZooKeeper dependency is particularly limiting for cloud deployments. Users on GitHub Issue #776 confirm that managed services — MSK, Confluent Cloud, Aiven, Redpanda Cloud — either lock down or no longer expose ZooKeeper endpoints, making CMAK incompatible with the majority of managed Kafka offerings. GitHub Issue #934 (October 2024, stefannmih) specifically flags compatibility problems with Confluent distributions.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;eshepelyuk/cmak-operator&lt;/code&gt; maintainer, the author of the most widely used Kubernetes deployment path for CMAK, recommends AKHQ as the better-maintained alternative: “AKHQ project seems to be the most active open source tool for managing and monitoring Kafka clusters. It could be missing some functionality from CMAK, but their developers are open for feature requests and contributions.”&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;CMAK has no commercial support offering. The only channel is GitHub Issues, and the maintainer response rate in 2023-2024 is low. Issues #925, #928, #929, #930, #931, and #933 all contain unanswered feature requests or release requests. A PR to add Kafka 3.3 compatibility (PR #924, submitted August 2023 by HenryCaiHaiying) remained unmerged as of the research date.&lt;/p&gt;
&lt;p&gt;There is no documentation site, no community Slack or Discord, and no commercial-support option. The project has not been formally archived by Yahoo, but it has not received a stable release since April 2022.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;CMAK suits small-to-medium teams running on-premises Kafka 2.x or ZooKeeper-era 3.x clusters, where the primary need is partition reassignment, preferred-replica election, and basic broker/topic visibility. It fits operators — SREs and platform engineers — who prefer a direct, Ops-oriented interface and have no compliance requirement for SSO, granular RBAC, or audit logging.&lt;/p&gt;
&lt;p&gt;It is not a fit for any cluster running Kafka 3.3+ in KRaft mode, any Kafka 4.0 deployment, or any managed Kafka service where ZooKeeper is not user-accessible. It is also not appropriate for teams that need message inspection, schema management, Kafka Connect administration, or enterprise access controls.&lt;/p&gt;
&lt;h2 id=&quot;cmak-pricing&quot;&gt;CMAK pricing&lt;/h2&gt;
&lt;p&gt;CMAK is free and open-source software, released under the Apache 2.0 licence. There are no paid tiers, hosted offerings, or commercial distributions.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;There is a single tier: free, self-hosted. You are responsible for all infrastructure, deployment, and operational costs.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;No trial is applicable. You can deploy CMAK directly from the &lt;a href=&quot;https://github.com/yahoo/CMAK&quot;&gt;GitHub repository&lt;/a&gt; or from a community-maintained Docker image.&lt;/p&gt;
&lt;h2 id=&quot;cmak-competitors-and-alternatives&quot;&gt;CMAK competitors and alternatives&lt;/h2&gt;
&lt;p&gt;CMAK occupies a specific niche: free, ZooKeeper-era Kafka operations tooling. The wider ecosystem’s move toward KRaft and richer feature sets has eroded that niche substantially. Most practitioners evaluating a new deployment now consider AKHQ, Kafdrop, Redpanda Console, or a commercial option before settling on CMAK.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment &amp;amp; ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ZK-era Kafka ops, partition reassignment&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CMAK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Multi-cluster, partition reassignment, preferred-replica election, JMX polling&lt;/td&gt;
&lt;td&gt;sbt build; community Docker and Helm only&lt;/td&gt;
&lt;td&gt;LDAP; global feature flags&lt;/td&gt;
&lt;td&gt;Functional, dated; cached state updates&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-featured OSS with schema registry and Connect&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Message browsing, live tailing, schema registry, Kafka Connect, ACL management, multi-cluster&lt;/td&gt;
&lt;td&gt;Docker, K8s Helm; actively maintained&lt;/td&gt;
&lt;td&gt;OIDC, LDAP, per-topic RBAC&lt;/td&gt;
&lt;td&gt;Modern SPA&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSS multi-cluster management with advanced SSO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kafbat UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Message browsing, schema registry, multi-cluster, Avro/Protobuf/JSON support&lt;/td&gt;
&lt;td&gt;Docker, K8s; actively maintained community fork&lt;/td&gt;
&lt;td&gt;OAuth 2.0, Okta, Active Directory&lt;/td&gt;
&lt;td&gt;Modern SPA&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightweight KRaft-compatible monitoring&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kafdrop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic browsing, consumer group monitoring, message inspection&lt;/td&gt;
&lt;td&gt;Docker; lightweight footprint&lt;/td&gt;
&lt;td&gt;Basic auth&lt;/td&gt;
&lt;td&gt;Simple web UI&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer-focused UI with KRaft support&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS / Commercial&lt;/td&gt;
&lt;td&gt;Message browsing, schema registry, Kafka Connect, multi-cluster&lt;/td&gt;
&lt;td&gt;Docker, K8s&lt;/td&gt;
&lt;td&gt;SSO/RBAC (Enterprise tier only)&lt;/td&gt;
&lt;td&gt;Modern React SPA&lt;/td&gt;
&lt;td&gt;Free (Community); paid (Enterprise)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Kafka tooling with advanced RBAC at scale&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Full Kafka management, monitoring at scale&lt;/td&gt;
&lt;td&gt;Stateless; straightforward deployment options&lt;/td&gt;
&lt;td&gt;Advanced RBAC&lt;/td&gt;
&lt;td&gt;Fully WCAG-compliant&lt;/td&gt;
&lt;td&gt;Per-cluster pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Platform users&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Full Kafka management, ksqlDB, schema registry, monitoring&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;td&gt;RBAC&lt;/td&gt;
&lt;td&gt;Feature-rich&lt;/td&gt;
&lt;td&gt;Paid (bundled)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;For a broader comparison of Kafka management tools, see the&lt;/em&gt; &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;&lt;em&gt;Factor House Kafka tools guide&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-practitioners-are-moving-to&quot;&gt;What practitioners are moving to&lt;/h3&gt;
&lt;p&gt;Across r/apachekafka and r/devops, the migration path away from CMAK is well-trodden. AKHQ is the most frequently recommended direct replacement for teams that need live topic inspection alongside cluster administration — user thebrobotic on the CMAK replacement thread (2022) noted: “Started using this and I like it better than CMAK. Has more features like view data in a topic / live tail of a topic which is very nice.” Kafbat UI (a maintained community fork of the original Provectus UI) has gained ground for teams running multi-cluster environments that also need OAuth 2.0 or Active Directory integration without a reverse-proxy workaround. For teams with compliance requirements or operating Kafka at significant scale, the gap between CMAK’s feature set and commercial alternatives such as &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; are substantial: per-topic RBAC, audit logging, and stateless deployment are not achievable in any configuration of CMAK.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-cmak&quot;&gt;Frequently asked questions about CMAK&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-cmak-cost-and-is-there-a-free-tier&quot;&gt;How much does CMAK cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;CMAK is free and open-source under the Apache 2.0 licence. There is no paid tier, hosted offering, or commercial support option. You self-host and bear all infrastructure costs.&lt;/p&gt;
&lt;h3 id=&quot;when-is-cmak-a-better-choice-than-the-alternatives&quot;&gt;When is CMAK a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;CMAK suits operators managing on-premises Kafka 2.x or ZooKeeper-era 3.x clusters who need partition reassignment, preferred-replica election, and multi-cluster visibility, without budget for commercial tooling and without SSO or RBAC requirements.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-cmak&quot;&gt;When are the alternatives a better choice than CMAK?&lt;/h3&gt;
&lt;p&gt;If your cluster runs KRaft (Kafka 3.3+ or 4.0), uses a managed service, or requires message browsing, schema registry, SSO, per-topic RBAC, or an audit log, CMAK cannot meet those needs. AKHQ, Kafdrop, or a commercial option are better fits.&lt;/p&gt;
&lt;h3 id=&quot;is-cmak-still-actively-maintained&quot;&gt;Is CMAK still actively maintained?&lt;/h3&gt;
&lt;p&gt;The last stable release is 3.0.0.6, dated 29 April 2022. Multiple PRs and feature requests have received no maintainer response since then. The project has not been formally archived, but it is not receiving new releases.&lt;/p&gt;
&lt;h3 id=&quot;does-cmak-support-kafka-40&quot;&gt;Does CMAK support Kafka 4.0?&lt;/h3&gt;
&lt;p&gt;No. Kafka 4.0 (released 18 March 2025) operates entirely without ZooKeeper. CMAK requires a ZooKeeper endpoint and has no KRaft support, so it is incompatible with Kafka 4.0 clusters.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Confluent Control Center: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/confluent-control-center/</link><guid isPermaLink="true">https://factorhouse.io/articles/confluent-control-center/</guid><description>An honest technical review of Confluent Control Center in 2026, covering features, deployment, pricing, and the best alternatives for Kafka teams.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Confluent Control Center is built exclusively for Confluent Platform. It cannot monitor Amazon MSK, Redpanda, or Aiven clusters, and requires the proprietary Confluent Metrics Reporter JAR installed on each broker.&lt;/li&gt;
&lt;li&gt;The next-generation Control Center, released with Confluent Platform 8.0 in May 2025, reduced startup time from up to 50 minutes to approximately one minute, but upgrading from the legacy version is a full migration that discards all historical metrics.&lt;/li&gt;
&lt;li&gt;Pricing is the most frequently cited complaint across practitioner reviews; Control Center, multi-tenancy support, and encryption each carry costs beyond the base Confluent Platform licence.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/articles/what-the-ibm-confluent-acquisition-means-for-kafka-users&quot;&gt;IBM’s acquisition of Confluent&lt;/a&gt;, completed in March 2026, is actively prompting installed-base customers to evaluate alternatives, citing concerns about future pricing pressure and roadmap direction.&lt;/li&gt;
&lt;li&gt;At large cluster scale, the legacy interceptor-based telemetry architecture creates measurable overhead: Control Center adds approximately 50 internal topics to broker metadata and can become unresponsive when the number of consumer groups is high. Many enterprise teams bypass it for production alerting, using Prometheus with Grafana instead.&lt;/li&gt;
&lt;li&gt;For teams that need Kafka tooling across distributions, or that cannot accept Confluent’s pricing and ecosystem constraints, alternatives such as &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; offer broader distribution support with more predictable per-cluster pricing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-confluent-control-center&quot;&gt;What is Confluent Control Center?&lt;/h2&gt;
&lt;p&gt;Confluent Control Center is a web-based management and monitoring interface bundled with Confluent Platform, Confluent’s commercial Kafka distribution. It provides a single dashboard for monitoring brokers, topics, consumer groups, Kafka Connect workers, Schema Registry, ksqlDB, and Kafka Streams topologies across Confluent-managed clusters.&lt;/p&gt;
&lt;p&gt;Two generations of Control Center are currently in active deployment. The legacy version, shipped with Confluent Platform 7.x and earlier, has been widely deployed since Confluent’s earliest commercial releases. The next-generation Control Center, released as generally available with Confluent Platform 8.0 in May 2025, replaces the internal Kafka Streams-based metrics pipeline with a Prometheus-based architecture and delivers substantially faster startup and improved partition scale limits.&lt;/p&gt;
&lt;p&gt;Control Center is closed-source and requires an enterprise Confluent Platform licence. It is not available as a standalone product.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722a00e8a935845fad116_confluent-control-center-blog-screenshot.avif&quot; alt=&quot;Confluent Control Center&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;confluent-control-center-review&quot;&gt;Confluent Control Center review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Control Center offers comprehensive monitoring within the Confluent ecosystem. Practitioners describe it as providing “a panoramic view into all the elements in Confluent Platform: brokers, consumers, Connect workers, ksqlDB objects.” [Ben, Confluent Community Forum, February 2021] The monitoring module has been described as “impressive” by at least one named enterprise reviewer. [Ahmed Emad, Territory Sales Leader at Sumerge, PeerSpot, January 2024]&lt;/p&gt;
&lt;p&gt;The specific functional differentiators that justify Control Center over open-source alternatives are Kafka Streams topology visualisation and native ksqlDB development integration. No open-source Kafka UI provides a comparable native view of these Confluent-specific components.&lt;/p&gt;
&lt;p&gt;Consumer lag monitoring has documented boundaries. Consumers using the &lt;code&gt;assign()&lt;/code&gt; method rather than &lt;code&gt;subscribe()&lt;/code&gt; are not tracked, and Metrics API lag values do not update during a rebalance. [Confluent documentation] A UI rendering bug also affects compound, nested Avro keys in the Topics &amp;gt; Messages view; because Control Center has no public issue tracker, there is no way for users to monitor for a fix. [Ben, Confluent Community Forum, February 2021]&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;The legacy Control Center has a well-documented reputation for slow startup and high resource consumption. Confluent’s own engineering blog acknowledged startup times of 15 to 50 minutes after a restart, and described customer ABANCA’s experience of service restarts as “tedious.” [Philip Wang and Surabhi Singh, Confluent Engineering Blog, May 2025] The next-generation release reduces startup to approximately one minute, increases partition scale support from 120,000 to 400,000, narrows metrics freshness to two to three minutes, and eliminates the requirement for a separate Kafka cluster to store metrics. [Philip Wang and Surabhi Singh, Confluent Engineering Blog, May 2025]&lt;/p&gt;
&lt;p&gt;The upgrade path from legacy to next-gen is a full migration, not a standard upgrade. Historical metrics do not carry over, and Confluent recommends running both versions in parallel for 7 to 15 days. Starting with CP 8.0, Confluent Monitoring Interceptors are also removed; teams that embedded interceptors for producer and consumer tracing will need to reconfigure for the Prometheus-based architecture. [Confluent documentation; Confluent release notes]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interceptor overhead at scale.&lt;/strong&gt; In the legacy architecture, Control Center relies on client-side interceptors to collect producer and consumer telemetry. A practitioner on r/apachekafka warned that deploying Control Center with interceptors “will add like 50 topics to your kafka brokers.” [xkillac4, r/apachekafka] At high consumer group counts, this metadata burden can become structurally unworkable. One platform engineer on the same thread described the scale failure directly: “Interceptors destroy CCC with the number of consumers/consumer groups we have.” [lord_pirax, r/apachekafka] The same engineer noted that restarts without wiping state or changing the application ID were unreliable on their cluster. These are cluster-size-dependent conditions; the same engineer explicitly noted that “for people without crazy clusters: CCC will probably work fine.”&lt;/p&gt;
&lt;p&gt;A documented practitioner response to this overhead is to decouple monitoring from Control Center entirely. On the same thread, an engineer stated: “I use Prometheus with the Confluent provided Grafana dashboards for all production deployments even when the Confluent Control Center is present.” [aerialbyte, r/apachekafka] This pattern of treating Control Center as an ad-hoc debugging tool while routing production alerting through external Prometheus pipelines is reported across multiple community discussions.&lt;/p&gt;
&lt;p&gt;Several operational issues in the legacy version remain unresolved. A user running version 7.5.0 reported continuous RAM growth until all available memory was exhausted, with no vendor response. [NhatDuy11, Confluent Community Forum, November 2024] Control Center 7.6.0 on Confluent for Kubernetes was separately reported to stop after a few hours with &lt;code&gt;RackId doesn&apos;t exist for process&lt;/code&gt; errors; the GitHub issue remained open with no assigned owner. [Mohamed Aziz Tousli, GitHub issue #305, confluentinc/confluent-kubernetes-examples, June 2024] A recurring pattern involves Kafka Streams entering a rebalancing loop on startup, leaving the UI on a “Loading data” spinner for 20 to 30 minutes. Confluent’s own mitigation recommends dedicated hardware at the level of an m4.2xl instance with 6 GB JVM heap and 8 stream threads, meaning Control Center can require compute comparable to the brokers themselves. [Elizabeth Bennett (Stitch Fix) and Xavier Léauté (Confluent), Google Groups, April 2017 — older source, flagged; deployment-sizing guidance confirmed in current Confluent documentation]&lt;/p&gt;
&lt;p&gt;Horizontal Pod Autoscaling is not supported for Control Center pods in Kubernetes, and the &lt;code&gt;cp-helm-charts&lt;/code&gt; repository was archived in February 2024, narrowing the supported Kubernetes deployment path to Confluent for Kubernetes (CFK). [Confluent documentation; GitHub, confluentinc/cp-helm-charts]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitOps and configuration drift.&lt;/strong&gt; A related operational concern for teams managing Control Center via CFK or Helm is configuration drift. Any cluster changes made through the Control Center UI — connector configurations, topic settings — are applied directly to the cluster state rather than through the declarative Git-managed manifest. One DevOps engineer noted the specific friction: “Only problem I have is not being able to easy persist my connector configs or being able to mount them as configmaps.” [r/devops] Teams running strict GitOps pipelines need to treat Control Center as a read-only observability surface in production, or accept that UI-driven changes will diverge from the declared state.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;RBAC in Control Center covers the expected enterprise use cases, with audit logging available for authentication and authorisation events across Confluent-managed clusters. [Confluent documentation]&lt;/p&gt;
&lt;p&gt;The most consequential security limitation for many organisations is that SAML SSO is not supported for self-managed, on-premises Control Center deployments. OIDC is the only supported SSO protocol for Confluent Platform; SAML is available only on Confluent Cloud. [Confluent documentation] Teams with a SAML-only identity provider have no supported path for SSO on self-managed deployments.&lt;/p&gt;
&lt;p&gt;Initial RBAC setup requires the &lt;code&gt;SystemAdmin&lt;/code&gt; role for the first role bindings, and in RBAC-enabled environments you cannot send only metrics to Control Center: full management must be enabled, which is an all-or-nothing constraint. [Confluent documentation] A scale limit of 10,000 rules per cluster adds friction for fine-grained access management at larger deployments. [Anonymous tech manager at a 1,001-5,000 employee tech services company, PeerSpot, August 2024] Principals who are Kafka &lt;code&gt;super.user&lt;/code&gt; but have no RBAC role assignment cannot view or assign roles through the UI; the CLI must be used as a workaround. [Confluent documentation]&lt;/p&gt;
&lt;p&gt;ACL configuration is a recurring pain point in community discussions. One engineer running Control Center in production described the experience as: “ACLs wise, was a pain, as for most of the confluent components, where ACLs are not really well documented.” [r/devops, Gathering opinions on Kafka management tools] This documentation gap is particularly notable given that RBAC is one of the headline enterprise features Confluent uses to justify the licence cost.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;Practitioners who use Control Center within the full Confluent Platform describe it as feature-rich and appropriate for day-to-day operations. One named reviewer called it “more than just an ordinary topic checking tool,” while a named solutions architect offered a less favourable view: “From the control center perspective, there is a lot of room for improvement in the visualization.” [German Osin, Towards Data Science, September 2021; Ravi Bhati, Solutions Architect at a 10,000+ employee tech services company, PeerSpot, October 2021]&lt;/p&gt;
&lt;p&gt;The production versus ad-hoc distinction is a consistent thread in community feedback. One platform engineer summarised the trade-off concisely: Control Center is “great for debugging” but “sadly not really reliable/stable/performant” as a continuous production observability tool. [lord_pirax, r/apachekafka] This matches the pattern of enterprise teams who keep Control Center available for incident investigation while routing routine alerting to Prometheus. A separate report from a practitioner running a local or dockerised instance described the experience as buggy, with errors not surfacing correctly, plugin integration issues, and no historical data available. [Accomplished-Map-984, r/apachekafka]&lt;/p&gt;
&lt;p&gt;The next-generation UI has received positive feedback. A known WebKit issue in Safari causes authentication failures when browsing topic messages; the documented workaround is Chrome or Firefox. [Confluent documentation] In the legacy version, the “Initialising” spinner can persist for 20 to 30 minutes after a restart, which is most disruptive during incidents. [Google Groups, April 2017; pattern confirmed in current Confluent troubleshooting documentation]&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Control Center integrates natively with Kafka Connect, Schema Registry, ksqlDB, and Kafka Streams. Schema Registry integration in particular receives positive mention: one software architect credited it with “significantly enhancing our organisation’s data quality assurance.” [Gustavo Barbosa dos Santos, Software Architect at C&amp;amp;A Brasil, PeerSpot, January 2024]&lt;/p&gt;
&lt;p&gt;The hard boundary is distribution compatibility. Control Center requires the Confluent Metrics Reporter JAR installed in broker classpaths to function fully. This prerequisite cannot be satisfied on Amazon MSK, Redpanda, or Aiven, and MSK’s native IAM authentication is not supported. The result is that Control Center is not viable for any team outside a pure Confluent Platform deployment. [Factor House, March 2026; Hayato Shimizu, AxonOps Blog, December 2025]&lt;/p&gt;
&lt;p&gt;IBM’s acquisition of Confluent, completed in March 2026, has introduced meaningful uncertainty about pricing and roadmap direction. One VP Engineering at an unnamed fintech company stated: “We started evaluating alternatives the day the acquisition was announced. Not to leave Confluent, but to understand our options.” [Stéphane Derosiaux, Medium, December 2025]&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Enterprise tier users generally describe Confluent support as responsive. Below that tier, most issues are handled through documentation and community forums. “We continuously face issues, such as Kafka being down and slow responses from the support team,” noted one SDE II at Nutanix. [Mayank Aggarwal, PeerSpot, February 2025]&lt;/p&gt;
&lt;p&gt;A recurring pattern across practitioner reviews is that the complexity of getting Control Center to work correctly requires deep engagement with Confluent professional services, and results have not always matched the investment: one reviewer described “many problems and limitations during implementation despite professional services.” [IT Security and Risk Management Associate at an IT services company, Gartner Peer Insights, January 2025 — anonymous, flagged; included for pattern]&lt;/p&gt;
&lt;p&gt;Because Control Center is closed-source, there is no public issue tracker. A GitHub issue raised in September 2024 proposing that the README be updated to state clearly that an enterprise licence is required remained open with no vendor response, pointing to a recurring source of user confusion. [Mark Sallee, GitHub issue #105, confluentinc/control-center-images, September 2024] The Platinum support tier is not available; the maximum is Enterprise, with quarterly patch updates for the current version only. [Confluent documentation]&lt;/p&gt;
&lt;p&gt;Pricing is the most consistently cited complaint across practitioner platforms. Multiple named reviewers describe multi-year contracts as “very costly” and rate pricing at 5 to 6 out of 10. “High fees while not offering features that match those of other tools” and “hundreds of configurations that application teams must understand” are both flagged as operational burdens. [PavanManepalli, Wells Fargo, PeerSpot, September 2025; RameshJogula, PeerSpot, April 2024; Praveeen Manvi, PeerSpot, March 2024; Clara Riva, PeerSpot, November 2023] Community feedback reinforces this: one engineer, asked why their team uses Control Center, replied simply: “we pay for it so might as well use it.” [r/devops, Gathering opinions on Kafka management tools] For teams running open-source Kafka rather than full Confluent Platform, the licensing cost of Control Center is rarely justified by features that modern open-source alternatives do not cover. [r/apachekafka community consensus]&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Control Center is a well-matched tool for organisations running Confluent Platform on-premises or on their own infrastructure, with the full stack in place: Kafka Streams, ksqlDB, Kafka Connect, and Schema Registry. If Kafka Streams topology visualisation or native ksqlDB tooling integrated with your monitoring UI is a hard requirement, there is currently no open-source alternative that provides an equivalent view.&lt;/p&gt;
&lt;p&gt;It suits large enterprise deployments where the Confluent ecosystem is already established and where in-house Kafka operations are not the preferred model. [Mayank Aggarwal, Nutanix, PeerSpot, February 2025] Community experience also suggests it is effective as an ad-hoc debugging and incident investigation tool, even for teams who have moved routine alerting elsewhere.&lt;/p&gt;
&lt;p&gt;It is a weaker fit for teams on MSK, Redpanda, or Aiven (where it simply cannot function), for organisations that require SAML SSO for self-managed tooling, for cost-sensitive or smaller environments, and for teams running mixed-distribution environments or actively managing vendor lock-in risk in the wake of the &lt;a href=&quot;/articles/what-the-ibm-confluent-acquisition-means-for-kafka-users&quot;&gt;IBM acquisition&lt;/a&gt;. Teams operating at high consumer group counts should evaluate the interceptor overhead against their broker capacity before committing to the legacy architecture.&lt;/p&gt;
&lt;h2 id=&quot;confluent-control-center-pricing&quot;&gt;Confluent Control Center pricing&lt;/h2&gt;
&lt;p&gt;Confluent Control Center is not priced or sold separately. It is bundled with Confluent Platform under an enterprise licence. Pricing is not published and requires direct engagement with Confluent’s sales team.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;Confluent Platform is sold under an enterprise licence. Features including Control Center, multi-tenancy support, and encryption carry costs beyond the base tier, meaning the all-in cost of a production Control Center deployment is higher than the licence headline suggests. Multiple named practitioners rate pricing at 5 to 6 out of 10 and describe multi-year contracts as “very costly.” [RameshJogula, PeerSpot, April 2024; Clara Riva, PeerSpot, November 2023; Praveeen Manvi, PeerSpot, March 2024] Additional charges for scaling have also been noted: “They charge a lot for scaling, which makes it expensive.” [Mayank Aggarwal, Nutanix, PeerSpot, February 2025]&lt;/p&gt;
&lt;p&gt;The Enterprise licence includes quarterly patch updates for the current version only. The Platinum support tier is not available for Control Center. [Confluent documentation]&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;A time-limited evaluation of Confluent Platform may be available through Confluent’s website.&lt;/p&gt;
&lt;h2 id=&quot;confluent-control-center-competitors-and-alternatives&quot;&gt;Confluent Control Center competitors and alternatives&lt;/h2&gt;
&lt;p&gt;For teams evaluating Kafka management tooling in 2026, the competitive landscape ranges from lightweight open-source options to commercial platforms designed for multi-distribution or multi-technology environments. The most important filtering decision is which Kafka distribution you run: if you are outside Confluent Platform, Control Center is not in the running.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full Confluent Platform users on-premises&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial (bundled)&lt;/td&gt;
&lt;td&gt;Kafka Streams topology, ksqlDB, Schema Registry, Kafka Connect, broker and consumer monitoring&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform; Kubernetes via CFK; HPA not supported&lt;/td&gt;
&lt;td&gt;Confluent RBAC; OIDC only (no SAML on self-managed)&lt;/td&gt;
&lt;td&gt;Web UI; legacy version slow to load; next-gen improved significantly&lt;/td&gt;
&lt;td&gt;Included with Confluent Platform enterprise licence; extra costs for Control Center, multi-tenancy, encryption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Kafka teams on any distribution&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic, consumer group, schema, and connector management; advanced RBAC; audit log; WCAG-compliant UI&lt;/td&gt;
&lt;td&gt;Stateless; no external database dependency; Docker, Helm, JAR; straightforward ops&lt;/td&gt;
&lt;td&gt;Advanced RBAC; SAML, OIDC, LDAP&lt;/td&gt;
&lt;td&gt;Fully WCAG-compliant; high performance at scale&lt;/td&gt;
&lt;td&gt;Per-cluster pricing that scales without penalising team growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightweight developer UI for small teams&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AKHQ / Kafbat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic, consumer group, schema management; basic ACL management&lt;/td&gt;
&lt;td&gt;Self-hosted; low resource overhead; no external dependencies&lt;/td&gt;
&lt;td&gt;ACL-based&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka operations visibility across distributions&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AxonOps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial (with OSS tier)&lt;/td&gt;
&lt;td&gt;Monitoring, alerting, backup and restore, topic management&lt;/td&gt;
&lt;td&gt;Self-hosted; Cassandra backend&lt;/td&gt;
&lt;td&gt;Role-based&lt;/td&gt;
&lt;td&gt;Ops-focused web UI&lt;/td&gt;
&lt;td&gt;Free tier; commercial tiers available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data exploration and SQL on streams&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lenses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Data exploration, SQL on streams, topology view, connector management&lt;/td&gt;
&lt;td&gt;Self-hosted and SaaS&lt;/td&gt;
&lt;td&gt;RBAC&lt;/td&gt;
&lt;td&gt;Web UI&lt;/td&gt;
&lt;td&gt;Per-licence (contact for pricing)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader comparison of Kafka management tools in 2026, see &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Top Kafka UI tools in 2026: a practical comparison for engineering teams&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-confluent-control-center&quot;&gt;Frequently asked questions about Confluent Control Center&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-confluent-control-center-cost-and-is-there-a-free-tier&quot;&gt;How much does Confluent Control Center cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;Control Center is bundled with Confluent Platform under an enterprise licence. Pricing is not published. Named practitioners rate it 5-6 out of 10, describing multi-year contracts as very costly. Additional charges apply for Control Center, multi-tenancy, and encryption beyond the base licence.&lt;/p&gt;
&lt;h3 id=&quot;when-is-confluent-control-center-a-better-choice-than-the-alternatives&quot;&gt;When is Confluent Control Center a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;If you run Confluent Platform on-premises with Kafka Streams and ksqlDB, Control Center provides native topology visualisation and ksqlDB tooling that no open-source alternative matches. The next-gen release (CP 8.0, May 2025) also resolves longstanding startup time and partition scaling complaints. It is also well-regarded as an ad-hoc debugging and investigation console even by teams who use external tools for production alerting.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-confluent-control-center&quot;&gt;When are the alternatives a better choice than Confluent Control Center?&lt;/h3&gt;
&lt;p&gt;If you run Amazon MSK, Redpanda, or Aiven, Control Center is not viable. Teams that need SAML SSO for self-managed deployments, have cost constraints, operate at high consumer group counts where interceptor overhead becomes a concern, or are managing mixed-distribution environments will find broader support and more predictable pricing elsewhere.&lt;/p&gt;
&lt;h3 id=&quot;does-confluent-control-center-work-with-amazon-msk&quot;&gt;Does Confluent Control Center work with Amazon MSK?&lt;/h3&gt;
&lt;p&gt;No. MSK’s native IAM authentication is not supported, and the Confluent Metrics Reporter JAR (required for full Control Center functionality) cannot be installed on MSK broker classpaths. Control Center is not a viable option for MSK deployments.&lt;/p&gt;
&lt;h3 id=&quot;what-changed-in-the-next-generation-control-center-released-with-cp-80&quot;&gt;What changed in the next-generation Control Center released with CP 8.0?&lt;/h3&gt;
&lt;p&gt;CP 8.0 (May 2025) cut startup from 15-50 minutes to ~1 minute, raised partition support from 120,000 to 400,000, and improved metrics freshness to 2-3 minutes. The new release also removes the legacy interceptor-based telemetry architecture in favour of Prometheus, which eliminates the interceptor overhead that affected large-scale deployments. Upgrading from the legacy version is a full migration; historical metrics do not carry over.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Kadeck: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/kadeck/</link><guid isPermaLink="true">https://factorhouse.io/articles/kadeck/</guid><description>Kadeck review for 2026: features, deployment, pricing, and how it compares to AKHQ, Kafbat, Conduktor, and Kpow for Kafka management teams.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kadeck is a commercial Kafka management tool from xeotek, available as a native desktop application and a Docker-based web/teams edition supporting Apache Kafka, Redpanda, and Amazon Kinesis.&lt;/li&gt;
&lt;li&gt;Reddit practitioners consistently recommend it for local development, topic inspection, and debugging — especially on managed clusters like Amazon MSK.&lt;/li&gt;
&lt;li&gt;The JavaScript QuickProcessor enables lightweight payload transformation and Dead Letter Queue recovery workflows without writing a full streaming application.&lt;/li&gt;
&lt;li&gt;The free tier caps message display at 100 records and limits cluster connections to one; teams managing separate Dev, Test, Stage, and Prod clusters will hit that ceiling quickly.&lt;/li&gt;
&lt;li&gt;The web edition runs exclusively in Docker with an online license check on every container start; teams in air-gapped environments need a separate offline activation process before deployment.&lt;/li&gt;
&lt;li&gt;For teams evaluating commercially supported alternatives with advanced RBAC and enterprise deployment options, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;Kpow&lt;/a&gt; from Factor House is worth examining alongside Kadeck.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kadeck&quot;&gt;What is Kadeck?&lt;/h2&gt;
&lt;p&gt;Kadeck is a Kafka management and data exploration tool built by xeotek, a German software vendor. It comes in two editions: a desktop application for Linux, macOS, and Windows, and a web/teams edition distributed as a Docker image. Both connect to Apache Kafka clusters and provide a graphical interface for browsing topics, inspecting message payloads, managing consumer groups, and working with ACLs and Schema Registry configurations. Redpanda and Amazon Kinesis are supported alongside Apache Kafka.&lt;/p&gt;
&lt;p&gt;The product is commercially licensed and is not open source. [Emil Koutanov, DZone, March 2020] Registration on the xeotek website is required before using the web edition in any substantive capacity.&lt;/p&gt;
&lt;p&gt;Kadeck was originally started in 2019 as a solo project by its founder, Benjamin Buick. Buick has noted in Reddit discussions that “hardly any of my original code is left and the team has grown considerably” since then. [Benjamin Buick, r/apachekafka] The tool has maintained an active presence in the Apache Kafka subreddit, where Buick has directly engaged with practitioners to promote and clarify the product’s capabilities.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6a1413a51d9ac9bc9dc7bf02_kadeck-kafka.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kadeck-review&quot;&gt;Kadeck review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Kadeck’s core strength is data exploration. It decodes Avro payloads via Confluent Schema Registry and presents structured records in a columnar layout, which is a practical improvement over reading raw bytes at the command line. A JavaScript-based Quick Processor lets users derive calculated fields from record values without writing a full streaming application. [Markus Günther, personal blog, mguenther.net, January 2021]&lt;/p&gt;
&lt;p&gt;Reddit practitioners echo this assessment. In a thread on Kafka troubleshooting, user usualdev described their workflow as: “To check messages, topics and config I use Kadeck. There is another tool called Kafka Tool to check topics and messages as well. Both of these are cross platform.” [usualdev, r/apachekafka] In a thread about Kafka tools for AWS managed Kafka, user lite-beer-1620 noted: “The Kadeck GUI management &amp;amp; monitoring tools for Kafka and MKS are also really good (and free).” [lite-beer-1620, r/aws]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dead Letter Queue reprocessing.&lt;/strong&gt; One well-documented use case is recovering malformed payloads from a Dead Letter Queue (DLQ) topic. Buick described the workflow in detail on Reddit: consumer applications write unprocessable records to a designated DLQ topic with an error attribute attached; developers then use Kadeck’s Data Browser to isolate the failed records; the JavaScript QuickProcessor applies inline payload transformations; corrected records are previewed and re-ingested into the original source topic; and Kadeck’s “Delete up to here” command purges the processed DLQ entries to prevent duplicate processing. [Benjamin Buick, r/apachekafka] This is a practical alternative to writing a one-off consumer-producer script for each recovery incident, though it requires a paid plan to inspect more than 100 records per query.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema documentation.&lt;/strong&gt; Kadeck includes an external metadata layer that lets teams document schema fields without modifying the Schema Registry or bumping a schema version. Buick described the capability in a thread on updating field documentation: “We have just recently added topic documentation capabilities to Kadeck.” [Benjamin Buick, r/apachekafka] This is useful for multi-team environments where consistent topic naming and field-level context need to be maintained outside the contract enforcement layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-assisted tuning.&lt;/strong&gt; Kadeck 5.x introduced an AI Health Assistant for monitoring and cluster optimization. Independent practitioner assessment of this feature is limited: the most substantial commentary is a single Reddit reply describing it as “a game-changer for Kafka monitoring.” [Mariobmf, r/apachekafka] No independent technical review of the feature has been located.&lt;/p&gt;
&lt;p&gt;Several functional limits are documented by independent practitioners. The free tier displays only 100 records per query. [Anonymous G2 reviewer, g2.com, estimated 2022-2024] The export function is inconsistent between formats: CSV exports respect the column filters applied in the record table, but JSON exports do not, which means the two formats carry different data from the same view. [Markus Günther, ibid.] There is no mechanism to stream derived or transformed data back into a Kafka topic — Kadeck is read-oriented. Kafka Streams and ksqlDB are not integrated, a limitation Günther described as worth noting for teams with processing-oriented workflows. [Markus Günther, ibid.]&lt;/p&gt;
&lt;p&gt;There is also no built-in deduplication filter to restrict the view to the newest record per key. [Markus Günther, ibid.]&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;The web and teams edition ships exclusively as a Docker image. There are no RPM packages, native binaries, or Helm charts for server deployment. [Guy Shilo, idata.co.il, September 2020] For production Kubernetes deployments, xeotek’s own documentation recommends using a persistent external database.&lt;/p&gt;
&lt;p&gt;Every container start triggers an online license validation call. For environments without outbound internet access, xeotek provides an offline activation path through the admin settings panel. [Guy Shilo, ibid.] Automated or ephemeral deployments — for example, spinning up Kadeck in a CI/CD pipeline — require handling a challenge-response license activation via xeotek’s Public API before the container becomes usable.&lt;/p&gt;
&lt;p&gt;The desktop edition does not require Docker. It installs natively on Debian/Ubuntu, macOS, and Windows and requires a Java runtime. An independent installation guide published in March 2024 did not report significant issues with the process. [kifarunix.com, March 2024] Apple Silicon and ARM support were added for both editions in 2024.&lt;/p&gt;
&lt;p&gt;Using the teams edition requires registering on the xeotek website to receive a team ID and secret key before the application is fully functional. [Guy Shilo, ibid.]&lt;/p&gt;
&lt;h3 id=&quot;scale-and-query-performance&quot;&gt;Scale and query performance&lt;/h3&gt;
&lt;p&gt;Because Apache Kafka does not natively index message payloads, searching for specific records requires sequential partition scans. For topics in the hundreds of gigabytes, this scanning pattern can cause significant performance issues in any desktop-class GUI tool.&lt;/p&gt;
&lt;p&gt;A data engineer on r/dataengineering described the limitation directly: their team had Kadeck set up by the cluster hosting team, but found it unusable for large-scale ad-hoc queries, eventually routing high-volume topic data to a parallelized layer instead. [Efxod, r/dataengineering] This is not a Kadeck-specific problem — sequential partition scanning will degrade any UI-based tool at sufficient scale — but it is worth accounting for when evaluating Kadeck for production debugging scenarios involving high-throughput topics.&lt;/p&gt;
&lt;p&gt;For large-scale analytical queries, the practical path is to bypass GUI visualizers entirely and use parallelized engines such as PySpark, Databricks, or external data lakes rather than expect any desktop-oriented tool to handle the load.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;Kadeck Teams includes role- and group-based access control with LDAP and OpenID Connect integration. The free tier of the teams edition includes password policies and basic audit logs, alongside namespace support for topics, ACLs, and consumer groups. [Capterra listing content, capterra.com]&lt;/p&gt;
&lt;p&gt;One Capterra reviewer described the group management as working well for separating project teams, noting that it prevented users from accessing data outside their scope. [Anonymous Product Owner, Capterra, September 2021] No independent practitioner feedback on SSO reliability, RBAC granularity, or the completeness of audit logs was found in any source reviewed during research.&lt;/p&gt;
&lt;h3 id=&quot;connection-limits-and-certificate-management&quot;&gt;Connection limits and certificate management&lt;/h3&gt;
&lt;p&gt;The free tier of the teams edition is limited to a single active cluster connection. Multi-broker clusters are supported within that single connection, but connecting to multiple clusters — for example, separate Dev, Test, Stage, and Prod environments — requires a paid plan.&lt;/p&gt;
&lt;p&gt;This surfaced directly in a Reddit thread on Kafka visualizers. User Jalebibabyded described looking for an open-source alternative because “we have multiple brokers from which the data needs to be fetched and Kadeck freemium is a limiting there.” Buick clarified in the same thread: “We removed the broker restriction a while ago. Kadeck now supports clusters with multiple brokers even in the free version. However, the number of cluster connections is limited to one cluster (connection).” Jalebibabyded confirmed that was the sticking point: “Yes, that was the problem. Because I was looking at connecting to multiple clusters.” [Jalebibabyded and Benjamin Buick, r/dataengineering]&lt;/p&gt;
&lt;p&gt;Certificate management carries a similar constraint. In a recent thread on Kafka UI tools, user Quirky-Design3856 noted: “Me using Kadeck, though free version only offer one cert at a time, its UX is perfect.” [Quirky-Design3856, r/apachekafka] For teams running multiple TLS-secured clusters with separate certificate authorities, this means either purchasing a commercial license or manually rotating credentials on each connection.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;The UI receives consistent praise from the available review sample. Reviewers describe it as clean, modern, and a meaningful step up from CLI-based Kafka tooling. [David W., Solution Architect (IT Services), Capterra, August 2021; Christine U., SAP Consultant (Insurance), Capterra, August 2021; anonymous G2 reviewer, g2.com] One reviewer noted minor usability problems in earlier versions that the support team addressed quickly. [David W., ibid.] Another described it as making working with data from multiple sources straightforward, and cited the team features as time-saving in day-to-day work. [Christine U., ibid.]&lt;/p&gt;
&lt;p&gt;Reddit commentary reinforces the UX narrative. The phrase “its UX is perfect” from Quirky-Design3856 — even in the context of raising a limitation — reflects the general tone of practitioner commentary on the interface. [Quirky-Design3856, r/apachekafka]&lt;/p&gt;
&lt;p&gt;No independent practitioner assessments of the Kadeck 4.x or 5.x UI redesign were found within the last 36 months. All available feedback dates from 2021 or earlier.&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Kadeck supports Confluent Schema Registry for Avro payload decoding. Protocol Buffer support via Schema Registry was added in release 3.2.3. [Xeotek release notes, support.xeotek.com] Kafka Connect management and ACL configuration are included. Amazon Kinesis is listed as a supported data source alongside Apache Kafka and Redpanda.&lt;/p&gt;
&lt;p&gt;Apache Pulsar is not supported, noted explicitly as a gap by at least one reviewer. [David W., Solution Architect, Capterra, August 2021] No independent practitioner feedback on ksqlDB, Apache Flink, Confluent Cloud, Amazon MSK, Aiven, or AWS Glue Schema Registry was found in any source reviewed.&lt;/p&gt;
&lt;p&gt;Kadeck appears in the Confluent Community’s reference list of third-party GUI tools for Apache Kafka but receives no user discussion in that thread. [Confluent Community forum, forum.confluent.io]&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;All three Capterra reviewers mentioned support in positive terms. One described receiving answers within minutes to questions that extended beyond Kadeck’s direct scope into general Kafka troubleshooting, and encouraged the team to “keep it up.” [Anonymous Product Owner, Capterra, September 2021] Another noted the support team reacted quickly. [David W., Capterra, August 2021] xeotek has documented conducting structured sessions with users to gather product feedback, and states that critical input from those sessions was incorporated into the Kadeck 4.0 redesign. [Xeotek blog, Kadeck.com]&lt;/p&gt;
&lt;p&gt;The confidence limit here is the review sample itself: three reviews, all from 2021, all 5-star. No complaints about support quality appear in any source reviewed, but no commentary on enterprise tier support, SLA differences, or documentation quality was found either.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Kadeck suits small-to-medium development and QA teams that want a polished graphical interface for data exploration, debugging, and lightweight governance without writing custom scripts or living in the CLI. The desktop edition is a reasonable choice for individual developers on macOS, Windows, or Linux who need a native Kafka browser without a Docker dependency. The teams edition suits environments where a Docker deployment model is already established and group-based access control at the namespace level is sufficient.&lt;/p&gt;
&lt;p&gt;Reddit community feedback broadly confirms this positioning: practitioners reach for Kadeck when they want a clean, accessible interface for inspecting topics and debugging consumer behaviour, particularly on managed clusters like Amazon MSK where CLI access is more cumbersome.&lt;/p&gt;
&lt;p&gt;It is a less natural fit for teams requiring an open-source-licensed tool, for air-gapped environments that want a simpler deployment model without online license activation, for any workflow that requires streaming derived data back into Kafka topics, for teams managing multiple clusters on the free tier, or for production debugging scenarios where you need to inspect more than 100 records without a paid plan.&lt;/p&gt;
&lt;h2 id=&quot;kadeck-pricing&quot;&gt;Kadeck pricing&lt;/h2&gt;
&lt;p&gt;The following describes what is confirmed in independent and vendor sources.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;Kadeck Teams has a free tier: up to five users, with message display capped at 100 records per query. The free tier includes password policies, basic audit logs, namespace support, and group-based access control. Beyond five users, or for teams that need to remove the 100-record display cap, commercial paid tiers apply. Paid tier pricing is user-based, starting at $25 USD per user per month for Professional, and $32 USD per user per month on their flagship Enterprise plan. For a team of 100 engineers, the flagship plan would cost $3,200 USD per month, equivalent to $38,400 per year.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;A free tier is available for the teams edition within the five-user and 100-record limits described above. Registration on the xeotek website is required to obtain a team ID and access credentials before using the tool.&lt;/p&gt;
&lt;h2 id=&quot;kadeck-competitors-and-alternatives&quot;&gt;Kadeck competitors and alternatives&lt;/h2&gt;
&lt;p&gt;The Kafka management tooling market includes a mix of open-source projects and commercial products. Open-source options typically carry strong community familiarity but require self-managed deployment and tend to offer limited access control out of the box. Commercial tools trade some flexibility for stronger governance features, vendor support, and defined roadmaps.&lt;/p&gt;
&lt;p&gt;Reddit discussions reflect this trade-off clearly. In a thread on UI tools for AWS-managed Kafka, developer JohnPreston72 noted a preference for commercial tooling once open-source options are outgrown: “I tried and used nearly all the open source ones. Winner for me was AKHQ before trying Conduktor. I now use Conduktor for work and well worth the money.” In the same thread, armanduco_ expressed satisfaction with AKHQ for lighter requirements: “In my company we use AKHQ, it’s simple but enough to retrieve some important information.” [JohnPreston72 and armanduco_, r/apachekafka]&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kadeck&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dev/QA teams wanting a polished UI; individual desktop users&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic browsing, payload decoding (Avro, Protobuf), consumer group management, Kafka Connect, ACLs, Schema Registry, Amazon Kinesis, JavaScript QuickProcessor&lt;/td&gt;
&lt;td&gt;Desktop (native) or Docker for web/teams; online license check on every start&lt;/td&gt;
&lt;td&gt;RBAC, LDAP, OIDC; password policies and audit logs on free tier&lt;/td&gt;
&lt;td&gt;Described as clean and modern; praised for UX by practitioners&lt;/td&gt;
&lt;td&gt;Free: 5 users, 100 records, 1 cluster; paid tiers [UNVERIFIED]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams wanting a self-hosted open-source web UI&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic management, Schema Registry, Kafka Connect, consumer group management, multi-cluster support&lt;/td&gt;
&lt;td&gt;Docker or JVM; no managed hosting&lt;/td&gt;
&lt;td&gt;Basic LDAP/OIDC auth; limited role granularity&lt;/td&gt;
&lt;td&gt;Functional web UI&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafbat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams wanting an actively maintained AKHQ fork&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Similar to AKHQ; more active maintenance cadence post-2023&lt;/td&gt;
&lt;td&gt;Docker or JVM&lt;/td&gt;
&lt;td&gt;Basic auth/OIDC&lt;/td&gt;
&lt;td&gt;Web UI, similar to AKHQ&lt;/td&gt;
&lt;td&gt;Free (Apache 2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enterprise teams needing advanced RBAC, strong performance at scale, and WCAG-compliant UI&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic management, consumer groups, Schema Registry, Kafka Connect, ACLs, RBAC, audit log&lt;/td&gt;
&lt;td&gt;Stateless; Docker or Kubernetes; straightforward ops&lt;/td&gt;
&lt;td&gt;Advanced RBAC; SSO; WCAG-compliant; trusted by HPE&lt;/td&gt;
&lt;td&gt;Fully WCAG-compliant web UI&lt;/td&gt;
&lt;td&gt;Per-cluster pricing; Community Edition free for individuals and organisations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader comparison of Kafka management tools, see [our Kafka management tools guide] [placeholder — link to be added].&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-kadeck&quot;&gt;Frequently asked questions about Kadeck&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-kadeck-cost-and-is-there-a-free-tier&quot;&gt;How much does Kadeck cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;Kadeck Teams has a free tier covering up to 5 users and 100 records displayed per query. It includes password policies, audit logs, and group access controls. Paid tier pricing is user-based, starting at $25 USD per user per month for Professional, and $32 USD per user per month on their flagship Enterprise plan. For a team of 100 engineers, the flagship plan would cost $3,200 USD per month, equivalent to $38,400 per year.&lt;/p&gt;
&lt;h3 id=&quot;when-is-kadeck-a-better-choice-than-the-alternatives&quot;&gt;When is Kadeck a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;Kadeck is worth considering if you want a native desktop client on macOS, Windows, or Linux without a Docker dependency, or if a small team needs a clean visual Kafka browser and basic group-based access control within the five-user free tier limit. Reddit practitioners particularly rate it for local development and debugging on managed Kafka clusters.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-kadeck&quot;&gt;When are the alternatives a better choice than Kadeck?&lt;/h3&gt;
&lt;p&gt;If you need open-source licensing, a deployment model without an online license check, stream processing or write-back capability, Apache Pulsar support, multiple simultaneous cluster connections without a paid plan, or the ability to inspect more than 100 records per query without upgrading, other options are worth evaluating first.&lt;/p&gt;
&lt;h3 id=&quot;is-kadeck-actively-maintained&quot;&gt;Is Kadeck actively maintained?&lt;/h3&gt;
&lt;p&gt;xeotek released Apple Silicon and ARM support for both editions in 2024 and has documented a 5.x release including an AI Health Assistant. Independent practitioner discussion is sparse compared to tools like &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; or &lt;a href=&quot;/articles/kafbat-ui&quot;&gt;Kafbat&lt;/a&gt;, and the most recent located independent reviews date from 2021. Buick remains personally active in the Apache Kafka subreddit, where he has engaged directly with user questions and promoted new features.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Kafbat UI: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/kafbat-ui/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafbat-ui/</guid><description>A practical review of Kafbat, the open-source kafka-ui fork — covering features, deployment, security, pricing, and best alternatives in 2026.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafbat is a free, open-source Kafka UI forked from the abandoned Provectus kafka-ui project in early 2024, maintained by community volunteers with no commercial backing and no SLA.&lt;/li&gt;
&lt;li&gt;The tool covers the core Kafka visibility surface well: multi-cluster management, Avro/Protobuf/JSON deserialization, Schema Registry, Kafka Connect, and CEL-based message filtering.&lt;/li&gt;
&lt;li&gt;Regression bugs across minor version upgrades have repeatedly broken Confluent Cloud connectivity and Schema Registry serde auto-selection; many users on v1.3.0 choose to stay there rather than upgrade.&lt;/li&gt;
&lt;li&gt;RBAC is available but shallow: configuration is error-prone and fails silently, with no team namespacing, no per-role data masking override, and no approval workflows.&lt;/li&gt;
&lt;li&gt;For teams that need enterprise-grade access controls, reliable support, and a commercial SLA, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; from Factor House is worth evaluating alongside Kafbat.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafbat&quot;&gt;What is Kafbat?&lt;/h2&gt;
&lt;p&gt;Kafbat is a web-based UI for Apache Kafka clusters. It is the active continuation of the Provectus &lt;code&gt;kafka-ui&lt;/code&gt; project, which paused development in late 2023 and left a critical remote code execution vulnerability unpatched for approximately six months. In early 2024, core contributors led by Roman Zabaluev (GitHub: Haarolean) and germanosin forked the project to &lt;code&gt;kafbat/kafka-ui&lt;/code&gt; and released v1.0.0 in March 2024.&lt;/p&gt;
&lt;p&gt;The project operates under a GitHub Sponsors model. There is no commercial backer, no paid tier, and no SLA. Development velocity is determined by volunteer contributor interest and the priorities of the core maintainers.&lt;/p&gt;
&lt;p&gt;The fork was well received across developer communities. On r/apachekafka, users expressed relief that the project was continuing and verified that their existing configurations carried over: “Looks like a fork, at least I tried it as is, and it is compatible with my old deployment” (Low-Iron6962, r/apachekafka, March 2024). The maintainer confirmed backward compatibility was a deliberate goal for the initial release.&lt;/p&gt;
&lt;p&gt;At its core, Kafbat supports multi-cluster management, topic browsing, message inspection with Avro/Protobuf/JSON Schema deserialization, consumer group lag monitoring, Schema Registry integration, Kafka Connect management, and ACL administration. Version 1.3.0 (July 2025) added GCP IAM authentication and MCP (Model Context Protocol) support. Version 1.5.0 introduced live consumer lag updates.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c72279c93efb0be1f59126_kafbat-blog-screenshot.avif&quot; alt=&quot;Kafbat&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kafbat-review&quot;&gt;Kafbat review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Kafbat’s core visibility features are solid for a free tool. Topic browsing, message inspection, and Schema Registry deserialization work reliably in stable releases. The move from Groovy-based scripting to CEL (Common Expression Language) filters is consistently praised: it removes the Groovy-based RCE risk while giving users a more expressive and readable filter syntax.&lt;/p&gt;
&lt;p&gt;That said, the functionality surface has recurring rough edges. Consumer lag accuracy is unreliable when transactional producers are in use: end-of-transaction marker messages are counted in the lag calculation, causing persistently elevated readings that do not reflect actual consumer progress (krumft, GitHub Issue #1039, April 2025). Filtering behaviour under high message volumes has been described as “unstable and unpredictable” (Rajan Gaul, Product Hunt, approximately February 2026). CEL filters are not persisted between sessions, requiring users to re-enter them on every login (GitHub Issue #1401, October 2025).&lt;/p&gt;
&lt;p&gt;There is no message replay capability from the UI, and no ability to add brokers, increase partitions, rebalance, or change replica counts from the interface (Zeenia Gupta, Platformatory, September 2024). These are open feature requests rather than bugs, but they matter for teams that need operational tooling, not just observational tooling.&lt;/p&gt;
&lt;p&gt;This scope is broadly understood by community users. One r/apachekafka developer summarised it well: “Kafka UI is great if what you want is a simple CLI replacement… browse topics, consumer groups/lag, tweak configs, peek messages, and mess with Schema Registry without everyone needing 8 terminal tabs. Conduktor / Lenses are more ‘we want guardrails + governance + workflows’ than ‘give me a UI’” (TellersTech, r/apachekafka, approximately 2025). Kafbat is a strong observational console; it is not a change-management platform.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Docker deployment is fast: the project is launchable with a single command and accessible at localhost:8080 (Vorrawut Judasri, Medium, October 2024). A Helm chart is actively maintained for Kubernetes deployments, with quick-start documentation available. Some users bypass Docker entirely by downloading the JAR and running it directly from the terminal, which avoids the 4 GB RAM minimum the default Docker configuration recommends (vernochan, r/apachekafka, approximately 2025).&lt;/p&gt;
&lt;p&gt;The main operational pain point is dynamic cluster configuration in Kubernetes. Users who attempt to add clusters through the UI in a Kubernetes deployment consistently receive 400 Bad Request validation errors. Static YAML configuration works; dynamic configuration does not. Documentation on how to deploy with dynamic config via Helm is described by multiple users as absent or insufficient (RaWqqq8, GitHub Issue #1637, January 2026).&lt;/p&gt;
&lt;p&gt;Other operational issues to be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot 3.4.4 reports unbound properties when cluster config is injected via environment variables, blocking AWS Secrets Manager and similar cloud-native secret patterns (Heniland, GitHub Issue #1045, April 2025).&lt;/li&gt;
&lt;li&gt;AWS IAM authentication via STS temporary tokens requires manual JAAS config injection through environment variables, and credential rotation is not documented for this pattern (r/apachekafka, approximately 2025).&lt;/li&gt;
&lt;li&gt;The Helm chart cannot mix &lt;code&gt;yamlApplicationConfig&lt;/code&gt; with an existing secret, has no &lt;code&gt;extraDeploy&lt;/code&gt; support, and does not expose deployment strategy configuration (Helm chart issues #43, #57, #47).&lt;/li&gt;
&lt;li&gt;The default Docker configuration recommends 4 GB RAM minimum, which can catch teams running smaller instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;Kafbat supports YAML-based RBAC, OAuth2 (Google, GitHub, Azure AD), LDAP/Active Directory, and basic auth. Active Directory support was added in v1.1.0 in response to demand carried over from the Provectus project. Community users cite this as a meaningful differentiator among free tools: “Unlike most free UIs, it comes with Active Directory integration for security which is a big plus for us” (Hopeful-Programmer25, r/apachekafka, approximately 2025). Data masking is available with REMOVE, REPLACE, and MASK policies. Audit logging ships as a built-in feature, writing events to a Kafka topic.&lt;/p&gt;
&lt;p&gt;In practice, RBAC configuration is error-prone. The most common failure mode is a mismatch between the OAuth token attribute used for subject matching and the email or username format in the RBAC YAML, causing silent permission failures where users cannot see any clusters at all. The project’s own FAQ directs users to enable trace logging on &lt;code&gt;io.kafbat.ui.service.rbac.extractor&lt;/code&gt; to diagnose the problem (kafbat RBAC FAQ; GitHub Discussion #290, April 2024).&lt;/p&gt;
&lt;p&gt;The security record warrants scrutiny for compliance-sensitive teams. CVE-2025-49127 (CVSS 10.0) was introduced in kafbat’s own v1.0.0: the application accepted user-provided JMX endpoints without validation, and a 30-second scheduler automatically connected to them, allowing any unauthenticated user to trigger unsafe Java deserialization and execute arbitrary code. It was patched in v1.1.0 (SecureLayer7, July 2025). This follows CVE-2023-52251, an RCE inherited from Provectus that took approximately six months to be patched under the original maintainers.&lt;/p&gt;
&lt;p&gt;Further gaps: there is no per-role data masking override (feature requested but not yet implemented; GitHub Issue #1311, September 2025), no team namespacing, no approval workflows, and no policy enforcement layer. The audit log feature is documented but inaccessible to users under RBAC due to a confirmed access denial bug that also generates continuous error spam in the server console (povigg, GitHub Discussion #587, October 2024).&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;Kafbat’s UI is consistently described as the most modern and visually clean among open-source Kafka UIs, and onboarding is fast for engineers already familiar with Kafka (multiple comparison sources, including Conduktor and Platformatory; Vorrawut Judasri, Medium, October 2024).&lt;/p&gt;
&lt;p&gt;The main UX pain points are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Filters reset on every session, requiring manual re-entry (GitHub Issue #1401).&lt;/li&gt;
&lt;li&gt;No consumer groups or lag visibility in the Topics list; users must navigate away to find lag context (GitHub Issue #1405).&lt;/li&gt;
&lt;li&gt;The Produce Message sidebar does not retain its “Keep Contents” state between uses (GitHub Issue #1535, November 2025).&lt;/li&gt;
&lt;li&gt;With Kafka 4.x in KRaft mode, the UI shows inconsistent partition leaders on every page refresh because it queries brokers rather than the KRaft quorum controller; at least one user reported nearly aborting a production migration based on the false impression of leadership instability (elielfg, GitHub Issue #1513, November 2025).&lt;/li&gt;
&lt;li&gt;There are no time-series graphs for consumer lag or message throughput; this has been an open feature request since early in the project (GitHub Issue #233).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Kafbat covers the standard Confluent ecosystem: Schema Registry with Avro, Protobuf, and JSON Schema deserialization, Kafka Connect with connector and task management, and basic ksqlDB. Custom SerDe plugins are available, including an AWS Glue integration. GCP IAM authentication and MCP support were added in v1.3.0 (Release Discussion #1212, Haarolean, July 2025).&lt;/p&gt;
&lt;p&gt;Cloud-managed Kafka compatibility is mixed. GCP Managed Kafka with SASL authentication works reliably, and Kafbat is the recommended replacement for the legacy Provectus image in that environment: multiple r/apachekafka users confirmed successful connections after switching container images (r/apachekafka, approximately late 2024 to early 2025). Confluent Cloud is a different story: connectivity broke in v1.4.x and v1.5.0 due to a metrics refactor that introduced three unhandled failure modes, leaving the cluster in a permanent INITIALIZING state. v1.3.0 works with the same configuration (miehar, GitHub Issue #1852, May 2026). Schema Registry OAuth2 authentication is not supported, blocking use with services such as Google Managed Schema Registry (GitHub Issue #1575, December 2025). Multiple Schema Registries per cluster are not supported (GitHub Discussion #569, October 2024). Flink integration is not available.&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Kafbat’s primary maintainer, Roman Zabaluev, is actively responsive on GitHub: triage typically happens within days, and there is at least one documented case of an Azure AD RBAC misconfiguration being diagnosed and resolved within 24 hours of the report (GitHub Discussion #290, April 2024). The GitHub Sponsors program offers priority bug handling for sponsors as a formal support incentive.&lt;/p&gt;
&lt;p&gt;There is no commercial support tier, no SLA, and no dedicated support queue. Several issues remain open without resolution for months. The audit log RBAC discussion was closed without a confirmed fix for the user who reported it (GitHub Discussion #587, October 2024). For teams that cannot absorb an unresolved incident in production, the absence of an escalation path is a meaningful operational risk.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Kafbat is well suited to solo engineers and small teams of up to roughly five engineers who need cluster visibility for development or staging environments, are comfortable managing open-source tooling, and are running self-managed Kafka clusters rather than cloud-managed services. It is a practical starting point where budget is the primary constraint and the team can absorb configuration and maintenance overhead.&lt;/p&gt;
&lt;p&gt;It is not a strong fit for teams where several engineers share a production cluster and need RBAC at team or namespace granularity, organisations with compliance requirements that depend on a clean server-side audit trail, teams using Confluent Cloud on v1.4.x or later, or any organisation that requires a commercial support channel or SLA.&lt;/p&gt;
&lt;h2 id=&quot;kafbat-pricing&quot;&gt;Kafbat pricing&lt;/h2&gt;
&lt;p&gt;Kafbat is free and open-source under the Apache 2.0 licence. There is no paid tier and no enterprise edition.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;What is included&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Full feature set; self-hosted; community support via GitHub Issues and Discussions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;There is nothing to trial: the full application is freely available. Deployment takes a few minutes via Docker or Kubernetes, with no registration required.&lt;/p&gt;
&lt;h2 id=&quot;kafbat-competitors-and-alternatives&quot;&gt;Kafbat competitors and alternatives&lt;/h2&gt;
&lt;p&gt;The open-source Kafka UI market includes a handful of actively maintained tools, from lightweight read-only viewers to fuller operational consoles. Commercial options offer stronger access controls, vendor support, and broader ecosystem integrations at the cost of licensing fees.&lt;/p&gt;
&lt;p&gt;One trend shaping adoption in 2024 and 2025: &lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor’s&lt;/a&gt; decision to restrict its community edition - reducing the number of allowed servers and users - pushed a portion of its user base toward fully open-source alternatives. On r/apachekafka, one developer noted “v1.43 ruined it by reducing the number of servers and users allowed” (Sure-Consideration33, r/apachekafka, approximately 2025). Kafbat and &lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; have been the primary beneficiaries of that migration, though AKHQ has its own ceiling: “We used AKHQ, it’s a nice tool but you hit limitation quite quickly.” (Senior-Act-3761, r/apachekafka, approximately 2025). Teams caught between free tools that run out of headroom and commercial tools with restrictive licensing are the core audience Kafbat is currently serving.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams needing enterprise RBAC, compliance controls, and commercial support&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Multi-cluster Kafka, advanced RBAC, data masking, Schema Registry, Kafka Connect&lt;/td&gt;
&lt;td&gt;Stateless; straightforward deployment; per-cluster pricing&lt;/td&gt;
&lt;td&gt;Advanced RBAC; trusted by large enterprises including HPE&lt;/td&gt;
&lt;td&gt;WCAG-compliant; clean and modern&lt;/td&gt;
&lt;td&gt;Per-cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafbat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small teams and dev environments on a budget&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Multi-cluster, Avro/Protobuf/JSON, CEL filters, Schema Registry, Kafka Connect&lt;/td&gt;
&lt;td&gt;Docker/Kubernetes; Helm chart; dynamic config issues in Kubernetes&lt;/td&gt;
&lt;td&gt;YAML RBAC; OAuth2; LDAP; no team namespacing&lt;/td&gt;
&lt;td&gt;Modern and clean; no filter persistence&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conduktor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enterprise teams needing governance and approval workflows&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Data masking, approval workflows, data quality, consumer group management&lt;/td&gt;
&lt;td&gt;Cloud or self-hosted&lt;/td&gt;
&lt;td&gt;Fine-grained RBAC; team namespacing; approval workflows&lt;/td&gt;
&lt;td&gt;Polished; feature-rich&lt;/td&gt;
&lt;td&gt;Paid tiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams standardised on Confluent Platform&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;End-to-end monitoring, Stream Lineage, ksqlDB, Kafka Connect&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;td&gt;Integrated with Confluent RBAC&lt;/td&gt;
&lt;td&gt;Comprehensive; can feel dense&lt;/td&gt;
&lt;td&gt;Bundled with Confluent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small teams needing a lightweight read-only view&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic browsing, message inspection, consumer groups, Schema Registry&lt;/td&gt;
&lt;td&gt;Docker/Kubernetes&lt;/td&gt;
&lt;td&gt;Basic RBAC&lt;/td&gt;
&lt;td&gt;Functional; less polished than Kafbat&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams on Redpanda or vanilla Kafka&lt;/td&gt;
&lt;td&gt;BSL / Commercial&lt;/td&gt;
&lt;td&gt;Topic management, message viewer, Schema Registry, Kafka Connect&lt;/td&gt;
&lt;td&gt;Docker/Kubernetes; cloud option&lt;/td&gt;
&lt;td&gt;Basic RBAC in BSL tier; more in paid tier&lt;/td&gt;
&lt;td&gt;Modern and fast&lt;/td&gt;
&lt;td&gt;Free (BSL, not Apache 2.0); paid (cloud)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader side-by-side comparison, see &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Top Kafka UI tools in 2026: a practical comparison for engineering teams&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-kafbat&quot;&gt;Frequently asked questions about Kafbat&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-kafbat-cost-and-is-there-a-free-tier&quot;&gt;How much does Kafbat cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;Kafbat is fully free and open-source under Apache 2.0. There is no paid tier. The full application is self-hosted and takes minutes to deploy via Docker or Kubernetes, with no account or registration required.&lt;/p&gt;
&lt;h3 id=&quot;when-is-kafbat-a-better-choice-than-the-alternatives&quot;&gt;When is Kafbat a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;Kafbat suits small teams and dev environments where budget is the primary constraint and the team can absorb configuration overhead. If you are self-managing Kafka, need basic cluster visibility, and do not require an SLA or enterprise controls, Kafbat is a reasonable starting point.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-kafbat&quot;&gt;When are the alternatives a better choice than Kafbat?&lt;/h3&gt;
&lt;p&gt;When you need team-level RBAC, a reliable audit trail, Confluent Cloud stability, or a commercial support channel. Kafbat has documented gaps in all four areas, and two critical unauthenticated RCEs (both CVSS 10.0) have been disclosed in the project’s history.&lt;/p&gt;
&lt;h3 id=&quot;is-kafbat-secure&quot;&gt;Is Kafbat secure?&lt;/h3&gt;
&lt;p&gt;Kafbat has had two CVSS 10.0 vulnerabilities disclosed: one inherited from Provectus, and CVE-2025-49127, which the kafbat team introduced in v1.0.0 and patched in v1.1.0. Running v1.1.0 or later and restricting network access to the UI are the minimum mitigations for production deployments.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Lenses.io: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/lenses/</link><guid isPermaLink="true">https://factorhouse.io/articles/lenses/</guid><description>Lenses.io review for 2026: honest assessment of SQL Studio, deployment complexity, pricing, and when to consider alternatives like Conduktor or Kpow.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lenses.io is a commercial &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;Kafka management platform&lt;/a&gt; that sits on top of your existing Kafka clusters, adding a SQL querying interface, topology visualisation, and a data catalog for multi-cluster environments. This review draws on practitioner feedback collected through mid-2026 to give you an honest picture of where the product delivers value and where it falls short.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Lenses.io’s SQL Studio is its standout feature, giving non-Kafka-savvy team members a familiar interface for inspecting and troubleshooting topics without writing consumer code.&lt;/li&gt;
&lt;li&gt;Multi-cluster topology and cross-cluster data lineage are genuine differentiators for organisations running several Kafka deployments across clouds or regions.&lt;/li&gt;
&lt;li&gt;Deployment complexity, upgrade friction, and the absence of a high-availability option for the Lenses HQ control plane are recurring concerns in practitioner reviews through 2025-2026.&lt;/li&gt;
&lt;li&gt;The community edition caps at two clusters and five users with basic authentication only; SSO, RBAC, and SAML all require the Team tier at a minimum of $4,000/year.&lt;/li&gt;
&lt;li&gt;Lenses was acquired by Celonis in early 2022, leading to uncertainty about future development. The product has continued as a standalone offering and has received some version updates since then.&lt;/li&gt;
&lt;li&gt;If your primary need is operational observability, audit logging, or data masking across Kafka clusters, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow by Factor House&lt;/a&gt; is worth evaluating alongside Lenses.io.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-lensesio&quot;&gt;What is Lenses.io?&lt;/h2&gt;
&lt;p&gt;Lenses.io is a Kafka governance and data exploration platform. It connects to one or more Kafka clusters and surfaces topic data, schema information, consumer group state, and Kafka Connect configuration through a browser-based UI. Its primary differentiator is SQL Studio, a proprietary SQL interface that lets engineers and analysts query Kafka topics using familiar syntax without writing Kafka consumer code.&lt;/p&gt;
&lt;p&gt;The platform also provides a topology view that maps data lineage across topics, connectors, and applications (including externally registered Flink jobs). A data catalog groups topics by logical domain and supports schema registry integration.&lt;/p&gt;
&lt;p&gt;Lenses operates as a Kafka client; it does not sit in the data path and does not act as a proxy. The current architecture consists of a central Lenses HQ node and lightweight agents deployed per cluster. KRaft clusters are supported without modification, since the agent connects as a standard Kafka client.&lt;/p&gt;
&lt;p&gt;Within the Kafka tooling ecosystem, practitioners draw a distinction between lightweight desktop clients designed for quick topic inspection and enterprise control planes that add governance, RBAC, and audit workflows. Lenses.io sits firmly in the latter category: its value becomes most apparent once multiple teams are sharing a cluster infrastructure and need guardrails around access, configuration changes, and data visibility.&lt;/p&gt;
&lt;p&gt;The product is available in three tiers: a free community edition, a Team edition, and an Enterprise edition.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722c109a967e2ee935e20_lenses-blog-screenshot.avif&quot; alt=&quot;Lenses&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;lensesio-review&quot;&gt;Lenses.io review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;The SQL Studio is consistently the most praised aspect of Lenses.io in practitioner reviews. Juan Luis d. on G2 (November 2025) described it as “providing a straightforward way to troubleshoot problem cases and check topic metrics,” with the broader UI characterised as “accessible for both experienced individuals as well as those who are not very familiar with Kafka development.” Community discussion on r/apachekafka echoes this: engineers highlight that SQL Studio lowers the barrier for team members who are not comfortable with Kafka’s internal mechanics, replacing the need to search through millions of raw JSON, Avro, or Protobuf payloads with familiar query syntax.&lt;/p&gt;
&lt;p&gt;The topology and lineage view is the second most cited strength. In multi-cluster environments where teams need a single pane of glass across producers, topics, connectors, and consumers, the topology view reduces the operational overhead of maintaining that picture manually.&lt;/p&gt;
&lt;p&gt;Lenses.io also provides SQL Processors, Kubernetes-native stream processing engines built on Kafka Streams. These allow teams to define stateful and stateless processing rules in SQL, compiled and executed within a Kubernetes cluster, without maintaining a dedicated Flink or Spark infrastructure. For straightforward transformation and filtering tasks, this can be a practical option. However, the proprietary nature of SQL Processors is a meaningful consideration. Jose Manuel C. on G2 (November 2025) wrote: “Some of Lenses’ functionalities that are not open-source create a vendor lock-in, with SQL Processors being the most clear example… the tradeoff doesn’t pay off in the long run.” Teams considering SQL Processors as a core part of their architecture should weigh portability before committing.&lt;/p&gt;
&lt;p&gt;The limitations are equally consistent across reviews. The ACL management interface has not advanced meaningfully across major releases: one unnamed G2 reviewer from the retail sector noted that version 6 “introduced a new interface, but the same issues remain: the ACLs listing is still too basic.” Bug resolution is perceived as slow, with Juan Luis d. noting “ongoing bugs within Lenses that remain unresolved, highlighting a potential lack of responsiveness in bug fixes.” One practitioner on r/apachekafka put it more directly: “I often felt like a QA for Lenses developers as a customer and it was exhausting.”&lt;/p&gt;
&lt;p&gt;Schema Registry integration exists but was described by Berta m. on G2 (November 2025) as “not entirely optimal” in terms of schema inference accuracy.&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Lenses.io supports Helm-based Kubernetes deployment, which is the recommended path. In practice, practitioners have encountered meaningful friction here.&lt;/p&gt;
&lt;p&gt;The platform runs a server-side JVM backend alongside its web interface, which makes it a relatively resource-intensive deployment. This comes up frequently in community discussions: developers who only need to inspect local topics during development find the Docker and JVM overhead excessive compared to lighter alternatives. Starting with version 5, the community edition also restricts connections to single-node clusters, which has frustrated users attempting to test multi-broker SSL-enabled environments locally. Several have reported rolling back to version 4.3 to retain multi-broker support in their trial setups.&lt;/p&gt;
&lt;p&gt;Upgrades to Lenses 6 HQ triggered Liquibase database migration errors in at least one documented case on the community forum (ask.lenses.io, November 2025), where a column-rename operation failed depending on the source version and database backend. A separate reported issue involved Traefik ingress login loops caused by the default &lt;code&gt;secureSessionCookies: true&lt;/code&gt; setting, which breaks non-HTTPS environments; the workaround requires adding &lt;code&gt;lensesHq.http.secureSessionCookies: false&lt;/code&gt; to &lt;code&gt;values.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A third operational issue affects teams connecting Lenses to Amazon MSK: the default broker metrics refresh interval (approximately 5 seconds) generates an unexpectedly high volume of JMX requests against MSK’s OpenMetrics/Prometheus-backed JMX exporter. The recommended workaround is to set the interval to 30 seconds or higher (ask.lenses.io, April-May 2026).&lt;/p&gt;
&lt;p&gt;Lenses HQ does not support high availability in the current architecture. Multiple G2 reviewers in 2025 identified this as a gap, particularly in production environments where the control plane itself needs to be resilient. The community edition further limits deployment to two clusters with five users and basic authentication only; a Helm chart for community edition was still a feature request as recently as May 2025.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;SSO (Okta, Keycloak, OneLogin, Google, Azure/Entra ID) and RBAC are available but gated to the Team tier at $4,000/year minimum. The community edition provides only username/password authentication.&lt;/p&gt;
&lt;p&gt;LDAP integration has had documented instability across recent releases. A connection-reset bug was fixed in v5.5.14 (December 2024). Version 5.5.6 (August 2024) introduced a breaking behaviour change: new LDAP users are no longer automatically created unless they belong to a mapped group, which silently broke access for teams relying on the previous behaviour.&lt;/p&gt;
&lt;p&gt;Azure Entra ID SAML integration has a documented limitation: Azure exposes only Group UUIDs (not group names) via SAML, requiring administrators to use UUID strings as group identifiers within Lenses. Google SSO requires custom attribute mapping because Google does not expose user groups or organisation units to SAML applications by default.&lt;/p&gt;
&lt;p&gt;The read-only account use case, a common requirement for giving stakeholders visibility without change permissions, was an unanswered question on the community forum as of August 2025, which suggests RBAC granularity for viewer-only roles is either not well-documented or not straightforward to configure in v6.&lt;/p&gt;
&lt;p&gt;It is worth noting that identity provider support has historically been a point of friction with Lenses. The team behind Kowl (now Redpanda Console) cited Lenses’ lack of support for their preferred identity provider and group configurations as one of the motivating reasons they built their own tool.&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;The general UI receives consistently positive marks in practitioner reviews. Descriptions include “simple and effective” (unnamed reviewer, retail sector, G2, November 2025), “user friendly, easy to use” (unnamed reviewer, apparel sector, G2, November 2025), and praise for the ease with which non-engineers can interact with topic configurations.&lt;/p&gt;
&lt;p&gt;The version 6 redesign refreshed the visual style but did not resolve the underlying capability gaps that existed in v5. The ACL management views remain limited, and the deployment experience was still described as “complicated” by reviewers writing after the v6 release. Documentation quality is also cited as a persistent concern, with multiple reviewers in 2025 describing it as “insufficient and unclear.”&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Lenses operates as a Kafka client and integrates with Schema Registry, Kafka Connect, and the Topology view can include externally registered Flink jobs for monitoring (though Flink is not natively managed through the platform). KRaft clusters are supported; the only known limitation is a minor display bug where the controller count is not shown correctly on KRaft clusters (confirmed by a Lenses team member on ask.lenses.io, July 2025).&lt;/p&gt;
&lt;p&gt;The open-source Stream Reactor connector library extends Lenses with a range of sinks and sources. Active GitHub issues from late 2025 through early 2026 show a maintenance backlog for the S3 connector (ByteArrayConverter envelope restoration failures, ConnectionClosedException on S3 Source), an MQTT source SchemaParseException triggered by topic names containing hyphens, and Azure Data Lake Gen2 connector header errors.&lt;/p&gt;
&lt;p&gt;Teams connecting to MSK Serverless with IAM authentication should note a documented access-denied issue: Lenses 6 Agent requires &lt;code&gt;kafka-cluster:*&lt;/code&gt; IAM actions, not the broader &lt;code&gt;kafka:*&lt;/code&gt; scope. A well-documented workaround exists on the community forum.&lt;/p&gt;
&lt;h3 id=&quot;celonis-acquisition-and-product-continuity&quot;&gt;Celonis acquisition and product continuity&lt;/h3&gt;
&lt;p&gt;Celonis acquired Lenses.io in early 2022. At the time, the acquisition generated significant uncertainty within the Kafka community, with several engineers on r/dataengineering and r/apachekafka speculating that Celonis had bought the company primarily for its core technology and planned to discontinue the standalone product.&lt;/p&gt;
&lt;p&gt;Lenses.io continues to operate as a standalone commercial product, and the cadence of version releases (including 5.3, 5.4, and the version 6 UI redesign) indicates that Celonis has maintained some active development. For teams evaluating the product, the acquisition risk appears lower than it did in 2022 and 2023, though Celonis has not publicly committed to a long-term standalone roadmap in specific terms.&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Community edition users rely on the public forum (ask.lenses.io) and documentation. The forum is actively monitored by a small number of Lenses team members, and response quality for technical issues is generally fair. Enterprise support SLAs are available at the Team and Enterprise tiers.&lt;/p&gt;
&lt;p&gt;Independent review coverage is thin. Gartner Peer Insights has two ratings as of mid-2026, giving Lenses a 3.7/5 overall with a 50% willingness to recommend. TrustRadius and PeerSpot had insufficient or no reviews to report an aggregate score at the time of this research. Roadmap clarity and documentation quality were the most frequently cited support-related concerns in 2025 G2 reviews.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Lenses.io is a good fit for teams where the primary use case is giving business analysts or less Kafka-savvy engineers self-service access to Kafka topic data through a familiar SQL interface, without requiring them to write consumer code or understand Kafka internals. It is also well-suited to organisations running multiple Kafka clusters across regions or cloud accounts who need unified topology visibility and cross-cluster querying.&lt;/p&gt;
&lt;p&gt;It is worth evaluating carefully if your organisation is small or budget-constrained (the step from the community edition to the Team tier is significant), if high availability for the control plane is a hard requirement, or if you are building on Kafka primarily for operational observability and governance rather than SQL-based data exploration.&lt;/p&gt;
&lt;h2 id=&quot;lensesio-pricing&quot;&gt;Lenses.io pricing&lt;/h2&gt;
&lt;p&gt;Lenses.io uses a tiered model with a free community edition and two paid tiers. Pricing details are based on the Lenses pricing page and community forum sources.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Clusters&lt;/th&gt;
&lt;th&gt;Users&lt;/th&gt;
&lt;th&gt;Notable limits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Basic authentication only; no SSO, no RBAC; single-node clusters only from v5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team&lt;/td&gt;
&lt;td&gt;From $4,000/year&lt;/td&gt;
&lt;td&gt;Multiple&lt;/td&gt;
&lt;td&gt;Multiple&lt;/td&gt;
&lt;td&gt;SSO, SAML, RBAC, and team-level support included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Contact sales&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Enterprise support SLAs, advanced governance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The jump from Community to Team is steep for smaller teams. SSO and any form of role-based access control require the paid Team tier, which starts at $4,000/year. The community edition’s two-cluster ceiling also limits its usefulness for anything beyond a single development or staging environment.&lt;/p&gt;
&lt;h3 id=&quot;self-hosting-context&quot;&gt;Self-hosting context&lt;/h3&gt;
&lt;p&gt;One recurring theme in community discussion is the financial case for self-hosting Kafka on Kubernetes with an operator like Strimzi, combined with a commercial management plane. Engineers on r/apachekafka have noted that managed Kafka pricing can change significantly with billing model updates: one example cited a partition-based fee change that would have increased monthly costs from $30 to $1,200 for a single team. Against that backdrop, the predictability of a fixed annual licence for Lenses (or an alternative like &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;) can be attractive, even at the Team tier price point.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;A time-limited trial of the Team or Enterprise edition is not available from the Lenses website.&lt;/p&gt;
&lt;h2 id=&quot;lensesio-competitors-and-alternatives&quot;&gt;Lenses.io competitors and alternatives&lt;/h2&gt;
&lt;p&gt;Lenses.io occupies a specific niche: SQL-based self-service access and multi-cluster topology for teams investing in a DataOps model. Depending on your actual requirements, other tools may be a better fit. The table below covers the most relevant alternatives across open-source and commercial options.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conduktor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Governance-first organisations needing proxy-level policy enforcement, data masking, and audit trails&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Consumer group management, schema registry, Connect, gateway proxy for policy enforcement&lt;/td&gt;
&lt;td&gt;Kubernetes, Docker; proxy sits in data path&lt;/td&gt;
&lt;td&gt;SSO, RBAC, data masking, audit log&lt;/td&gt;
&lt;td&gt;Clean and opinionated; strong governance UI&lt;/td&gt;
&lt;td&gt;Tiered; community plan available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow by Factor House&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Platform teams needing stateless deployment, air-gap operation, per-cluster pricing, and comprehensive RBAC with audit logging&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic inspection with kJQ filtering, consumer lag, Kafka Streams topology, schema-aware deserialization, staged approval workflows&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes, JAR; stateless, no external DB; air-gap capable; deploys in minutes&lt;/td&gt;
&lt;td&gt;SSO (Okta, Azure AD, Keycloak, LDAP, SAML, OAuth2), RBAC, data masking, full audit log&lt;/td&gt;
&lt;td&gt;Focused and fast; no SQL layer&lt;/td&gt;
&lt;td&gt;Community free; Enterprise from $3,950/year per cluster; 30-day trial, no credit card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKHQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Development teams wanting a zero-cost, self-hosted Kafka UI&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic browser, consumer group management, schema registry, Connect&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes; self-managed&lt;/td&gt;
&lt;td&gt;Basic auth, LDAP, OAuth2; limited fine-grained RBAC&lt;/td&gt;
&lt;td&gt;Functional; suited to engineers&lt;/td&gt;
&lt;td&gt;Free (self-hosted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafbat UI (kafka-ui)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams wanting a lightweight, actively maintained OSS alternative to the archived kafka-ui&lt;/td&gt;
&lt;td&gt;OSS&lt;/td&gt;
&lt;td&gt;Topic/consumer group browser, schema registry, Connect, basic ACL management&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes; self-managed&lt;/td&gt;
&lt;td&gt;Basic auth, OAuth2&lt;/td&gt;
&lt;td&gt;Modern, configurable&lt;/td&gt;
&lt;td&gt;Free (self-hosted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams fully committed to the Confluent Platform stack&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Cluster health, ksqlDB, stream lineage, connector management&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform; cloud or on-prem&lt;/td&gt;
&lt;td&gt;Confluent RBAC&lt;/td&gt;
&lt;td&gt;Polished; tightly coupled to Confluent&lt;/td&gt;
&lt;td&gt;Included in Confluent Platform licensing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams using Redpanda or wanting a lightweight Kafka-compatible UI&lt;/td&gt;
&lt;td&gt;OSS / Commercial&lt;/td&gt;
&lt;td&gt;Topic browser, consumer groups, schema registry, Connect&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes; lightweight&lt;/td&gt;
&lt;td&gt;Basic auth; SSO in enterprise tier&lt;/td&gt;
&lt;td&gt;Minimal, fast&lt;/td&gt;
&lt;td&gt;Free OSS; paid tiers for enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a more detailed comparison of Kafka UI tooling, see our &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;guide to Kafka UI tools in 2026&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-lensesio&quot;&gt;Frequently asked questions about Lenses.io&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-lensesio-cost-and-is-there-a-free-tier&quot;&gt;How much does Lenses.io cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;The community edition is free and supports 2 clusters and 5 users with basic authentication only. From version 5, the community edition is also restricted to single-node clusters. SSO and RBAC require the Team tier, which starts at $4,000/year. Enterprise pricing is available on request.&lt;/p&gt;
&lt;h3 id=&quot;when-is-lensesio-a-better-choice-than-the-alternatives&quot;&gt;When is Lenses.io a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;Lenses.io is strongest when your team needs SQL-based self-service access to Kafka topics for non-engineering stakeholders, or when you need unified topology visibility and cross-cluster querying across multiple Kafka deployments. No comparable OSS tool provides an equivalent SQL Studio experience.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-lensesio&quot;&gt;When are the alternatives a better choice than Lenses.io?&lt;/h3&gt;
&lt;p&gt;If your primary need is operational governance (audit logging, data masking, gateway-level policy enforcement), Conduktor’s proxy architecture is better suited. For teams whose main requirement is comprehensive RBAC, audit logging, and stateless deployment without an external database dependency, Kpow is worth evaluating. For small teams or single-cluster deployments, the community edition’s limits and the $4,000/year jump to SSO make the cost-to-value ratio difficult to justify against open-source alternatives.&lt;/p&gt;
&lt;h3 id=&quot;does-lensesio-support-kraft&quot;&gt;Does Lenses.io support KRaft?&lt;/h3&gt;
&lt;p&gt;Yes. Lenses connects to Kafka clusters as a standard client, so KRaft clusters work without modification. There is a minor known display issue where the controller count is not shown correctly on KRaft clusters, confirmed by the Lenses team in July 2025.&lt;/p&gt;
&lt;h3 id=&quot;is-there-high-availability-for-the-lenses-control-plane&quot;&gt;Is there high availability for the Lenses control plane?&lt;/h3&gt;
&lt;p&gt;Not in the current architecture. Lenses HQ is a single-node deployment. Multiple practitioner reviews from 2025 identified the absence of native HA as a gap for production control-plane resilience. Manual workarounds are possible but are not a supported configuration.&lt;/p&gt;
&lt;h3 id=&quot;is-lensesio-still-actively-developed-following-the-celonis-acquisition&quot;&gt;Is Lenses.io still actively developed following the Celonis acquisition?&lt;/h3&gt;
&lt;p&gt;Yes. Celonis acquired Lenses.io in early 2022, which initially prompted concern in the community about product continuity. The product has continued as a standalone offering since then, with version updates including a full v6 UI redesign and ongoing connector and feature development. Community forum support from Lenses team members has remained active.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Redpanda Console: Review, pricing, and best alternatives in 2026</title><link>https://factorhouse.io/articles/redpanda-console/</link><guid isPermaLink="true">https://factorhouse.io/articles/redpanda-console/</guid><description>Redpanda Console reviewed for 2026: features, pricing, limitations, and the best alternatives for engineering teams running Apache Kafka or Redpanda.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Redpanda Console’s message viewer is among the best available in any free Kafka UI: time-travel offset debugging, multi-format deserialization (Avro, Protobuf, JSON, XML, CBOR, MessagePack), and JavaScript-based filtering are standout capabilities for developer workflows.&lt;/li&gt;
&lt;li&gt;The Observer Mode lets developers stream topic messages without joining a consumer group, avoiding accidental rebalancing on production offsets.&lt;/li&gt;
&lt;li&gt;The tool is a viewer and browser, not an operational platform. There are no built-in broker metrics, no alerting, and no historical trend views; a separate Prometheus/Grafana stack is required for production monitoring.&lt;/li&gt;
&lt;li&gt;RBAC, SSO, and data masking are locked behind a paid Redpanda Enterprise license. Teams running vanilla Apache Kafka or Amazon MSK who need access controls will pay for a Redpanda license even if they do not run Redpanda as their broker.&lt;/li&gt;
&lt;li&gt;Multi-cluster management is absent at the broker tier. Teams managing dev, staging, and production clusters must deploy separate Console instances.&lt;/li&gt;
&lt;li&gt;If you need production-grade access controls, multi-cluster support, and a fast-moving commercial roadmap, &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is worth evaluating as a dedicated Apache Kafka management tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-redpanda-console&quot;&gt;What is Redpanda Console?&lt;/h2&gt;
&lt;p&gt;Redpanda Console is an open source web UI for inspecting and managing Apache Kafka-compatible clusters. It was originally developed by CloudHut under the name Kowl and acquired by Redpanda in April 2022. The product serves both Redpanda clusters (where it integrates with the Redpanda admin API for additional capabilities) and vanilla Apache Kafka, Amazon MSK, and Confluent Platform deployments.&lt;/p&gt;
&lt;p&gt;The community edition is available under the Business Source License (BSL), which permits free use by internal teams but restricts commercial SaaS use. An enterprise edition adds RBAC, SSO (OIDC), and data masking behind a paid Redpanda Enterprise license.&lt;/p&gt;
&lt;p&gt;The application is built on Go and React, and is distributed as a Docker image and a Helm chart.&lt;/p&gt;
&lt;p&gt;Platform neutrality is a frequently confirmed strength among practitioners. Engineers running standard Apache Kafka report the tool works well outside of a Redpanda environment: “I’ve been using Redpanda’s console and it’s pretty awesome. You should be able to use it with any implementation of Kafka — that’s what I’m doing.” [vintage_px, r/apachekafka, 2024] This positions Console as a viable standardisation choice for teams running diverse broker configurations across on-premise, cloud-managed, and hybrid environments.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7228f5010387c137d9bf0_redpanda-console-blog-screenshot.avif&quot; alt=&quot;Redpanda Console&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;redpanda-console-review&quot;&gt;Redpanda Console review&lt;/h2&gt;
&lt;h3 id=&quot;functionalities&quot;&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;Redpanda Console’s primary strength is its message viewer. The interface supports consuming, seeking, and filtering messages with a level of care that practitioners consistently describe as best-in-class for a free tool. One engineer who spent a year running Redpanda in production wrote that the interface made dedicated ad-hoc tools like Apache Zeppelin or custom query applications unnecessary. [Yaroslav Tkachenko, streamingdata.tech, July 2023]&lt;/p&gt;
&lt;p&gt;Time-travel offset management, replay workflows, and JavaScript-based message filtering are well-regarded for developer debugging scenarios. Deserialization covers Avro, Protobuf, JSON, XML, CBOR, MessagePack, and binary (hex).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protobuf without a schema registry&lt;/strong&gt; is a less-discussed but practically useful capability. Teams with Protobuf-encoded topics that have no schema registry in their stack can configure Console with local proto descriptor maps, declaring which schema applies to which topic. The original Kowl maintainer confirmed: “Redpanda Console is capable of doing this — you just have to configure what Protos it should use for each topic.” [leventus93, r/apachekafka, 2022] This avoids the common workaround of running external CLI decoding scripts during incident response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observer Mode&lt;/strong&gt; allows developers to stream and browse topic messages without joining an active consumer group. This matters in production contexts: joining a consumer group triggers a rebalance, which can affect downstream processing and pollutes offset tracking. Observer Mode sidesteps this entirely. Engineering teams reference it specifically for development-time debugging and lag investigation: “We mainly use scripts as we want automation. But we use Kowl/Redpanda Console for quickly viewing messages or checking lags during development or troubleshooting for instance.” [handstand2001, r/apachekafka, 2025]&lt;/p&gt;
&lt;p&gt;Beyond the message viewer, the feature set thins out. The tool has no built-in broker metrics, no alerting, and no historical trend analysis. An independent comparison from January 2026 concluded that Console is suited to “browsing and basic management” and that any production monitoring setup requires an external stack. [KLogic, klogic.io, January 2026]&lt;/p&gt;
&lt;p&gt;Consumer group management is present, but there is a known rendering bug: on some cluster configurations a partition is absent from the consumer group view in the UI while being correctly visible via the command-line tool &lt;code&gt;rpk group describe&lt;/code&gt;. [freef4ll, GitHub Issue #447, August 2022]&lt;/p&gt;
&lt;p&gt;Schema Registry support was historically read-only. A GitHub issue requesting write operations (creating, updating, and deleting schemas) was filed in August 2022 and closed, suggesting the capability was addressed, though the exact resolution is not confirmed in public sources. [sap1ens, GitHub Issue #434, August 2022] An intermittent display bug causing the Schema Registry Overview card to show “Not configured” despite correct configuration was filed in April 2026. [haoyukongTrackunit, GitHub Issue #2422, April 2026]&lt;/p&gt;
&lt;p&gt;There is no ability to produce (write) messages to a topic via the UI. A feature request for this was filed in May 2021 and closed with no visible resolution. [tej1996nitrr, GitHub Issue #221, May 2021] The capability was discussed publicly on Reddit as far back as 2021, when the then-Kowl maintainer indicated it was on the roadmap — including key and value input fields, JSON validation, and confirmation dialogs — but it has not appeared in the stable release as of the research date. [Cell-i-Zenit and leventus93, r/apachekafka, 2021]&lt;/p&gt;
&lt;p&gt;Console cannot set request quotas or trigger partition rebalancing. [Zeenia Gupta, platformatory.io, September 2024]&lt;/p&gt;
&lt;h3 id=&quot;deployment-and-operations&quot;&gt;Deployment and operations&lt;/h3&gt;
&lt;p&gt;Docker and Helm deployments are straightforward for single-cluster setups. One engineer deployed Console on Amazon EKS via Helm with “minimal configuration required.” [Priyankar Prasad, medium.com, February 2023]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Local developer overhead&lt;/strong&gt; is a recurring friction point for engineers who want ad-hoc topic inspection without running a persistent stack. Because Console is a server-side web application rather than a native desktop client, it requires Docker or a running server. Practitioners note the mismatch for lightweight diagnostic tasks: “Kafka UI, AKHQ, Redpanda Console — all great, but they’re web apps that need Docker or a server. On my work machine I don’t always have Docker running, and spinning up a container just to peek at a topic feels like overkill.” [r/apachekafka, 2025] This has driven interest in native desktop and TUI alternatives for local development loops, even among teams that run Console centrally for shared cluster access.&lt;/p&gt;
&lt;p&gt;There is a known Helm upgrade bug: upgrading with &lt;code&gt;console.enabled: true&lt;/code&gt; fails with “cannot patch ‘redpanda-console’ with kind Deployment” due to immutable label selectors. This is documented in the official Redpanda troubleshooting guides.&lt;/p&gt;
&lt;p&gt;At scale, hardcoded timeouts become a meaningful operational problem. &lt;code&gt;GetClusterInfo&lt;/code&gt; has a 6-second timeout for DescribeLogDirs and Metadata; &lt;code&gt;GetTopicsOverview&lt;/code&gt; has a 5-second timeout for DescribeConfigs. On large AWS MSK clusters with IAM authentication, these limits are regularly exceeded because the overhead of the STS token exchange with IAM adds latency that the hardcoded values do not account for. Observed actual times of 4.9s and 5.7s were reported in April 2026. [grassiale, GitHub Issue #2410, April 2026] A separate timeout of 35 seconds in &lt;code&gt;ListMessages&lt;/code&gt; also causes failures on slow or heavily filtered clusters; versions 3.5.2, 3.6.0, and 3.7.2 are all affected, and users have requested configurable timeout values. [cobolbaby, GitHub Issue #2432, May 2026]&lt;/p&gt;
&lt;p&gt;Partial cluster failures expose a design limitation: when a single broker goes offline in a multi-node cluster, all consumer group queries fail entirely rather than degrading gracefully. The error reads “failed to list end offsets for topics: request ListOffsets has 1 separate shard errors.” This is a significant problem during incidents, when observability is most critical. [cobolbaby, GitHub Issue #2327, March 2026]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes and TLS complexity&lt;/strong&gt; is a documented risk for teams running Console in Kubernetes alongside the Redpanda Operator. Integrating with Knative channels, ingress controllers, or custom TLS configurations can lead to certificate validation failures against the Admin API on port 9644, hostname verification errors, and &lt;code&gt;rpk debug bundle&lt;/code&gt; failures when localhost is not included in the certificate’s subject alternative names. One practitioner documented spending approximately 20 hours attempting to resolve these issues on a fresh GitOps-managed cluster before switching to Kafka with Strimzi, which completed the equivalent setup in under an hour: “After 3-4 hours (now total of about 20 hrs), I decided to cut losses with Redpanda Operator, and instead went with Strimzi.” [JuroOravec, r/kubernetes, 2024] Automated CD pipelines can encounter additional friction from Helm rate-limiting during FluxCD reconciliation of the Console HelmRelease.&lt;/p&gt;
&lt;h3 id=&quot;access-control-and-security&quot;&gt;Access control and security&lt;/h3&gt;
&lt;p&gt;RBAC and SSO (OIDC) are enterprise-only features. The free community tier has no access control of any kind. This is a significant limitation for teams managing shared clusters in multi-team or regulated environments.&lt;/p&gt;
&lt;p&gt;The OIDC integration introduces a split authentication model that is worth understanding before committing to it. OIDC SSO for the Console UI is available across Redpanda Enterprise Self-Managed, BYOC, and Dedicated cloud deployments. However, OIDC authentication for the Kafka API, HTTP Proxy API, Admin API, and Schema Registry API is only available in Redpanda Enterprise Self-Managed as of early 2026. [r/redpanda, 2025] Teams on BYOC or Dedicated therefore operate two parallel security models: human operators authenticate through a centralised identity provider when accessing the Console UI, while automated client applications must fall back to mTLS, SASL/SCRAM, or OAuthBearer tokens when accessing the same APIs programmatically. This increases the maintenance surface and complicates auditing.&lt;/p&gt;
&lt;p&gt;If the enterprise license expires at runtime, Redpanda Console shuts down entirely. If the license has already expired at startup, Console prints an error and exits. This behaviour creates production risk if license renewal is missed. [Redpanda documentation, docs.redpanda.com]&lt;/p&gt;
&lt;p&gt;A security configuration bug filed in May 2026 reports that Kafka Connect requests ignore the Console base path setting. [malinskibeniamin, GitHub Issue #2440, May 2026]&lt;/p&gt;
&lt;p&gt;Multi-tenant deployments requiring per-account payload visibility controls present an additional challenge. Teams co-mingling records from different accounts on shared topics, where access must be restricted by jurisdiction or contract, need data masking rules that redact values based on user group membership while preserving metadata for debugging. This capability is available in Console but is gated behind the enterprise license. [GitHub Issue discussion, r/apachekafka, 2021]&lt;/p&gt;
&lt;p&gt;One comparison article summarised the commercial model plainly: “Every enterprise feature that matters (SSO, RBAC, data masking) requires a paid Redpanda Enterprise license. This creates a problematic dynamic for vanilla Apache Kafka or MSK users: you can use the viewer for free, but the moment you need governance, you’re paying for a Redpanda license even if you don’t run Redpanda.” [Factor House, factorhouse.io]&lt;/p&gt;
&lt;h3 id=&quot;user-interface&quot;&gt;User interface&lt;/h3&gt;
&lt;p&gt;The UI is consistently described as modern, clean, and developer-focused. Practitioner feedback from 2022 onwards regularly describes it as the best-looking free Kafka UI available, with the message viewer praised as “extremely thoughtful.” [Yaroslav Tkachenko, streamingdata.tech, July 2023; rfernandez2007, Confluent Community Forum, February 2022]&lt;/p&gt;
&lt;p&gt;Several active frontend bugs undermine this reputation on specific configurations. Pressing &lt;code&gt;?&lt;/code&gt; (the help shortcut) crashes the entire page with a React error when Console is connected to vanilla Apache Kafka rather than Redpanda. The root cause is repeated failed calls to the &lt;code&gt;ListEnterpriseFeatures&lt;/code&gt; endpoint, which returns HTTP 501 “the Redpanda admin API must be configured to use this endpoint.” The bug affects versions 3.1.0, 3.3.0, and 3.5.3. [alexkau, GitHub Issue #2262, March 2026] A separate input field bug causes the search text field to replace typed input with a scientific notation number. [Wouter-M, GitHub Issue #2459, May 2026] Message previews could not expand or collapse after upgrading to Console 3.5+. [cobolbaby, GitHub Issue #2247, February 2026]&lt;/p&gt;
&lt;p&gt;Users running Confluent Platform noted that the tool “felt optimised for Redpanda rather than Confluent Platform,” with credential propagation limitations for Kafka Connect. [whatsupbros, Confluent Community Forum, March 2023]&lt;/p&gt;
&lt;h3 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;/h3&gt;
&lt;p&gt;Kafka Connect management is present: Console queries all configured Kafka Connect clusters and supports multi-cluster Kafka Connect setups (unlike broker-tier multi-cluster, which is not supported). Functionality covers connector discovery, bulk configuration operations, and basic task status monitoring. This distinction matters for teams with connector-heavy architectures who want a single pane for Connect management without needing separate instances per environment. [Redpanda documentation, docs.redpanda.com]&lt;/p&gt;
&lt;p&gt;There is no native support for ksqlDB. A feature request was filed in 2021 and, as of the research date, there is no confirmed implementation. [GitHub Issue #177]&lt;/p&gt;
&lt;p&gt;Multi-cluster management at the broker tier is not supported. A team managing development, staging, and production clusters must run three separate Console instances. Two GitHub issues requesting single-instance multi-cluster support were filed in 2021 and 2022; one remains open. [GitHub Issues #250, #349]&lt;/p&gt;
&lt;p&gt;Support for Redpanda Connect (pipeline) instances via Console has been requested but is not yet available. [steffyd, GitHub Issue #2430, May 2026]&lt;/p&gt;
&lt;h3 id=&quot;licensing-and-community-reception&quot;&gt;Licensing and community reception&lt;/h3&gt;
&lt;p&gt;The BSL licensing transition following the Redpanda acquisition of CloudHut in 2022 has been a persistent source of friction in the open-source community. Kowl was originally maintained under highly permissive terms that encouraged broad adoption. The shift to commercial licensing alienated a portion of the tool’s user base, particularly those who relied on it for sporadic or non-commercial use: “I was a heavy user of Kowl, but I do not like their new Redpanda license. Waiting to hear other people’s recommendations. I need a free version for sporadic usage.” [CasperTDK, r/apachekafka, 2022]&lt;/p&gt;
&lt;p&gt;This pattern is not unique to Redpanda Console. Competing tools have followed similar trajectories: Conduktor restricted its community version in release v1.43, reducing the number of allowed servers and users, while Lenses charges approximately $3,650 per cluster annually for its commercial tier. The community response has been a cycle of open-source alternatives gaining rapid adoption — most recently Kafbat UI (the maintained Apache 2.0 fork of the original Kafka-UI project) — followed by those alternatives eventually reaching the same commercial crossroads.&lt;/p&gt;
&lt;p&gt;For teams that need enterprise access controls and are committed to the Redpanda ecosystem, the licensing model is a reasonable trade-off. For teams running vanilla Apache Kafka or Amazon MSK who simply want governance features, the situation is less comfortable: the enterprise license funds Redpanda’s commercial interests regardless of whether the buyer runs a single Redpanda broker.&lt;/p&gt;
&lt;h3 id=&quot;customer-support&quot;&gt;Customer support&lt;/h3&gt;
&lt;p&gt;Documentation at docs.redpanda.com is comprehensive, covering installation, configuration, security, Kubernetes deployment, and troubleshooting in detail. Active changelog entries confirm ongoing bug fixes and security patches.&lt;/p&gt;
&lt;p&gt;GitHub Issues is the primary community support channel for bugs and feature requests, and engagement from the Redpanda team is visible across recent issues.&lt;/p&gt;
&lt;p&gt;No practitioner accounts of enterprise support quality, response times, or account management were available in the public sources reviewed for this article.&lt;/p&gt;
&lt;h3 id=&quot;best-for&quot;&gt;Best for&lt;/h3&gt;
&lt;p&gt;Redpanda Console is best suited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Teams running Redpanda&lt;/strong&gt; as their broker (self-managed or cloud). The native integration with the Redpanda admin API enables features that are unavailable on vanilla Kafka. [Hayato Shimizu, axonops.com, December 2025]&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solo developers and small teams&lt;/strong&gt; who need a lightweight, visually clean message browser with strong deserialization support for a single cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer debugging workflows&lt;/strong&gt;: time-travel offset management, Observer Mode for non-intrusive topic inspection, offset rollback for replay scenarios, and JavaScript-based message filtering are the standout capabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform teams hosting a shared single-cluster instance&lt;/strong&gt; for internal developers, where the platform team handles SASL/TLS configuration and developers access a read-oriented UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is a poor fit for teams that need multi-cluster management, production observability without a separate monitoring stack, governance features without paying for a Redpanda Enterprise license, reliable operation on large AWS MSK clusters with IAM authentication, or a native desktop option for lightweight local inspection.&lt;/p&gt;
&lt;h2 id=&quot;redpanda-console-pricing&quot;&gt;Redpanda Console pricing&lt;/h2&gt;
&lt;p&gt;Redpanda Console is open source under the Business Source License. Internal use is free; commercial SaaS use requires a commercial agreement.&lt;/p&gt;
&lt;h3 id=&quot;pricing-tiers&quot;&gt;Pricing tiers&lt;/h3&gt;
&lt;p&gt;The community edition is free and covers the core message viewer, topic and consumer group management, Kafka Connect management, and Schema Registry browsing. RBAC, SSO (OIDC), and data masking are not included.&lt;/p&gt;
&lt;p&gt;Enterprise features require a Redpanda Enterprise license. Specific pricing for the enterprise tier is not published publicly.&lt;/p&gt;
&lt;h3 id=&quot;free-trial&quot;&gt;Free trial&lt;/h3&gt;
&lt;p&gt;There is no time-limited trial for the enterprise tier described in the available public documentation. Teams can run the community edition indefinitely without a license.&lt;/p&gt;
&lt;h2 id=&quot;redpanda-console-competitors-and-alternatives&quot;&gt;Redpanda Console competitors and alternatives&lt;/h2&gt;
&lt;p&gt;Redpanda Console occupies the developer-friendly free-tier end of the Kafka UI market. The landscape includes tools that match it on open-source availability (AKHQ, Kafbat UI) and commercial tools that extend further into governance, observability, and multi-cluster management. The right choice depends heavily on whether you need production-grade access controls and whether you run a single cluster or many.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool / Best for&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Key functionalities&lt;/th&gt;
&lt;th&gt;Deployment and ops&lt;/th&gt;
&lt;th&gt;Access control&lt;/th&gt;
&lt;th&gt;User interface&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redpanda Console&lt;/strong&gt; Redpanda and single-cluster Kafka browsing&lt;/td&gt;
&lt;td&gt;OSS (BSL)&lt;/td&gt;
&lt;td&gt;Message viewer, deserialization (Avro, Protobuf, JSON, XML, CBOR), Observer Mode, Kafka Connect, Schema Registry&lt;/td&gt;
&lt;td&gt;Docker, Helm; hardcoded timeouts at scale; Kubernetes TLS friction; hard shutdown on license expiry&lt;/td&gt;
&lt;td&gt;Enterprise-only RBAC and SSO&lt;/td&gt;
&lt;td&gt;Modern, clean; React crashes on vanilla Kafka with some versions&lt;/td&gt;
&lt;td&gt;Free community tier; enterprise license required for RBAC/SSO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AKHQ / Kafbat&lt;/strong&gt; Lightweight self-hosted Kafka UI&lt;/td&gt;
&lt;td&gt;OSS (Apache 2.0)&lt;/td&gt;
&lt;td&gt;Topic management, consumer groups, Schema Registry, Kafka Connect&lt;/td&gt;
&lt;td&gt;Docker, Helm; low resource usage&lt;/td&gt;
&lt;td&gt;No built-in RBAC&lt;/td&gt;
&lt;td&gt;Functional; less polished than Console&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lenses / AxonOps&lt;/strong&gt; Teams needing full Kafka observability with governance&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Governance, data masking, audit trails, metrics&lt;/td&gt;
&lt;td&gt;SaaS and self-hosted&lt;/td&gt;
&lt;td&gt;Enterprise RBAC and SSO&lt;/td&gt;
&lt;td&gt;Feature-rich; steeper learning curve&lt;/td&gt;
&lt;td&gt;Commercial; higher price point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kpow (Factor House)&lt;/strong&gt; Production Kafka management with enterprise RBAC&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;Topic management, consumer groups, Schema Registry, Kafka Connect, multi-cluster, audit logging&lt;/td&gt;
&lt;td&gt;Self-hosted; stateless; straightforward deployment&lt;/td&gt;
&lt;td&gt;Advanced RBAC, SSO, data masking&lt;/td&gt;
&lt;td&gt;WCAG-compliant; high-performance at scale&lt;/td&gt;
&lt;td&gt;Per-cluster pricing that does not penalise team growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confluent Control Center&lt;/strong&gt; Confluent Platform users&lt;/td&gt;
&lt;td&gt;Commercial (bundled)&lt;/td&gt;
&lt;td&gt;Deep Confluent Platform integration, KSQL, metrics, replication&lt;/td&gt;
&lt;td&gt;Bundled with Confluent Platform&lt;/td&gt;
&lt;td&gt;Enterprise RBAC&lt;/td&gt;
&lt;td&gt;Dated UX by comparison&lt;/td&gt;
&lt;td&gt;Included with Confluent Platform license&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a broader comparison of Kafka UI tools in 2026, see &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Top Kafka UI tools in 2026: a practical comparison for engineering teams&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-about-redpanda-console&quot;&gt;Frequently asked questions about Redpanda Console&lt;/h2&gt;
&lt;h3 id=&quot;how-much-does-redpanda-console-cost-and-is-there-a-free-tier&quot;&gt;How much does Redpanda Console cost, and is there a free tier?&lt;/h3&gt;
&lt;p&gt;The community edition is free under the BSL for internal use. RBAC, SSO, and data masking require a paid Redpanda Enterprise license. Pricing for the enterprise tier is not published publicly.&lt;/p&gt;
&lt;h3 id=&quot;when-is-redpanda-console-a-better-choice-than-the-alternatives&quot;&gt;When is Redpanda Console a better choice than the alternatives?&lt;/h3&gt;
&lt;p&gt;It is the strongest free option for single-cluster Kafka or Redpanda setups where the primary need is message inspection and developer debugging. For Redpanda users specifically, native admin API integration adds capabilities unavailable elsewhere without cost. The Observer Mode and multi-format deserialization pipeline — including Protobuf without a schema registry — are capabilities that few free tools match.&lt;/p&gt;
&lt;h3 id=&quot;when-are-the-alternatives-a-better-choice-than-redpanda-console&quot;&gt;When are the alternatives a better choice than Redpanda Console?&lt;/h3&gt;
&lt;p&gt;When you manage more than one cluster, need RBAC or SSO without a Redpanda license, require built-in metrics and alerting, run large AWS MSK clusters with IAM authentication where Console’s hardcoded timeouts cause regular failures, or want a native desktop client for lightweight local inspection without running Docker.&lt;/p&gt;
&lt;h3 id=&quot;does-redpanda-console-work-with-vanilla-apache-kafka&quot;&gt;Does Redpanda Console work with vanilla Apache Kafka?&lt;/h3&gt;
&lt;p&gt;Yes, but with limitations. Some features rely on the Redpanda admin API and fail silently or throw errors on vanilla Kafka. The help shortcut (&lt;code&gt;?&lt;/code&gt;) causes a full React crash on certain Console versions when connected to a non-Redpanda cluster. Practitioners confirm the tool works well with standard Apache Kafka for message browsing and consumer group management, but the gaps become more visible as teams move beyond basic inspection.&lt;/p&gt;
&lt;h3 id=&quot;is-redpanda-console-open-source&quot;&gt;Is Redpanda Console open source?&lt;/h3&gt;
&lt;p&gt;It is source-available under the BSL. Internal use is free. Commercial SaaS deployments require a commercial agreement. The original maintainer (weeco of CloudHut) confirmed after the 2022 acquisition that “99% of all users can still use it” under the BSL. The licensing transition has nonetheless driven a portion of the original Kowl community toward fully open-source alternatives.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Top Kafka UI Tools in 2026: A Practical Comparison for Engineering Teams</title><link>https://factorhouse.io/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams/</link><guid isPermaLink="true">https://factorhouse.io/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams/</guid><description>Honest comparison of Kafka UI tools for enterprise teams. We evaluate AKHQ, Kafbat, Redpanda Console, Conduktor, Confluent Control Center, and Kpow.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Managing Apache Kafka through the command line made sense when clusters were small and teams were smaller. That era is over. Modern Kafka deployments span multiple clusters, process millions of messages per second, and serve dozens of teams who need visibility into topics they don’t own. The CLI simply cannot provide the observability, governance, and operational efficiency that production environments demand.&lt;/p&gt;
&lt;p&gt;This guide evaluates the leading Kafka UI tools against the criteria that actually matter for enterprise data engineering teams. We cover the commercial platforms, the vendor-specific options, and the open-source alternatives, with an honest assessment of where each excels and where each falls short.&lt;/p&gt;
&lt;h2 id=&quot;what-to-look-for-in-a-kafka-ui&quot;&gt;What to Look for in a Kafka UI&lt;/h2&gt;
&lt;p&gt;Before diving into specific tools, it’s worth establishing what separates a production-grade Kafka UI from a basic message viewer. The gap between these categories has widened significantly as Kafka has moved from simple pub/sub to the central nervous system of enterprise data architectures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka distribution support&lt;/strong&gt; matters more than most teams initially realise. Your UI needs to work with your specific flavour of Kafka, whether that’s vanilla Apache Kafka, AWS MSK with IAM authentication, Confluent Cloud, Redpanda, or Aiven. A tool that works beautifully with self-managed Kafka but can’t authenticate against MSK IAM is useless for half of modern deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Governance and security&lt;/strong&gt; have become non-negotiable. SOC 2, HIPAA, GDPR, and internal compliance frameworks require granular access controls, audit trails, and data masking. A Kafka UI is effectively a window into your organisation’s data, and treating security as an afterthought is increasingly untenable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-cluster management&lt;/strong&gt; separates enterprise tools from development toys. Most organisations run separate clusters for development, staging, and production, often across multiple cloud providers. Switching between browser tabs or reconfiguring connections is not a sustainable workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operational architecture&lt;/strong&gt; determines your total cost of ownership. Does the tool require an external PostgreSQL database? Does it need gigabytes of heap memory? Can it run in air-gapped environments? These questions matter when your SRE team is already stretched thin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serialisation support&lt;/strong&gt; is where many tools quietly fail. Kafka stores bytes; the intelligence is in the serialisation layer. Your UI needs to handle Avro, Protobuf, JSON Schema, and ideally custom serialisers for AWS Glue or proprietary formats. A tool that chokes on nested schemas or schema drift is useless in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Streaming ecosystem breadth&lt;/strong&gt; is increasingly relevant as data platforms expand beyond Kafka. Teams running Kafka Streams, Kafka Connect, ksqlDB, or Apache Flink need tooling that provides visibility across their entire streaming infrastructure, not just the broker layer.&lt;/p&gt;
&lt;h2 id=&quot;the-tools-compared&quot;&gt;The Tools Compared&lt;/h2&gt;
&lt;h3 id=&quot;kpow-enterprise-governance-without-the-infrastructure-tax&quot;&gt;Kpow: Enterprise Governance Without the Infrastructure Tax&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722460e8a935845facc77_kpow-blog-screenshot.avif&quot; alt=&quot;Kpow Kafka UI&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow&lt;/a&gt; was purpose-built for a problem most Kafka UIs ignore: delivering enterprise-grade governance and observability without introducing operational complexity that rivals the clusters you’re trying to manage.&lt;/p&gt;
&lt;p&gt;The answer is architectural. Kpow is fully stateless. It stores all state in internal Kafka topics using Kafka Streams, which means it deploys as a single container with zero external dependencies. No PostgreSQL to maintain. No separate data store to back up, patch, and monitor. No data ever leaves your network control. For regulated industries, this simplicity translates directly into faster procurement and fewer moving parts for your security team to evaluate.&lt;/p&gt;
&lt;p&gt;This architectural simplicity belies serious depth. Kpow’s Data Policies provide server-side masking of sensitive fields based on key names or patterns, ensuring PII never reaches the browser. This satisfies PCI-DSS and HIPAA requirements without the complexity of a proxy layer sitting in front of your brokers. Comprehensive audit logging captures every action: who viewed what data, who reset which offset, who changed which configuration, with optional Slack integration for ChatOps transparency.&lt;/p&gt;
&lt;p&gt;Where Kpow genuinely pulls ahead of every other tool on this list is Kafka distribution compatibility. It offers &lt;strong&gt;native&lt;/strong&gt; AWS MSK IAM authentication (not a workaround via SASL, but actual IAM integration), Confluent Cloud, Redpanda, Aiven, and AWS Glue Schema Registry support, all from a single deployment. If you’re running a multi-cloud or hybrid Kafka environment, and increasingly most enterprises are, Kpow provides a unified view across every cluster and every distribution without per-instance configuration.&lt;/p&gt;
&lt;p&gt;Kpow’s kJQ filtering deserves specific attention. It provides JQ-based predicates for searching deeply nested data structures server-side, which engineers at organisations like Binance and Cash App have cited as critical for reducing incident resolution time. When you’re debugging a production issue at 2am, the ability to write precise queries against complex message payloads without writing disposable consumer code is a material operational advantage.&lt;/p&gt;
&lt;p&gt;Teams can get started with a &lt;a href=&quot;#try-kpow&quot;&gt;30-day free trial of Kpow&lt;/a&gt;. There is transparent per-cluster pricing rather than per-user models that penalise growing teams. A free &lt;a href=&quot;#try-community&quot;&gt;Community Edition&lt;/a&gt; is available for local testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Any team where Kafka is production infrastructure that must be governed, audited, and operated reliably, particularly in regulated industries, multi-cluster environments, or organisations where operational simplicity and vendor independence are strategic priorities.&lt;/p&gt;
&lt;h3 id=&quot;akhq-capable-open-source-but-mind-the-governance-gaps&quot;&gt;AKHQ: Capable Open-Source, but Mind the Governance Gaps&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7225fffb866d414dd12b8_akhq-blog-screenshot.avif&quot; alt=&quot;AKHQ&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/akhq&quot;&gt;AKHQ&lt;/a&gt; (formerly KafkaHQ) is the most established open-source Kafka UI option. It’s built on Micronaut and designed for configuration-as-code deployments.&lt;/p&gt;
&lt;p&gt;AKHQ’s strength is its GitOps-native architecture. Connections, users, groups, and schema registry links can all be defined in YAML, making it straightforward to deploy consistently across environments using Helm charts. It supports LDAP, OAuth2/OIDC, and GitHub SSO, with regex-based topic filtering for access control.&lt;/p&gt;
&lt;p&gt;The limitations become apparent at enterprise scale. AKHQ has &lt;strong&gt;no native data masking&lt;/strong&gt;, which is a significant compliance gap for any team handling PII or operating under HIPAA, PCI-DSS, or GDPR requirements. Audit logging is limited to whatever your authentication provider captures, rather than comprehensive activity tracking within the tool itself. The UI has also received persistent criticism for responsiveness issues under load, which the maintainer has acknowledged but not fully resolved. For development and staging environments these gaps may be acceptable; for production governance, they leave real exposure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Mid-size organisations with strong DevOps cultures who need proven open-source tooling and can accept the governance limitations, or as a complement to a commercial tool for non-production environments.&lt;/p&gt;
&lt;h3 id=&quot;kafka-ui-kafbat-fork-modern-but-fragile&quot;&gt;Kafka UI (Kafbat fork): Modern but Fragile&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c72279c93efb0be1f59126_kafbat-blog-screenshot.avif&quot; alt=&quot;Kafbat&quot;&gt;&lt;/p&gt;
&lt;p&gt;A critical warning first: if you’re still running the original &lt;code&gt;provectuslabs/kafka-ui&lt;/code&gt; Docker image, you’re running abandoned software with known security vulnerabilities. The project was effectively unmaintained from late 2023, with a remote code execution vulnerability (CVE-2023-52251) taking six months to patch. The core maintainers forked the project to &lt;code&gt;kafbat/kafka-ui&lt;/code&gt;, which is where active development continues.&lt;/p&gt;
&lt;p&gt;The Kafbat fork offers the most approachable open-source interface, with multi-cluster management, Kafka Connect integration, and Avro/Protobuf/JSON support. It includes basic RBAC via YAML configuration and data masking with regex support.&lt;/p&gt;
&lt;p&gt;The fundamental trade-off is sustainability. This is a community-maintained fork of an abandoned project. There’s no vendor to call during an incident, no SLA, and long-term development direction depends entirely on volunteer contributor interest. The project inherited a significant bug backlog from Provectus, and while the Kafbat team has been responsive, the provectus/kafka-ui abandonment is a cautionary tale about relying on open-source projects without commercial backing for production infrastructure tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Startups and development environments where budget is the primary constraint and the team has capacity to manage configuration, maintenance, and the risk of project abandonment.&lt;/p&gt;
&lt;h3 id=&quot;redpanda-consolefast-viewer-locked-governance&quot;&gt;Redpanda Console: Fast Viewer, Locked Governance&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c7228f5010387c137d9bf0_redpanda-console-blog-screenshot.avif&quot; alt=&quot;Redpanda Console&quot;&gt;&lt;/p&gt;
&lt;p&gt;Originally built as Kowl by CloudHut before Redpanda’s acquisition, &lt;a href=&quot;/articles/redpanda-console&quot;&gt;Redpanda Console&lt;/a&gt; performs well as a message viewer. Written in Go, it delivers minimal memory footprint and near-instant startup, which is a meaningful advantage for developers running local stacks. Its automatic deserialisation heuristics for Protobuf, Avro, MessagePack, and JSON are solid.&lt;/p&gt;
&lt;p&gt;The catch is the licensing model. The core viewer is free under a Business Source License, but every enterprise feature that matters (SSO, RBAC, data masking) requires a paid &lt;strong&gt;Redpanda Enterprise&lt;/strong&gt; license. This creates a problematic dynamic for vanilla Apache Kafka or MSK users: you can use the viewer for free, but the moment you need governance, you’re paying for a Redpanda license even if you don’t run Redpanda. Without those enterprise features, it’s a browser for messages, not an operational tool.&lt;/p&gt;
&lt;p&gt;Multi-cluster support is also limited compared to dedicated multi-cluster solutions, and MSK IAM authentication requires SASL workarounds rather than native integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Local development and debugging where you need a fast, lightweight message viewer. Not a realistic option for production governance unless you’re already a Redpanda customer.&lt;/p&gt;
&lt;h3 id=&quot;confluent-control-center-powerful-but-captive&quot;&gt;Confluent Control Center: Powerful but Captive&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722a00e8a935845fad116_confluent-control-center-blog-screenshot.avif&quot; alt=&quot;Confluent Control Center&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/confluent-control-center&quot;&gt;Confluent Control Center&lt;/a&gt; provides the deepest integration for Confluent Platform users. Kafka Streams topology visualisation, ksqlDB development, and Replicator monitoring are tightly coupled to the Confluent ecosystem in ways that third-party tools have not replicated. If you’re fully committed to the Confluent ecosystem, it provides native observability across the platform.&lt;/p&gt;
&lt;p&gt;The critical limitation is that commitment must be total. Control Center requires the Confluent Metrics Reporter JAR installed in broker classpaths and effectively mandates the Confluent ecosystem for full functionality. It cannot work with AWS MSK’s native IAM authentication, immediately disqualifying it for the growing number of organisations using MSK. It also doesn’t support Redpanda or Aiven deployments.&lt;/p&gt;
&lt;p&gt;The resource footprint is substantial, with some deployments requiring as much compute as the Kafka brokers themselves. And because Control Center is bundled with Confluent Platform licensing, you’re not evaluating the UI in isolation; you’re evaluating an entire ecosystem commitment.&lt;/p&gt;
&lt;p&gt;Best for: Organisations already fully committed to Confluent Platform who specifically need Kafka Streams and ksqlDB visualisation. Not viable, and not intended, for vanilla Apache Kafka, AWS MSK, or multi-vendor environments.&lt;/p&gt;
&lt;h3 id=&quot;conduktorbroad-feature-set-operationally-heavy&quot;&gt;Conduktor: Broad Feature Set, Operationally Heavy&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722b1f17be485095adbee_conduktor-blog-screenshot.avif&quot; alt=&quot;Conduktor&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/conduktor&quot;&gt;Conduktor&lt;/a&gt; has evolved from a desktop application into a web platform targeting enterprise data quality and governance. Its headline feature is the Gateway proxy architecture, a Kafka proxy layer that enables field-level encryption, data masking, and policy enforcement at the wire level without modifying producer applications.&lt;br&gt;
‍&lt;br&gt;
The platform offers RBAC with wildcard patterns, compliance-ready audit logging with SIEM integration, and data quality validation rules that catch schema violations before bad data pollutes topics.&lt;br&gt;
‍&lt;br&gt;
The operational cost is significant and should be evaluated carefully. Conduktor Console requires an external PostgreSQL database, adding another stateful dependency to provision, back up, patch, monitor, and secure. This stands in stark contrast to stateless architectures that deploy as a single container. The licensing model includes per-user pricing and tiered feature access, which means the cost scales with team size in ways that per-cluster pricing does not. For large platform teams, this distinction can be material.&lt;br&gt;
‍&lt;br&gt;
Best for: Enterprises that specifically need wire-level proxy capabilities for encryption or policy enforcement, and have the operational capacity to manage the additional infrastructure dependencies.&lt;/p&gt;
&lt;h3 id=&quot;lensesio-ambitious-scope-mixed-execution&quot;&gt;Lenses.io: Ambitious Scope, Mixed Execution&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c722c109a967e2ee935e20_lenses-blog-screenshot.avif&quot; alt=&quot;Lenses&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/lenses&quot;&gt;Lenses&lt;/a&gt; positions itself as a DataOps platform rather than a pure Kafka UI, with a proprietary SQL engine that lets users query and transform streaming data using SQL syntax. The SQL interface may appeal to business analyst roles or less technical team members who need access to Kafka data without writing Java or Scala consumer code. SQL Processors allow deploying continuous transformations to Kubernetes, though this introduces a proprietary abstraction layer over your streaming infrastructure.&lt;/p&gt;
&lt;p&gt;The data catalog and lineage tracking provide searchable topic discovery and data flow visualisation across multi-cluster environments.&lt;/p&gt;
&lt;p&gt;However, Lenses occupies a different price point that reflects its broader DataOps positioning, which may be difficult to justify if your primary need is Kafka observability and governance. Some users report deployment complexity, particularly in air-gapped environments. The SQL abstraction introduces a proprietary layer that creates its own form of lock-in. And recent user feedback on G2 and community forums has flagged concerns about product roadmap velocity and unresolved bugs across releases, which are signals worth monitoring when evaluating long-term investment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Organisations where SQL-based streaming data access is a primary requirement, particularly for business analysts or less technical roles who need self-service Kafka access. Evaluate carefully if your core need is operational governance rather than data exploration.&lt;/p&gt;
&lt;h2 id=&quot;platform-compatibility-matrix&quot;&gt;Platform Compatibility Matrix&lt;/h2&gt;
&lt;p&gt;Enterprise deployments typically span multiple Kafka flavours. This compatibility matrix reflects verified documentation as of early 2026:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;AWS MSK (IAM)&lt;/th&gt;
&lt;th&gt;Confluent Cloud&lt;/th&gt;
&lt;th&gt;Redpanda&lt;/th&gt;
&lt;th&gt;Self-Managed&lt;/th&gt;
&lt;th&gt;Multi-Cluster&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kpow&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Unified view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conduktor&lt;/td&gt;
&lt;td&gt;Deep integration&lt;/td&gt;
&lt;td&gt;Deep integration&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Per-instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lenses&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Global catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;IAM Auth&lt;/td&gt;
&lt;td&gt;Via SASL&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka UI (Kafbat)&lt;/td&gt;
&lt;td&gt;Glue SerDe&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redpanda Console&lt;/td&gt;
&lt;td&gt;Via SASL&lt;/td&gt;
&lt;td&gt;Via SASL&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent CC&lt;/td&gt;
&lt;td&gt;No native support&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Requires components&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The absence of native AWS MSK IAM support in Confluent Control Center is a significant limitation for the growing number of organisations using MSK as their primary Kafka deployment.&lt;/p&gt;
&lt;p&gt;Kpow is the only tool on this list that provides native integration with every major Kafka distribution, including AWS MSK IAM, Confluent Cloud, Redpanda, Aiven, and AWS Glue Schema Registry, from a single, stateless deployment. For organisations running heterogeneous Kafka environments, this eliminates the need for distribution-specific workarounds or multiple tool instances.&lt;/p&gt;
&lt;h2 id=&quot;why-we-built-kpow&quot;&gt;Why We Built Kpow&lt;/h2&gt;
&lt;p&gt;Factor House builds tooling for streaming data platforms. We started with Kpow because we saw a clear gap: existing Kafka UIs either required complex infrastructure to deliver governance, or treated governance as an afterthought to keep things simple. Our thesis was that these shouldn’t be mutually exclusive, and the adoption by teams at organisations like Binance, Cash App, and &lt;a href=&quot;/case-studies/nord-lb&quot;&gt;NORD/LB&lt;/a&gt; has validated that approach.&lt;/p&gt;
&lt;p&gt;The streaming ecosystem is expanding beyond Kafka. Teams now run Kafka alongside Kafka Connect, Kafka Streams, and increasingly Apache Flink. We believe tooling should evolve with this reality rather than remaining siloed. Factor House’s roadmap extends Kpow’s operational model (stateless deployment, transparent pricing, compliance-first design) across the streaming stack with products like &lt;a href=&quot;https://factorhouse.io/products/flex&quot;&gt;Flex for Apache Flink&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/products/factor-platform&quot;&gt;Factor Platform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Kpow delivers is the deepest combination of Kafka governance, distribution compatibility, and operational simplicity available, without the infrastructure overhead, vendor lock-in, or per-user pricing that comes with the alternatives.&lt;/p&gt;
&lt;h2 id=&quot;choosing-the-right-tool-for-your-team&quot;&gt;Choosing the Right Tool for Your Team&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;For AWS MSK-primary environments:&lt;/strong&gt; Kpow provides native IAM authentication with zero workarounds, making it the cleanest MSK integration available. Conduktor also offers strong MSK support but requires PostgreSQL infrastructure. AKHQ is the best free alternative with MSK IAM support, though without data masking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For Confluent Platform shops:&lt;/strong&gt; Use Confluent Control Center if you specifically need Kafka Streams topology and ksqlDB visualisation. For broader observability, governance, and multi-cluster management, Kpow delivers comparable or better capabilities without the ecosystem lock-in or resource overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For multi-cloud or hybrid deployments:&lt;/strong&gt; Kpow is the clear choice, offering the only truly unified multi-cluster view across every major Kafka distribution with transparent per-cluster pricing. No other tool matches this breadth from a single deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For compliance-heavy enterprises:&lt;/strong&gt; Kpow’s stateless architecture means no external database storing sensitive metadata, server-side data masking ensures PII never reaches browsers, and comprehensive audit logging satisfies SOC 2 and HIPAA requirements. Conduktor’s Gateway adds wire-level encryption if you need proxy capabilities, but evaluate whether that complexity is justified for your use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For cost-conscious teams:&lt;/strong&gt; AKHQ offers the most proven free option with broad enterprise adoption. Kafbat provides a more modern UI but carries project sustainability risk. &lt;a href=&quot;https://factorhouse.io/pricing&quot;&gt;Kpow’s Community Edition&lt;/a&gt; is free for local testing with a single cluster and full features, which is worth evaluating before committing to open-source maintenance overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For development and testing:&lt;/strong&gt; &lt;a href=&quot;https://account.factorhouse.io/onboarding/new?license_type=16b07fcf-6bd7-42b5-bda0-ec3a7dfe46ed&quot;&gt;Kpow Community Edition&lt;/a&gt; is free for local testing with no feature limitations. Redpanda Console and Kafbat are also solid options for local development.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Kafka UI you choose has real consequences for your team’s operational efficiency, compliance posture, and long-term flexibility. The right decision depends on matching your tool to your actual constraints: regulatory requirements, Kafka distribution, deployment complexity, and team capacity.&lt;/p&gt;
&lt;p&gt;Open-source tools have matured. AKHQ and Kafbat are production-viable for many organisations, though both carry governance limitations that matter at enterprise scale. The provectus/kafka-ui abandonment is a useful reminder that project health matters as much as feature sets.&lt;/p&gt;
&lt;p&gt;For enterprises where governance isn’t optional, the commercial landscape offers clear trade-offs. Conduktor adds proxy capabilities at the cost of infrastructure complexity. Lenses provides SQL abstraction at a DataOps price point. Confluent Control Center delivers deep platform integration but demands total ecosystem commitment. Kpow provides the broadest Kafka distribution support, the simplest operational footprint, and the deepest compliance capabilities, without requiring external databases, vendor lock-in, or per-user pricing.&lt;/p&gt;
&lt;p&gt;If Kafka is critical infrastructure for your organisation, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;start with a free trial of Kpow&lt;/a&gt; and see the difference for yourself.&lt;/p&gt;
</content:encoded><category>Comparisons</category><author>Factor House</author></item><item><title>Kpow Community Edition 🚀</title><link>https://factorhouse.io/articles/kpow-community-edition/</link><guid isPermaLink="true">https://factorhouse.io/articles/kpow-community-edition/</guid><description>Kpow Community Edition is a free, developer focused toolkit for Apache Kafka clusters, schema registries, and connect installations.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Kpow Community Edition is a free, developer focused toolkit for Apache Kafka clusters, schema registries, and connect installations.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This may be the &lt;em&gt;worst-kept secret in Kafka-tooling land&lt;/em&gt; because thousands of engineers are already using Kpow Community Edition (CE).&lt;/p&gt;
&lt;p&gt;After a soft launch at &lt;a href=&quot;https://www.confluent.io/events/current-2022/&quot;&gt;&lt;strong&gt;Current ’22&lt;/strong&gt;&lt;/a&gt;, then a medium launch at &lt;a href=&quot;https://www.kafka-summit.org/kafka-summit-london-2023&quot;&gt;&lt;strong&gt;Kafka Summit ’23&lt;/strong&gt;&lt;/a&gt;, we’re making Kpow CE generally available to everyone, everywhere.&lt;/p&gt;
&lt;p&gt;Starting today, individuals can use Kpow CE for free, even at work. Organisations can install Kpow CE in up to three non-production environments.&lt;/p&gt;
&lt;p&gt;Each installation of Kpow CE can manage one Kafka Cluster, one Schema Registry, and one Connect cluster. See our &lt;a href=&quot;/products/kpow/features&quot;&gt;&lt;strong&gt;feature matrix&lt;/strong&gt;&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2 id=&quot;kpow-for-apache-kafka&quot;&gt;Kpow for Apache Kafka&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Apache Kafka is a transformative technology, every engineer should have access to tooling that makes Kafka a joy.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Built on our learnings from a decade of shipping systems with Kafka, we believe Kpow is the most powerful and intuitive Kafka Web UI available to engineers today.&lt;/p&gt;
&lt;p&gt;Designed for enterprise integration from our first commit in 2018, Kpow runs securely in your network with zero data egress and a full suite of user authentication and authorization features including SAML, LDAP, OpenID, OAuth2, Okta, Keycloak, RBAC, Multi-Tenancy, Data-Masking, Audit Logs, and more.&lt;/p&gt;
&lt;p&gt;Five years, 10k commits, and more than 800k Docker pulls later, Kpow now has tens of thousands of users in 100+ countries. Our focus on quality tooling sees Kpow stacked with features and our commitment to performance means that one instance of Kpow can manage up to a dozen Kafka clusters and associated resources.&lt;/p&gt;
&lt;p&gt;Explore the full feature-set of Kpow in our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;&lt;strong&gt;live, multi-cluster demo environment&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7832ec1566bfaadf32249_kpow-hero.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kpow-community-edition&quot;&gt;Kpow Community Edition&lt;/h2&gt;
&lt;p&gt;Kpow CE is the free, light-weight version of Kpow that is packed with features to accelerate your Kafka experience (with none of the enterprise-y stuff, you know the drill).&lt;/p&gt;
&lt;p&gt;If we have to give you one reason to install Kpow? Here it is..&lt;/p&gt;
&lt;h3 id=&quot;blazing-fast-multi-topic-search&quot;&gt;Blazing Fast Multi-Topic Search!&lt;/h3&gt;
&lt;p&gt;Searching for data in Kafka can feel like trying to find the needle in a haystack. Kpow CE makes topic search easy with built-in support for JSON Query (JQ) predicates, e.g:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.key.id &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; endswith&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;4d4a&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.value.trade.status &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;final&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.value.trade.price &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;double &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 25.45&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.value.partner.network &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;AMEX&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.headers.flag[&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;audit&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kpow’s multi-topic search easily scans tens of thousands of messages a second, finding the ones that match your JQ predicate in a flash.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Read more about Kpow’s implementation of JQ in our &lt;a href=&quot;https://docs.kpow.io/features/data-inspect/kjq-filters/&quot;&gt;&lt;strong&gt;kJQ Documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;monitor-control-and-explore&quot;&gt;Monitor, Control, and Explore&lt;/h2&gt;
&lt;p&gt;Kpow CE covers the full surface area of Apache Kafka, from creating topics and resetting consumer group offsets to editing schema and restarting connectors.&lt;/p&gt;
&lt;p&gt;Watch the Devoxx UK conference talk &lt;a href=&quot;https://www.youtube.com/watch?v=_ltBpVTo8rw&quot;&gt;&lt;strong&gt;‘Tune-Up your Kafka Tooling with Kpow Community’&lt;/strong&gt;&lt;/a&gt; and discover how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up Kpow CE with a Kafka Cluster, Schema Registry, and Kafka Connect. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=181&quot;&gt;&lt;strong&gt;[3:00]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Skip the setup wizard and run Kpow CE with environment variables. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=259&quot;&gt;&lt;strong&gt;[4:19]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Learn how Kpow generates unique metrics and insights while running airgapped and secure. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=405&quot;&gt;&lt;strong&gt;[6:45]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Diagnose the root cause of production lag issues with multi-dimensional consumer metrics. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=687&quot;&gt;&lt;strong&gt;[11:30]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Skip poison-pill messages and recompute topics with consumer offset management. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=809&quot;&gt;&lt;strong&gt;[13:30]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Manage and monitor Kafka Connect clusters and schema registries. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=921&quot;&gt;&lt;strong&gt;[15:22]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Find needle-in-a-haystack messages with blazing-fast multi-topic search and built in JQ predicates. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=1006&quot;&gt;&lt;strong&gt;[16:46]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blend Clojure and JQ to make quick assertions about topic data with the kREPL. &lt;a href=&quot;https://youtu.be/_ltBpVTo8rw?t=1338&quot;&gt;&lt;strong&gt;[22:18]&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tune-up-your-kafka-tooling&quot;&gt;Tune-Up your Kafka Tooling!&lt;/h2&gt;
&lt;p&gt;Kpow CE works with any Kafka cluster v1.0.0+ including Confluent Cloud &amp;amp; Platform, AWS MSK, MSK Serverless, Redpanda, and clusters from providers like Aiven and Instaclustr.&lt;/p&gt;
&lt;p&gt;Supported Kafka resource integrations include Confluent Schema Registry, AWS Glue, Apache Kafka Connect, Confluent Managed Connect, and MSK Connect.&lt;/p&gt;
&lt;p&gt;Get started in minutes with the &lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow-ce&quot;&gt;&lt;strong&gt;Kpow CE Docker container&lt;/strong&gt;&lt;/a&gt;, our multi-arch build provides support for both ARM and x86.&lt;/p&gt;
&lt;p&gt;Configure and launch Kpow CE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access Kpow CE setup wizard on &lt;a href=&quot;http://localhost:3000/&quot;&gt;&lt;strong&gt;http://localhost:3000&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Follow the steps to get an &lt;a href=&quot;https://factorhouse.io/kpow/community/#individual&quot;&gt;&lt;strong&gt;individual&lt;/strong&gt;&lt;/a&gt; or &lt;a href=&quot;https://factorhouse.io/kpow/community/#organization&quot;&gt;&lt;strong&gt;organization&lt;/strong&gt;&lt;/a&gt; license.&lt;/li&gt;
&lt;li&gt;Configure your Kafka Cluster, Schema Registry, Connect Cluster.&lt;/li&gt;
&lt;li&gt;Launch Kpow CE!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the &lt;a href=&quot;https://docs.kpow.io/ce/configuration/&quot;&gt;&lt;strong&gt;configuration guide&lt;/strong&gt;&lt;/a&gt; to learn how to configure Kpow CE with environment variables instead of using the setup wizard.&lt;/p&gt;
&lt;p&gt;Running in Kubernetes? Use our &lt;a href=&quot;https://github.com/factorhouse/kpow-helm-charts&quot;&gt;&lt;strong&gt;Helm charts&lt;/strong&gt;&lt;/a&gt; to deploy Kpow CE to your cluster.&lt;/p&gt;
&lt;p&gt;We hope you find Kpow Community Edition useful. If you encounter any problems or techincal questions just raise an issue on the &lt;a href=&quot;https://github.com/factorhouse/kpow/issues&quot;&gt;&lt;strong&gt;Kpow Github repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Interested in a commercial license? &lt;a href=&quot;/contact&quot;&gt;Contact us&lt;/a&gt; any time to discuss requirements and start a POC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Derek Troy-West is a Co-Founder and CEO of Factor House.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/&quot;&gt;&lt;strong&gt;Factor House&lt;/strong&gt;&lt;/a&gt; build essential tools for modern engineers.&lt;/p&gt;
</content:encoded><category>Product</category><author>Derek Troy-West</author></item><item><title>Best practices for Kafka data observability</title><link>https://factorhouse.io/articles/best-practices-kafka-data-observability/</link><guid isPermaLink="true">https://factorhouse.io/articles/best-practices-kafka-data-observability/</guid><description>12 best practices for Kafka data observability covering consumer lag monitoring, schema enforcement, end-to-end auditing, DLQs, and lineage, with an implementation roadmap.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kafka data observability is distinct from broker monitoring. Broker metrics tell you the cluster is healthy; data observability tells you whether the data flowing through it is correct, fresh, complete, and structurally sound.&lt;/li&gt;
&lt;li&gt;Consumer lag is your north-star metric, but raw offset thresholds are an unreliable way to measure it. Trend-based evaluation per consumer group, per partition, is more accurate.&lt;/li&gt;
&lt;li&gt;Schema Registry with enforced compatibility in CI is non-negotiable above a handful of services. Schema drift causes silent consumer failures that broker dashboards will never surface.&lt;/li&gt;
&lt;li&gt;End-to-end auditing, counting messages at every tier from producer to consumer, catches data loss that infrastructure monitoring alone cannot detect.&lt;/li&gt;
&lt;li&gt;Tools like &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; consolidate broker health, consumer lag, schema management, and Kafka Streams visibility into a single interface, reducing the operational overhead of maintaining separate observability tooling.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Kafka’s operational surface area has two distinct layers. The first is the cluster itself: broker health, partition replication, JVM pressure, disk utilisation. The second is the data: whether messages are arriving on time, whether the schema is what consumers expect, whether any messages were lost in transit.&lt;/p&gt;
&lt;p&gt;Most teams instrument the first layer thoroughly and underinvest in the second. The consequence is a pattern that practitioners have converged on calling “green dashboards, broken data.” The cluster looks healthy. CPU is normal. ISR is stable. And yet, somewhere downstream, a consumer is silently deserialising nulls because a field was renamed three hours ago.&lt;/p&gt;
&lt;p&gt;PagerDuty’s August 2025 outage is the most thoroughly documented recent example of this failure mode. Brokers looked fine throughout the incident, but a producer-instance leak in a &lt;code&gt;pekko-connectors-kafka&lt;/code&gt; integration caused approximately 4.2 million new producers to register per hour, roughly 84 times the normal rate. Brokers exhausted JVM heap tracking producer metadata, the cluster cascaded, and approximately 95% of incoming events were rejected over 38 minutes. PagerDuty’s own post-mortem explicitly names two causes: “previously minimal alerting on Kafka” and an “observability gap in Kafka producer and consumer telemetry including anomaly detection for unexpected workloads.”&lt;/p&gt;
&lt;p&gt;Infrastructure monitoring alone could not have caught it. This guide covers the practices that close that gap.&lt;/p&gt;
&lt;h2 id=&quot;what-kafka-data-observability-actually-means&quot;&gt;What Kafka data observability actually means&lt;/h2&gt;
&lt;p&gt;Data observability, as applied to Kafka, asks five questions about data in motion:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Kafka-specific signals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Freshness&lt;/td&gt;
&lt;td&gt;Is data arriving on time?&lt;/td&gt;
&lt;td&gt;Consumer lag per group per partition, end-to-end latency from event-time to consume-time, watermark delay in Flink/Kafka Streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume&lt;/td&gt;
&lt;td&gt;Is the expected amount arriving?&lt;/td&gt;
&lt;td&gt;MessagesInPerSec per topic, producer record counts, audit-tier message counts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;td&gt;Has the structure changed unexpectedly?&lt;/td&gt;
&lt;td&gt;Schema Registry version count, compatibility-check failure rate, DLQ rate from deserialization errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution&lt;/td&gt;
&lt;td&gt;Are field values within expected ranges?&lt;/td&gt;
&lt;td&gt;Field-level rules in data contracts, null-rate spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lineage&lt;/td&gt;
&lt;td&gt;Where did this data come from, and where is it going?&lt;/td&gt;
&lt;td&gt;Producer-to-topic-to-consumer graph, Kafka Connect transform-aware lineage, OpenLineage events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;The distinction from cluster monitoring is important. Monitoring tells you consumer lag spiked at 3:15 PM. Observability tells you consumer lag spiked at 3:15 PM, correlating with schema version 47 deployed at 3:12 PM, which introduced a backward-incompatible field removal causing consumer deserialization failures. The shift is from cluster-centric &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;JMX metrics&lt;/a&gt; to data-centric signals.&lt;/p&gt;
&lt;h2 id=&quot;12-best-practices-for-kafka-data-observability&quot;&gt;12 best practices for Kafka data observability&lt;/h2&gt;
&lt;h3 id=&quot;1-use-trend-based-consumer-lag-monitoring-not-threshold-alerts&quot;&gt;1. Use trend-based consumer lag monitoring, not threshold alerts&lt;/h3&gt;
&lt;p&gt;Raw offset thresholds are a losing proposition for consumer lag alerting. A traffic spike will trip a fixed threshold even when the consumer is keeping up. Aggregating lag across partitions hides per-partition stalls, meaning one stuck partition out of ten will disappear into the average.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn’s&lt;/a&gt; SRE team built Burrow specifically to address this. Rather than alerting on absolute lag values, Burrow evaluates each consumer group over a sliding window of committed offsets and classifies it as &lt;code&gt;OK&lt;/code&gt;, &lt;code&gt;WARN&lt;/code&gt;, or &lt;code&gt;ERROR&lt;/code&gt; based on lag trend. Uber extended the same idea with uGroup, which decodes the &lt;code&gt;__consumer_offsets&lt;/code&gt; topic directly because consumer-side metrics cannot fully account for all group activity, particularly when consumers are experiencing problems.&lt;/p&gt;
&lt;p&gt;The practical implementation is to scrape Burrow’s REST API into Prometheus and alert on &lt;code&gt;ERROR&lt;/code&gt; state persisting for five or more minutes. Conduktor reported going from 47 lag alerts per month (2 real) to 3 alerts per month (all real) after switching from threshold-based to rate-of-change alerting.&lt;/p&gt;
&lt;p&gt;For more on this topic, see our &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;consumer lag monitoring guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;2-track-consumer-lag-as-both-a-count-and-a-time&quot;&gt;2. Track consumer lag as both a count and a time&lt;/h3&gt;
&lt;p&gt;Offset lag and time lag measure different things. “A consumer is 50,000 messages behind” depends entirely on the traffic shape of that topic. “A fraud-detection consumer is 30 seconds behind” is immediately actionable.&lt;/p&gt;
&lt;p&gt;Most major platforms expose both. Datadog Data Streams Monitoring, the Confluent Metrics API, and AWS MSK CloudWatch all provide both &lt;code&gt;OffsetLag&lt;/code&gt; and &lt;code&gt;EstimatedTimeLag&lt;/code&gt;. Time-based lag is the SLO metric that matters to the business. Define your SLOs in time units, alert in time units.&lt;/p&gt;
&lt;p&gt;A useful PromQL pattern for lag alerting that accounts for both lag and lack of progress:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max by(group, topic)(kafka_consumer_group_partition_lag) &amp;gt; 10000   and   rate(kafka_consumer_fetch_manager_records_consumed_total[5m]) == 0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This fires on high lag combined with zero consumption progress, which filters out deployment blips and traffic spikes where the consumer is genuinely keeping up.&lt;/p&gt;
&lt;h3 id=&quot;3-enforce-schema-compatibility-in-ci-before-any-producer-deploys&quot;&gt;3. Enforce schema compatibility in CI before any producer deploys&lt;/h3&gt;
&lt;p&gt;Above roughly five services producing to Kafka, Schema Registry with enforced compatibility transitions from a nice-to-have to a requirement. The failure mode without it is well-documented: a producer deploys a new schema version that removes a field, consumers expect that field and crash when it is missing, messages accumulate unprocessed, lag grows into the millions, and alerts fire hours after the schema change that caused it.&lt;/p&gt;
&lt;p&gt;The defaults that most practitioners converge on: &lt;code&gt;BACKWARD&lt;/code&gt; for Avro and JSON Schema, &lt;code&gt;BACKWARD_TRANSITIVE&lt;/code&gt; for Protobuf. The distinction matters for Protobuf because adding new message types is not forward-compatible, so transitive checking across all previous versions is required.&lt;/p&gt;
&lt;p&gt;The enforcement point is at least as important as the compatibility mode. Community Schema Registry enforces only in the producer SDK, which any client speaking the Kafka wire protocol directly can bypass. Gate schema changes in CI with &lt;code&gt;mvn schema-registry:test-compatibility&lt;/code&gt; or the Gradle equivalent, and fail the build on incompatibility. Disable &lt;code&gt;auto.register.schemas&lt;/code&gt; in production so rogue producers cannot silently introduce new schemas.&lt;/p&gt;
&lt;p&gt;One important operational note: Confluent Schema Registry defaults to &lt;code&gt;BACKWARD&lt;/code&gt;, but AWS Glue Schema Registry defaults to &lt;code&gt;DISABLED&lt;/code&gt; and Apicurio defaults to &lt;code&gt;NONE&lt;/code&gt;. Always verify and set compatibility modes explicitly rather than relying on defaults.&lt;/p&gt;
&lt;h3 id=&quot;4-go-beyond-schema-validation-with-data-contracts&quot;&gt;4. Go beyond schema validation with data contracts&lt;/h3&gt;
&lt;p&gt;Schema compatibility validates structure. Data contracts extend that to semantics and SLAs.&lt;/p&gt;
&lt;p&gt;The Open Data Contract Standard (ODCS), originally open-sourced by PayPal and now maintained under the Bitol project, extends schemas with data-quality rules, SLAs, security classifications, and ownership metadata. Confluent’s Data Contracts feature implements field-level validation using Common Expression Language (CEL) rules at serialization time, including validators like &lt;code&gt;isEmail&lt;/code&gt; and &lt;code&gt;isUuid&lt;/code&gt;, field transformations, and DLQ routing on rule failure.&lt;/p&gt;
&lt;p&gt;The boundary on enforcement is worth understanding clearly. Community Schema Registry validates client-side only. Anything that speaks the Kafka wire protocol directly sidesteps the entire enforcement chain. Broker-side validation is available as a paid Confluent feature; Redpanda’s Wasm Data Transforms provide broker-side validation in open source. If you cannot use broker-side enforcement, supplement contract validation with audit-tier message counting (see practice 7).&lt;/p&gt;
&lt;h3 id=&quot;5-implement-dead-letter-queues-with-rich-metadata-and-active-monitoring&quot;&gt;5. Implement dead letter queues with rich metadata and active monitoring&lt;/h3&gt;
&lt;p&gt;A dead letter queue with no observability is a silent failure sink. Messages arrive, nobody sees them, and what should have been a detectable error becomes an invisible data hole.&lt;/p&gt;
&lt;p&gt;The pattern that most teams have converged on, originally published by &lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber&lt;/a&gt; Insurance Engineering: a tiered retry structure with a main topic, a series of count-based retry topics with exponential backoff (&lt;code&gt;topic.retry.1&lt;/code&gt; at one minute, &lt;code&gt;topic.retry.2&lt;/code&gt; at five minutes), and a final DLQ for manual review. Each DLQ message should carry headers recording the original topic, partition, offset, exception class, attempt count, and stack trace.&lt;/p&gt;
&lt;p&gt;Kafka Connect has native DLQ support since version 2.0. Kafka Streams requires custom &lt;code&gt;DeserializationExceptionHandler&lt;/code&gt; and &lt;code&gt;ProductionExceptionHandler&lt;/code&gt; implementations.&lt;/p&gt;
&lt;p&gt;The monitoring rules: alert on DLQ message rate above 10 messages per second for five minutes as a warning, and DLQ backlog above 1,000 messages for 15 minutes as critical. Feed DLQ message rates into ksqlDB to drive per-sink breach alerts.&lt;/p&gt;
&lt;h3 id=&quot;6-track-end-to-end-latency-with-opentelemetry-trace-context-in-message-headers&quot;&gt;6. Track end-to-end latency with OpenTelemetry trace context in message headers&lt;/h3&gt;
&lt;p&gt;Broker and consumer metrics cannot tell you how long a specific message spent in a topic waiting to be consumed. OpenTelemetry’s messaging semantic conventions solve this at the per-message level.&lt;/p&gt;
&lt;p&gt;Producers inject a W3C &lt;code&gt;traceparent&lt;/code&gt; header into each Kafka message. Consumers extract it and create a child span linked to the producer span. The gap between the producer span ending and the consumer span starting is the topic dwell time for that message. Relevant span attributes per the OpenTelemetry messaging semantic conventions include &lt;code&gt;messaging.system=kafka&lt;/code&gt;, &lt;code&gt;messaging.destination.name&lt;/code&gt;, &lt;code&gt;messaging.kafka.partition&lt;/code&gt;, &lt;code&gt;messaging.kafka.message.offset&lt;/code&gt;, and &lt;code&gt;messaging.message.id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For high-throughput topics, use tail-based sampling. Keep all error spans and all spans exceeding p99 latency; sample the rest. Strimzi shipped OpenTelemetry support for Kafka Connect, MirrorMaker 2, and the Kafka Bridge in 2023.&lt;/p&gt;
&lt;p&gt;A practical note: OpenTelemetry messaging semantic conventions are still evolving as of mid-2026. Some span attributes including &lt;code&gt;messaging.kafka.consumer.group&lt;/code&gt; and &lt;code&gt;messaging.message.body.size&lt;/code&gt; are stable; others remain experimental. Pin your instrumentation library versions in production.&lt;/p&gt;
&lt;h3 id=&quot;7-build-end-to-end-auditing-for-true-data-loss-detection&quot;&gt;7. Build end-to-end auditing for true data-loss detection&lt;/h3&gt;
&lt;p&gt;This is the practice that separates mature streaming organisations from those that discover data loss during incident response. Broker metrics, consumer lag, and distributed traces do not tell you whether a message was dropped in transit between tiers. End-to-end auditing does.&lt;/p&gt;
&lt;p&gt;The pattern, implemented by Uber as Chaperone and &lt;a href=&quot;/articles/netflix-kafka-architecture&quot;&gt;Netflix&lt;/a&gt; as Inca: every tier (proxy, broker, mirror, aggregate, consumer) emits an audit message to a dedicated audit topic with a tuple of message ID, tier name, count, and time window. A stream-processing job (Flink at Netflix, custom at Uber) keys by message ID, fans across tiers, and emits a missing-trace signal when an expected tier did not report within the time window.&lt;/p&gt;
&lt;p&gt;Netflix’s Inca uses a Flink &lt;code&gt;GlobalWindow&lt;/code&gt; with a custom trigger and reduces state via consumer offsets. It produces loss-rate, duplicate-rate, and end-to-end latency as SLO metrics, plus the IDs of lost messages to a Kafka topic for automated re-fetch.&lt;/p&gt;
&lt;p&gt;Uber’s published example of why this matters: a dead-loop bug in uReplicator caused silent data loss. Neither uReplicator nor the Kafka brokers triggered any alerts. Only the end-to-end message counting job detected the loss. A pure broker-and-replicator-metric observability stack would never have surfaced it.&lt;/p&gt;
&lt;p&gt;This level of investment is appropriate once you have more than 100 topics or more than 10 teams producing to Kafka. For earlier-stage deployments, the priority is practices 1 through 4.&lt;/p&gt;
&lt;h3 id=&quot;8-enforce-topic-naming-conventions-through-ci&quot;&gt;8. Enforce topic naming conventions through CI&lt;/h3&gt;
&lt;p&gt;Topic naming is cheap to get right and expensive to get wrong. Kafka topics cannot be renamed after creation, so naming decisions made during initial development persist indefinitely.&lt;/p&gt;
&lt;p&gt;The community-converged convention is hierarchical:&lt;/p&gt;
&lt;p&gt;`&lt;env&gt;.&lt;visibility&gt;.&lt;type&gt;.&lt;domain&gt;.&lt;entity&gt;-by-&lt;key&gt;-v&lt;n&gt;&lt;/p&gt;
&lt;h1 id=&quot;example&quot;&gt;Example&lt;/h1&gt;
&lt;p&gt;prod.public.fct.payments.payment_completed-by-order_id-v2`&lt;/p&gt;
&lt;p&gt;Hard rules: lowercase only, choose either &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt; as the separator and use it consistently (mixing both creates collisions on JMX metric names), exclude any fields that might change over time such as team name, service name, or owner, and reserve the version suffix for backward-incompatible breaks only.&lt;/p&gt;
&lt;p&gt;Disable &lt;code&gt;auto.create.topics.enable&lt;/code&gt; on all production clusters. Provision topics through GitOps using Strimzi &lt;code&gt;KafkaTopic&lt;/code&gt; custom resources, Confluent for Kubernetes, or an in-house provisioning API. Validate names against a regex in a pre-commit hook. DoorDash replaced its Terraform-based topic-creation flow with an in-house API and reduced real-time pipeline onboarding time by 95%.&lt;/p&gt;
&lt;h3 id=&quot;9-set-retention-and-compaction-policies-with-observability-in-mind&quot;&gt;9. Set retention and compaction policies with observability in mind&lt;/h3&gt;
&lt;p&gt;Compacted topics (&lt;code&gt;cleanup.policy=compact&lt;/code&gt;) only retain the most recent value per key. Intermediate values may be deleted by compaction before a consumer reads them. This breaks audit-tier counting and any observability approach that relies on replaying message history.&lt;/p&gt;
&lt;p&gt;Reserve compaction for state changelogs, which is what Kafka Streams uses internally. Use &lt;code&gt;cleanup.policy=delete&lt;/code&gt; for event topics, with &lt;code&gt;retention.ms&lt;/code&gt; set to your maximum replay window. If you need long retention without the cost of persistent SSD storage, tiered storage (available in Confluent Cloud and as an open-source implementation from Pinterest via KIP-405) lets you extend retention to object storage.&lt;/p&gt;
&lt;p&gt;Track &lt;code&gt;LogSegmentBytes&lt;/code&gt; per topic and alert on disk-pressure-driven retention truncation, which silently shortens your replay window without any explicit configuration change.&lt;/p&gt;
&lt;h3 id=&quot;10-add-heartbeats-and-offset-translation-verification-for-multi-cluster-setups&quot;&gt;10. Add heartbeats and offset-translation verification for multi-cluster setups&lt;/h3&gt;
&lt;p&gt;In multi-cluster deployments, MirrorMaker 2’s replication of data is generally reliable. The harder problems are detecting replication lag, verifying offset translation, and coordinating client failover.&lt;/p&gt;
&lt;p&gt;MirrorMaker 2’s &lt;code&gt;MirrorHeartbeatConnector&lt;/code&gt; produces a heartbeat on every source cluster every five seconds. Alert if the heartbeat does not appear in the destination within &lt;code&gt;replication_factor × interval&lt;/code&gt;. &lt;code&gt;MirrorCheckpointConnector&lt;/code&gt; translates consumer offsets across clusters; verify by spot-checking that &lt;code&gt;sync.group.offsets.enabled=true&lt;/code&gt; and that a test consumer can fail over cleanly.&lt;/p&gt;
&lt;p&gt;Every published Kafka DR retrospective converges on the same lesson: replication is the solved part. Coordinating client failover during an actual incident, while also managing schema-registry consistency and consumer-group state across clusters, is where DR plans actually fail. Test your DR plan with current tooling and find the coordination gaps before investing in additional cross-cluster observability tooling.&lt;/p&gt;
&lt;h3 id=&quot;11-alert-on-slos-and-symptoms-not-on-infrastructure-causes&quot;&gt;11. Alert on SLOs and symptoms, not on infrastructure causes&lt;/h3&gt;
&lt;p&gt;URP (Under-Replicated Partitions) is the most commonly over-alerted Kafka metric. Todd Palino, who led SRE for Kafka at LinkedIn, has explicitly stated that URP does not map to an SLO and is often not actionable; it should be collected for forensics but should not page anyone.&lt;/p&gt;
&lt;p&gt;The metrics that should page are those that map directly to user-visible impact: under-min-ISR partitions (genuine data-loss risk), consumer-group &lt;code&gt;ERROR&lt;/code&gt; state from trend-based lag monitoring, DLQ rate breach, schema compatibility failure rate, and audit-tier loss-rate breach.&lt;/p&gt;
&lt;p&gt;The practical approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define SLOs first: “p99 end-to-end latency under 5 seconds for the &lt;code&gt;payments&lt;/code&gt; topic” or “fraud-detection consumer lag under 30 seconds.”&lt;/li&gt;
&lt;li&gt;Alert on SLO burn rate using multi-window burn-rate alerting.&lt;/li&gt;
&lt;li&gt;Demote URP, ISR shrink, broker CPU, and request-handler idle ratio to dashboards available for forensics, not to PagerDuty.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Aggregate consumer group lag with &lt;code&gt;max()&lt;/code&gt; per partition, not &lt;code&gt;avg()&lt;/code&gt;. One stalled partition out of ten will be invisible in the average.&lt;/p&gt;
&lt;h3 id=&quot;12-treat-data-lineage-as-a-runtime-artifact&quot;&gt;12. Treat data lineage as a runtime artifact&lt;/h3&gt;
&lt;p&gt;Static documentation of data lineage becomes inaccurate as soon as a pipeline changes. Runtime lineage, emitted as events as jobs execute, stays current.&lt;/p&gt;
&lt;p&gt;OpenLineage’s specification uses Job, Run, and Dataset entities with Facets for schema, quality, and other metadata. It emits events natively from Airflow, dbt, Spark, and Flink to Marquez (the reference implementation) or DataHub (originally LinkedIn, now maintained by Acryl Data). DataHub treats Kafka topics as first-class datasets, parses Kafka Connect SMT configurations including &lt;code&gt;RegexRouter&lt;/code&gt; and &lt;code&gt;EventRouter&lt;/code&gt; to resolve the actual destination topic, and supports column-level lineage where schema-registry data is available.&lt;/p&gt;
&lt;p&gt;Emit OpenLineage events from every job that touches Kafka. The producer-to-topic-to-consumer graph becomes queryable, auditable, and accurate in near-real time rather than a diagram that someone last updated six months ago.&lt;/p&gt;
&lt;h2 id=&quot;tooling-stack&quot;&gt;Tooling stack&lt;/h2&gt;
&lt;p&gt;No single tool covers the full observability surface, but a comprehensive Kafka management platform like Kpow by Factor House gets close, combining broker monitoring, consumer lag, schema registry, DLQ management, and &lt;a href=&quot;/articles/top-kafka-ui-tools-in-2026-a-practical-comparison-for-engineering-teams&quot;&gt;Kafka UI&lt;/a&gt; and management in one place. For teams that need additional capabilities, the ecosystem includes purpose-built tools across the following categories:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Open source options&lt;/th&gt;
&lt;th&gt;Commercial options&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broker/cluster monitoring&lt;/td&gt;
&lt;td&gt;Prometheus + JMX Exporter, Kafka Exporter, Cruise Control, Strimzi&lt;/td&gt;
&lt;td&gt;Kpow (Factor House), Confluent Control Center, Datadog, New Relic, Dynatrace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer lag monitoring&lt;/td&gt;
&lt;td&gt;Burrow, Kafka Lag Exporter&lt;/td&gt;
&lt;td&gt;Kpow (Factor House), Confluent Metrics API, Datadog, AWS MSK CloudWatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema registry&lt;/td&gt;
&lt;td&gt;Apicurio, Karapace, Confluent Schema Registry (community)&lt;/td&gt;
&lt;td&gt;Kpow (Factor House), Confluent Stream Governance, AWS Glue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data contracts&lt;/td&gt;
&lt;td&gt;ODCS, data-contract-cli&lt;/td&gt;
&lt;td&gt;Confluent Data Contracts, Atlan, Soda Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed tracing&lt;/td&gt;
&lt;td&gt;OpenTelemetry Collector + Jaeger/Tempo/SigNoz&lt;/td&gt;
&lt;td&gt;Honeycomb, Datadog APM, Dynatrace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DLQ management&lt;/td&gt;
&lt;td&gt;Kafka Connect native DLQ, Karafka&lt;/td&gt;
&lt;td&gt;Kpow (Factor House), Confluent Control Center&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lineage and metadata&lt;/td&gt;
&lt;td&gt;OpenLineage + Marquez, DataHub, Apache Amundsen&lt;/td&gt;
&lt;td&gt;Factor Platform (Factor House), Acryl Data, Atlan, Collibra, Monte Carlo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka UI and management&lt;/td&gt;
&lt;td&gt;AKHQ, Kafka UI, Redpanda Console&lt;/td&gt;
&lt;td&gt;Kpow (Factor House), Conduktor, Lenses.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-cluster replication&lt;/td&gt;
&lt;td&gt;MirrorMaker 2, uReplicator&lt;/td&gt;
&lt;td&gt;Confluent Replicator, Confluent Cluster Linking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;A reasonable starting point for a team with no existing investment: Prometheus + JMX Exporter + Kafka Exporter for cluster metrics, Burrow for consumer lag, Apicurio or Confluent Schema Registry community edition for schemas, OpenTelemetry Collector with Jaeger or Tempo for tracing, and OpenLineage with Marquez or DataHub for lineage. Add a Kafka UI for ad-hoc inspection and correlating data-level signals with cluster state. This broadly matches Cloudflare’s observability stack and the direction Shopify moved toward when consolidating off third-party tooling.&lt;/p&gt;
&lt;p&gt;For teams evaluating their options more broadly, see our guide to the &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;best Kafka monitoring tools&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-with-kafka-data-observability&quot;&gt;How Kpow helps with Kafka data observability&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; by Factor House covers several of the observability concerns described in this article within a single deployable tool.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe9961b0c8180a1e940601_kpow-cluster-management.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag visibility.&lt;/strong&gt; Kpow surfaces consumer group lag at the group, broker, topic, and &lt;a href=&quot;/articles/kafka-topic-partition-best-practices&quot;&gt;partition&lt;/a&gt; level. It handles both active groups and &lt;code&gt;EMPTY&lt;/code&gt; consumer groups, calculating lag for empty groups directly from start and end offsets via the AdminClient rather than relying on a cached snapshot. This matters in scenarios where a poison message has caused all instances of a consumer group to go offline; Kpow can still read the offsets and allow you to reset them without requiring the group to be running. Kpow also identifies simple consumers (those using manual partition assignment without group coordination), which appear in their own tab and are common in some Flink and Spark deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema Registry management.&lt;/strong&gt; Kpow integrates with Schema Registry, allowing you to inspect schema versions, view compatibility settings, and manage schemas directly from the UI. This is useful when investigating deserialization errors or verifying that compatibility modes are set correctly across environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prometheus egress for alerting integration.&lt;/strong&gt; Kpow exposes Prometheus endpoints following the OpenMetrics standard, making it straightforward to pipe Kafka metrics into your existing Grafana dashboards or AlertManager setup. Available endpoints include &lt;code&gt;/metrics/v1&lt;/code&gt; for all cluster metrics, &lt;code&gt;/group-offsets/v1&lt;/code&gt; for per-assignment group offset data, &lt;code&gt;/offsets/v1&lt;/code&gt; for topic partition offsets, and &lt;code&gt;/streams/v1&lt;/code&gt; for Kafka Streams metrics from connected agents. The group offset endpoint exposes &lt;code&gt;group_assignment_delta&lt;/code&gt;, &lt;code&gt;group_assignment_last_read&lt;/code&gt;, and &lt;code&gt;group_assignment_offset&lt;/code&gt; at the partition assignment level, which gives you the granularity needed for accurate lag alerting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Streams observability.&lt;/strong&gt; Through the &lt;code&gt;kpow-streams-agent&lt;/code&gt;, Kpow collects Kafka Streams application metrics and exposes them via the &lt;code&gt;/streams/v1&lt;/code&gt; and &lt;code&gt;/streams/v1/state&lt;/code&gt; Prometheus endpoints. This provides visibility into Kafka Streams topology state alongside broker and consumer metrics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kafka Connect management.&lt;/strong&gt; Kpow allows you to manage Kafka Connect connectors from the same interface, which reduces the context-switching involved when investigating pipeline issues that span brokers, consumers, and connectors.&lt;/p&gt;
&lt;p&gt;Kpow connects to any Kafka cluster and deploys via Docker, Helm, or JAR. If you want to evaluate it against your current observability setup, you can &lt;a href=&quot;/products/kpow&quot;&gt;try Kpow free for 30 days&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;implementation-roadmap&quot;&gt;Implementation roadmap&lt;/h2&gt;
&lt;p&gt;The practices in this guide are not all equal in complexity or return on investment. A phased approach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Within 30 days (foundation):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy trend-based consumer lag monitoring (Burrow or equivalent) and replace any threshold-based lag alerts with &lt;code&gt;ERROR&lt;/code&gt;/&lt;code&gt;WARN&lt;/code&gt; classification per consumer group.&lt;/li&gt;
&lt;li&gt;Stand up Schema Registry with compatibility set explicitly: &lt;code&gt;BACKWARD&lt;/code&gt; for Avro/JSON Schema, &lt;code&gt;BACKWARD_TRANSITIVE&lt;/code&gt; for Protobuf. Disable &lt;code&gt;auto.register.schemas&lt;/code&gt; in production. Add compatibility checking to CI as a hard gate.&lt;/li&gt;
&lt;li&gt;Disable &lt;code&gt;auto.create.topics.enable&lt;/code&gt; on all production clusters. Move topic provisioning into GitOps.&lt;/li&gt;
&lt;li&gt;Audit your current alerts. Demote URP, ISR shrink, and broker CPU to dashboards. Keep paging for under-min-ISR partitions, consumer-group &lt;code&gt;ERROR&lt;/code&gt;, DLQ rate breach, and schema compatibility failure rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Within 90 days (data-level observability):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instrument all producers and consumers with OpenTelemetry. Ensure trace context propagates in message headers. Export to Jaeger or Tempo with tail-based sampling.&lt;/li&gt;
&lt;li&gt;Deploy tiered DLQ patterns for any consumer where message loss is unacceptable. Instrument DLQs with rich headers and alert on rate and backlog.&lt;/li&gt;
&lt;li&gt;Define an SLO per business-critical topic. Track burn rate and alert on multi-window burn.&lt;/li&gt;
&lt;li&gt;Emit OpenLineage events from any Kafka Connect, Flink, Spark, or Airflow job touching Kafka. Ingest into Marquez or DataHub.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Within 6 months (mature pattern, applicable above 100 topics or 10 producing teams):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build or adopt end-to-end auditing: count messages at each tier and use a Flink or ksqlDB job to detect loss and duplication per message ID.&lt;/li&gt;
&lt;li&gt;Adopt data contracts beyond schemas: field-level rules, SLAs, and ownership. Choose between Confluent Data Contracts (paid, broker-side enforcement), ODCS with Redpanda Data Transforms (open source, broker-side), or producer-side CI validation.&lt;/li&gt;
&lt;li&gt;Consolidate dashboards so broker health, consumer lag, schema events, DLQ rates, and end-to-end traces are visible in a single view.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are experiencing more than three production incidents per quarter caused by schema changes, accelerate the schema enforcement work. If your mean time to detection for streaming pipeline incidents exceeds five minutes, end-to-end tracing and SLO-based alerting are the highest-leverage next steps.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka message size best practice</title><link>https://factorhouse.io/articles/kafka-message-size-best-practice/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-message-size-best-practice/</guid><description>How large should Kafka messages be in production? Covers sizing tiers, the four-config chain, compression codecs, and patterns for handling payloads above 1 MB.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every Kafka cluster operates under a set of message-size constraints, and getting them wrong costs you in ways that are not always immediately obvious: silent replication stalls, infinite producer retry loops, page-cache eviction cascades, and hard architectural ceilings imposed by cloud providers. This guide covers the sizing targets that production operators at scale converge on, how the configuration chain works, and what to do when your use case genuinely requires large messages.&lt;/p&gt;
&lt;h2 id=&quot;best-practice-for-kafka-message-size&quot;&gt;Best practice for Kafka message size&lt;/h2&gt;
&lt;p&gt;The production consensus, backed by what &lt;a href=&quot;/articles/linkedin-kafka-architecture&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;/articles/cloudflare-kafka-architecture&quot;&gt;Cloudflare&lt;/a&gt;, &lt;a href=&quot;/articles/netflix-kafka-architecture&quot;&gt;Netflix&lt;/a&gt;, and &lt;a href=&quot;/articles/uber-kafka-architecture&quot;&gt;Uber&lt;/a&gt; have published about their own clusters, is to keep individual messages well under 1 MB. The Apache Kafka broker default of &lt;code&gt;message.max.bytes = 1,048,588&lt;/code&gt; bytes (1 MiB plus 12 bytes of overhead, aligned under &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-4203&quot;&gt;KAFKA-4203&lt;/a&gt; with the Java producer’s &lt;code&gt;max.request.size = 1,048,576&lt;/code&gt;) is the design point Kafka is optimised for. It is not a ceiling to design towards.&lt;/p&gt;
&lt;p&gt;LinkedIn, which runs more than 100 clusters, 4,000 brokers, and 7 trillion messages per day, states this policy explicitly in their engineering blog: messages are capped at 1 MB, and anything that exceeds that is handled outside the standard message path using client-side fragmentation.&lt;/p&gt;
&lt;p&gt;The practical sweet spot for most production workloads is significantly smaller. Public Kafka benchmarks from Aiven, Google Cloud, and LinkedIn typically use 100 bytes to 10 KB as representative message sizes. Confluent’s canonical performance benchmark uses 1 KB messages. These numbers are not arbitrary: LinkedIn’s 2014 benchmark noted that at 100-byte messages you saturate the network, making small records the harder case for a messaging system to optimise.&lt;/p&gt;
&lt;p&gt;The 1 KB to 10 KB range is where Kafka’s batching, compression, and zero-copy I/O work most efficiently together. Messages in this range can be batched densely, compressed effectively at the batch level, and served from page cache without displacing hot data.&lt;/p&gt;
&lt;h2 id=&quot;kafka-message-size-limits&quot;&gt;Kafka message size limits&lt;/h2&gt;
&lt;h3 id=&quot;broker-defaults&quot;&gt;Broker defaults&lt;/h3&gt;
&lt;p&gt;The broker’s &lt;code&gt;message.max.bytes&lt;/code&gt; defaults to 1,048,588 bytes. This is the maximum size of a compressed record batch that the broker will accept. At the topic level, &lt;code&gt;max.message.bytes&lt;/code&gt; overrides this per topic; the effective limit is whichever value is higher.&lt;/p&gt;
&lt;h3 id=&quot;cloud-provider-ceilings&quot;&gt;Cloud provider ceilings&lt;/h3&gt;
&lt;p&gt;If you are running on a managed Kafka service, the provider’s hard limits constrain what you can configure:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Ceiling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apache Kafka (self-managed)&lt;/td&gt;
&lt;td&gt;1 MB default; configurable upward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Cloud Basic / Standard&lt;/td&gt;
&lt;td&gt;8 MB (8,388,608 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent Cloud Dedicated&lt;/td&gt;
&lt;td&gt;20 MB (20,971,520 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS MSK Serverless&lt;/td&gt;
&lt;td&gt;8 MB; not user-configurable without a support case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Event Hubs (Kafka API)&lt;/td&gt;
&lt;td&gt;1 MB; no override&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;These ceilings are architectural constraints, not soft guidelines. A design that relies on messages larger than 1 MB is incompatible with Azure Event Hubs. A design that needs more than 8 MB is incompatible with both Confluent Cloud Basic/Standard and AWS MSK Serverless. Validate these limits against your managed service tier before settling on an approach.&lt;/p&gt;
&lt;h2 id=&quot;sizing-tiers&quot;&gt;Sizing tiers&lt;/h2&gt;
&lt;p&gt;The guidance from Confluent, Red Hat, Strimzi, and operator engineering blogs converges on five tiers:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Size range&lt;/th&gt;
&lt;th&gt;Recommended approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ideal&lt;/td&gt;
&lt;td&gt;Less than 10 KB (typically 100 B – 10 KB)&lt;/td&gt;
&lt;td&gt;Default Kafka tuning. Batching and compression dominate. The benchmark sweet spot for most production workloads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceptable&lt;/td&gt;
&lt;td&gt;10 KB – 100 KB&lt;/td&gt;
&lt;td&gt;No configuration changes required. Tune batch.size to 64–128 KB and linger.ms to 10–20 ms to keep batches efficient.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle with care&lt;/td&gt;
&lt;td&gt;100 KB – 1 MB&lt;/td&gt;
&lt;td&gt;Individual messages can saturate per-partition batches at default config. Increase batch.size to 256 KB or higher and ensure compression is enabled. Messages larger than batch.size are sent unbatched.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoid unless necessary&lt;/td&gt;
&lt;td&gt;1 MB – 10 MB&lt;/td&gt;
&lt;td&gt;Requires raising all four configs in the producer/broker/consumer chain. Confluent’s own documentation describes very large messages as an anti-pattern in Kafka. Run these workloads on a dedicated cluster if other topics have low-latency SLAs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Do not use inline&lt;/td&gt;
&lt;td&gt;Over 10 MB&lt;/td&gt;
&lt;td&gt;Use the claim-check pattern (store payload in S3 or GCS; put a pointer in Kafka) or client-side chunking. Allocating a 1 GB JVM chunk per 1 GB message on both the client and broker is not operationally viable.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;how-to-configure-larger-messages&quot;&gt;How to configure larger messages&lt;/h2&gt;
&lt;p&gt;If the claim-check pattern is not viable and you need to raise the effective message size limit, you must update four interdependent configurations in concert. Missing any one of them produces failures that range from immediately visible to silently catastrophic.&lt;/p&gt;
&lt;p&gt;The chain is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;producer max.request.size     → topic max.message.bytes / broker message.max.bytes       → broker replica.fetch.max.bytes         → consumer max.partition.fetch.bytes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Configure them in this order to avoid silent failures:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Broker (requires restart for &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;message.max.bytes=10485760   replica.fetch.max.bytes=10485760   socket.request.max.bytes=104857600&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Topic (can be applied without a broker restart)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max.message.bytes=10485760&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Producer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max.request.size=10485760   buffer.memory=67108864&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Consumer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch.max.bytes=52428800   max.partition.fetch.bytes=10485760&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Keep these values equal or monotonically non-decreasing along the chain.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-when-the-chain-breaks&quot;&gt;What happens when the chain breaks&lt;/h3&gt;
&lt;p&gt;The failure mode depends on which link is misconfigured:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Producer over &lt;code&gt;max.request.size&lt;/code&gt;&lt;/strong&gt;: a synchronous &lt;code&gt;RecordTooLargeException&lt;/code&gt; is thrown. The message never reaches the broker. This is the cleanest failure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Producer under &lt;code&gt;max.request.size&lt;/code&gt; but over broker &lt;code&gt;message.max.bytes&lt;/code&gt;&lt;/strong&gt;: the broker returns &lt;code&gt;MESSAGE_TOO_LARGE&lt;/code&gt;. Modern Java producers auto-split the batch and retry. If &lt;code&gt;batch.size&lt;/code&gt; itself exceeds &lt;code&gt;message.max.bytes&lt;/code&gt;, you enter an infinite split-and-retry loop (&lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-8350&quot;&gt;KAFKA-8350&lt;/a&gt;). Always validate that &lt;code&gt;batch.size&lt;/code&gt; does not exceed &lt;code&gt;message.max.bytes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; smaller than &lt;code&gt;message.max.bytes&lt;/code&gt;&lt;/strong&gt;: historically this caused silent replication stalls, ISR shrinkage, and data loss under unclean leader election. Modern Kafka guarantees forward progress by always returning the first record batch, but replication latency degrades and a permanent replica-lag floor is introduced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer &lt;code&gt;max.partition.fetch.bytes&lt;/code&gt; too small&lt;/strong&gt;: the same forward-progress guarantee applies, but a consumer group can fall permanently behind on partitions containing large records.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;kafka-connect-and-mirrormaker-2&quot;&gt;Kafka Connect and MirrorMaker 2&lt;/h3&gt;
&lt;p&gt;For Kafka Connect and MirrorMaker 2, apply the large-message overrides via the &lt;code&gt;producer.override.*&lt;/code&gt; and &lt;code&gt;consumer.*&lt;/code&gt; prefixes in the connector configuration. The connector-level settings do not inherit from broker defaults automatically.&lt;/p&gt;
&lt;h2 id=&quot;how-to-handle-very-small-messages&quot;&gt;How to handle very small messages&lt;/h2&gt;
&lt;p&gt;The performance problem with small messages is a batching problem, not a size problem. Kafka compression operates at the batch level, not the message level. A compressor needs a sufficiently large sample of data to find repeated patterns; on a single 100-byte message, compression saves under 5% while still incurring the full CPU cost.&lt;/p&gt;
&lt;p&gt;Apache Kafka 4.0 (released March 2025) changed &lt;code&gt;linger.ms&lt;/code&gt; from 0 to 5 ms as a direct acknowledgement that the previous default was actively damaging batching efficiency. If you are on Kafka earlier than 4.0, set &lt;code&gt;linger.ms&lt;/code&gt; explicitly. Do not rely on the zero default.&lt;/p&gt;
&lt;p&gt;The standard production tuning for small-to-medium messages:&lt;/p&gt;
&lt;p&gt;`# Producer&lt;br&gt;
batch.size=131072        # 128 KB; default 16 KB is undersized for high-volume workloads&lt;br&gt;
linger.ms=20             # or 10–100 depending on latency tolerance&lt;br&gt;
compression.type=lz4     # or zstd if storage cost is the bottleneck&lt;br&gt;
buffer.memory=67108864   # 64 MB&lt;/p&gt;
&lt;h1 id=&quot;topic&quot;&gt;Topic&lt;/h1&gt;
&lt;p&gt;compression.type=producer    # broker preserves whatever the producer used&lt;br&gt;
min.insync.replicas=2`&lt;/p&gt;
&lt;p&gt;To illustrate the leverage: Confluent’s throughput tuning benchmark shows that with default producer config, 8 KB records produce 23.58 MB/s at 927 ms average latency. With &lt;code&gt;batch.size=200000&lt;/code&gt;, &lt;code&gt;linger.ms=100&lt;/code&gt;, &lt;code&gt;compression.type=lz4&lt;/code&gt;, and &lt;code&gt;acks=1&lt;/code&gt;, the same workload achieves 94.89 MB/s at 4.92 ms average latency. That is a 4x throughput increase from producer configuration changes alone.&lt;/p&gt;
&lt;p&gt;The JMX metric &lt;code&gt;bufferpool-wait-ratio&lt;/code&gt; is a direct signal of buffer pressure. Values above 0.05 indicate that the producer is blocking on buffer allocation; increase &lt;code&gt;buffer.memory&lt;/code&gt; to 64–256 MB if this occurs.&lt;/p&gt;
&lt;h2 id=&quot;key-kafka-message-configurations&quot;&gt;Key Kafka message configurations&lt;/h2&gt;
&lt;h3 id=&quot;messagemaxbytes-broker&quot;&gt;&lt;code&gt;message.max.bytes&lt;/code&gt; (broker)&lt;/h3&gt;
&lt;p&gt;Default: 1,048,588 bytes. The largest compressed record batch the broker accepts. Raising this without also raising &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; is the most common source of silent replication degradation in misconfigured clusters.&lt;/p&gt;
&lt;h3 id=&quot;maxmessagebytes-topic&quot;&gt;&lt;code&gt;max.message.bytes&lt;/code&gt; (topic)&lt;/h3&gt;
&lt;p&gt;Per-topic override of &lt;code&gt;message.max.bytes&lt;/code&gt;. The effective limit for a given partition is the higher of the topic-level and broker-level values. Setting this per topic rather than at the broker level is the safer approach: it limits exposure and allows you to apply large-message config only where it is required.&lt;/p&gt;
&lt;h3 id=&quot;maxrequestsize-producer&quot;&gt;&lt;code&gt;max.request.size&lt;/code&gt; (producer)&lt;/h3&gt;
&lt;p&gt;Default: 1,048,576 bytes. The maximum size of a single produce request (the entire batch, post-compression). This must be raised before raising broker limits, or the producer will fail at the client before the broker ever sees the message.&lt;/p&gt;
&lt;h3 id=&quot;replicafetchmaxbytes-broker-follower&quot;&gt;&lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; (broker, follower)&lt;/h3&gt;
&lt;p&gt;Default: 1,048,576 bytes. Controls how many bytes a follower fetches per partition per request. This is the configuration most commonly forgotten when raising message size limits, and historically the most dangerous to misconfigure. Per Cloudera’s documentation: a broker can accept messages it cannot replicate if this value is smaller than &lt;code&gt;message.max.bytes&lt;/code&gt;, which creates a data loss risk. Always set this equal to or larger than &lt;code&gt;message.max.bytes&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;batchsize-producer&quot;&gt;&lt;code&gt;batch.size&lt;/code&gt; (producer)&lt;/h3&gt;
&lt;p&gt;Default: 16,384 bytes (16 KB). Records larger than this value are sent as their own batch, unbatched. For workloads with messages in the 10–100 KB range, the default &lt;code&gt;batch.size&lt;/code&gt; becomes the binding constraint on batching efficiency. Set to 64–256 KB for high-volume producers.&lt;/p&gt;
&lt;h3 id=&quot;lingerms-producer&quot;&gt;&lt;code&gt;linger.ms&lt;/code&gt; (producer)&lt;/h3&gt;
&lt;p&gt;Default: 5 ms (Kafka 4.0+); 0 ms (pre-4.0). The maximum time the producer waits to fill a batch before sending. At 0 ms in low-to-medium throughput environments, most batches are single-message, which eliminates most of the benefit of compression.&lt;/p&gt;
&lt;h3 id=&quot;compressiontype-producer--topic&quot;&gt;&lt;code&gt;compression.type&lt;/code&gt; (producer / topic)&lt;/h3&gt;
&lt;p&gt;Default: &lt;code&gt;none&lt;/code&gt; at the producer, &lt;code&gt;producer&lt;/code&gt; at the topic level (which means the broker preserves whatever the producer sent). At the topic level, you can override to a specific codec, but this forces broker-side recompression if producers and topics diverge. Setting the topic to &lt;code&gt;producer&lt;/code&gt; and controlling compression at the producer is the lower-overhead approach.&lt;/p&gt;
&lt;h2 id=&quot;tips-for-achieving-better-performance&quot;&gt;Tips for achieving better performance&lt;/h2&gt;
&lt;h3 id=&quot;use-lz4-as-the-default-compression-codec-switch-to-zstandard-when-storage-is-the-bottleneck&quot;&gt;Use LZ4 as the default compression codec, switch to Zstandard when storage is the bottleneck&lt;/h3&gt;
&lt;p&gt;LZ4 offers the best throughput-per-CPU ratio for latency-sensitive workloads: compress speeds around 594 MB/s and decompress speeds around 2,428 MB/s on modern hardware. Zstandard at level 1 produces better compression ratios than LZ4 with acceptable throughput. At level 3 (the default), Zstd compresses around 242 MB/s but achieves ratios of roughly 24% of the original payload size for typical Kafka data. The KIP-390 measurements show that moving from Zstd level 3 to level 1 produces 32.7% more messages per second.&lt;/p&gt;
&lt;p&gt;Cloudflare’s documented switch from no compression to Snappy to Zstandard reduced their topic size by 4.5x, freeing them from a pending hardware expansion. Their data was highly repetitive HTTP log payloads; your compression ratio will depend on your payload structure.&lt;/p&gt;
&lt;p&gt;Gzip is the slowest codec in all benchmarks, with throughput around 830 msg/s in the HUMAN Security benchmark versus 3,400 for LZ4 and Snappy. The bottleneck is not CPU but lock contention in the gzip JNI binding. Prefer LZ4 or Zstd for all new workloads.&lt;/p&gt;
&lt;h3 id=&quot;switch-from-json-to-avro-or-protobuf&quot;&gt;Switch from JSON to Avro or Protobuf&lt;/h3&gt;
&lt;p&gt;JSON is materially larger than its binary equivalents for the same logical record. A typical Avro or Protobuf payload is 30–50% the size of equivalent JSON. Cloudflare moved from JSON to Protobuf specifically because JSON made forward and backward compatibility harder to enforce and produced substantially larger messages.&lt;/p&gt;
&lt;p&gt;Using Avro or Protobuf with a schema registry also reduces the per-message overhead: Confluent Schema Registry stores a 4-byte schema ID plus 1 magic byte per message, with the schema itself fetched once per consumer session and cached.&lt;/p&gt;
&lt;h3 id=&quot;keep-heap-small-let-the-os-manage-page-cache&quot;&gt;Keep heap small; let the OS manage page cache&lt;/h3&gt;
&lt;p&gt;Kafka brokers serve data primarily from Linux page cache via &lt;code&gt;sendfile()&lt;/code&gt;, which bypasses the JVM heap entirely for message bodies. The JVM heap is used for request handling, metadata management, and format down-conversion. On a 64 GB broker, set JVM heap to 6–10 GB and let the operating system use the remainder as page cache. Setting &lt;code&gt;vm.swappiness=1&lt;/code&gt; prevents the kernel from swapping page cache to disk under memory pressure.&lt;/p&gt;
&lt;p&gt;Large messages are particularly damaging in this model: a single large message can evict thousands of small hot records from page cache, converting subsequent reads from cache hits to physical disk reads.&lt;/p&gt;
&lt;h3 id=&quot;run-large-message-workloads-on-a-dedicated-cluster&quot;&gt;Run large-message workloads on a dedicated cluster&lt;/h3&gt;
&lt;p&gt;The tuning required for large messages (larger &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt;, higher &lt;code&gt;batch.size&lt;/code&gt;, longer &lt;code&gt;linger.ms&lt;/code&gt;) conflicts with the tuning required for low-latency real-time traffic. Mixing workloads with significantly different size profiles on the same cluster means optimising for neither. If you have topics with 1 MB or larger messages and other topics with strict latency SLAs, use separate clusters.&lt;/p&gt;
&lt;h3 id=&quot;monitor-the-right-jmx-metrics&quot;&gt;Monitor the right JMX metrics&lt;/h3&gt;
&lt;p&gt;The metrics most directly relevant to message size behaviour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RequestSizeAvg&lt;/code&gt; and &lt;code&gt;RequestSizeMax&lt;/code&gt;: track against &lt;code&gt;message.max.bytes&lt;/code&gt;. Set an alert at 75% of the broker limit on &lt;code&gt;RequestSizeMax&lt;/code&gt; so you detect payload bloat before it becomes an incident.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RecordsPerRequestAvg&lt;/code&gt;: low values indicate poor batching.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bufferpool-wait-ratio&lt;/code&gt;: producer-side pressure on buffer allocation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;records-lag-max&lt;/code&gt;: correlate spikes with &lt;code&gt;RequestSizeAvg&lt;/code&gt; to determine whether consumer lag is driven by large records.&lt;/li&gt;
&lt;li&gt;Under-replicated partitions (URP): a sustained URP count is the primary signal of a &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; misconfiguration for large messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;for-payloads-that-genuinely-need-to-exceed-1-mb-use-the-claim-check-pattern&quot;&gt;For payloads that genuinely need to exceed 1 MB, use the claim-check pattern&lt;/h3&gt;
&lt;p&gt;Confluent’s own pattern documentation describes this as the standard approach: store the payload in an external store such as S3 or GCS, and publish only a small pointer record to Kafka. The pointer typically contains bucket, key, ETag, content type, size, and schema version. Sign URLs at read time with short TTLs. Manage object cleanup via lifecycle rules, and ensure &lt;code&gt;delete.retention.ms&lt;/code&gt; on the Kafka topic is longer than the S3 grace period to avoid orphaned tombstones.&lt;/p&gt;
&lt;p&gt;The claim-check pattern adds an external call in the read path, which introduces latency and a dependency on the external store’s availability. It is not suitable for stream-processing topologies where the external hop breaks the processing model. For those cases, in-band chunking using a library such as LinkedIn’s open-source &lt;code&gt;li-apache-kafka-clients&lt;/code&gt; is the alternative: the producer fragments messages at a configurable segment size, and the consumer reassembles them before the application sees the record.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-you-manage-message-sizes&quot;&gt;How Kpow helps you manage message sizes&lt;/h2&gt;
&lt;p&gt;Correctly sizing and configuring Kafka messages across a production cluster involves tracking multiple interdependent parameters across brokers, topics, and clients. &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; is a commercial Kafka management UI and API from Factor House that surfaces the configurations and metrics most relevant to message-size operations without requiring you to instrument a separate observability stack.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ff681d840e0f91468fd5b0_kpow-data-inspect.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;configuration-visibility-across-brokers-and-topics&quot;&gt;Configuration visibility across brokers and topics&lt;/h3&gt;
&lt;p&gt;Kpow’s topic and broker configuration views show every config parameter alongside its current value, source (default, dynamic, or static), and importance. For message-size-relevant parameters specifically, the topic creation form displays the top five most common values set across your cluster for each config item, which makes it straightforward to spot drift in &lt;code&gt;max.message.bytes&lt;/code&gt; across topics. You can view and edit &lt;code&gt;message.max.bytes&lt;/code&gt; and &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; at the broker level with appropriate &lt;a href=&quot;/articles/rbac-for-kafka&quot;&gt;RBAC&lt;/a&gt; permissions, and Kpow renders the equivalent &lt;code&gt;kafka-topics.sh&lt;/code&gt; command if you prefer to manage changes through GitOps rather than through the UI.&lt;/p&gt;
&lt;h3 id=&quot;under-replicated-partition-detection&quot;&gt;Under-replicated partition detection&lt;/h3&gt;
&lt;p&gt;Kpow surfaces under-replicated partition counts with elapsed time since the URP was first detected. This is the primary operational signal for catching &lt;code&gt;replica.fetch.max.bytes&lt;/code&gt; misconfiguration in large-message environments. The calculation handles offline brokers that are not visible to the AdminClient, so URP counts remain accurate even during partial cluster failures.&lt;/p&gt;
&lt;h3 id=&quot;message-inspection-for-large-payload-topics&quot;&gt;Message inspection for large-payload topics&lt;/h3&gt;
&lt;p&gt;For topics carrying large messages, Kpow’s Data Inspect feature runs server-side JQ-like queries across JSON, Avro, and Protobuf payloads. For topics with large messages, the &lt;code&gt;SAMPLER_POLL_MS&lt;/code&gt; parameter can be increased from its 3,500 ms default to give consumers more time to fetch and batch records per poll. Message size configuration is directly relevant here: Kpow’s own documentation explicitly notes this tuning for large-message scenarios.&lt;/p&gt;
&lt;h3 id=&quot;throughput-and-lag-metrics-per-topic&quot;&gt;Throughput and lag metrics per topic&lt;/h3&gt;
&lt;p&gt;Kpow computes per-topic throughput in both messages per second and MB/s, partition lag, and consumer group offsets without requiring external Prometheus exporters. For clusters running on Confluent Cloud, it integrates with the Confluent Cloud Metrics API to surface retained bytes and active connection counts per topic, which helps you verify whether actual on-disk topic size matches your message-size and retention assumptions. Prometheus endpoints are also available per cluster and per topic for teams that want to pipe data into an existing &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Grafana&lt;/a&gt; stack.&lt;/p&gt;
&lt;p&gt;Kpow runs as a single stateless Docker container and is compatible with Apache Kafka 1.0 and later, as well as managed services including Amazon MSK, Confluent Cloud, Azure Event Hubs, Aiven, and Redpanda. A &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; with full enterprise functionality is available. No credit card is required, and a free Community Edition is permanently available for local development environments.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related reading&lt;/h2&gt;
&lt;p&gt;For guidance on structuring and selecting message keys, which interacts with partitioning behaviour and message ordering, see our article on &lt;a href=&quot;/articles/kafka-message-key-best-practices&quot;&gt;Kafka message key best practices&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka partition key best practices</title><link>https://factorhouse.io/articles/kafka-partition-key-best-practices/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-partition-key-best-practices/</guid><description>How Kafka partition keys work, what makes a good key, and practical guidance on cardinality, hot partitions, compaction, cross-language hashing, and safe key migration.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The partition key is the most consequential design decision you make for a Kafka topic. It determines message ordering, consumer parallelism, log compaction behavior, and whether your cluster develops hot spots under load. Getting it wrong early is expensive to fix later.&lt;/p&gt;
&lt;p&gt;This guide covers what partition keys actually do at the implementation level, common patterns and anti-patterns, and practical guidance on monitoring and migration. Tools like &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; can help you observe partition behavior across your cluster, which is covered toward the end.&lt;/p&gt;
&lt;h2 id=&quot;what-partition-keys-are-and-what-they-do&quot;&gt;What partition keys are and what they do&lt;/h2&gt;
&lt;p&gt;A Kafka producer record is a &lt;code&gt;(key, value, headers, timestamp)&lt;/code&gt; tuple. When the producer sends a record, the partitioner decides which partition it lands on. The default Java &lt;code&gt;DefaultPartitioner&lt;/code&gt; follows this logic:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;if (partition is explicitly specified) → use it   else if (keyBytes != null)            → partition = murmur2(keyBytes) % numPartitions   else                                   → sticky partitioner picks a partition until the batch fills&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Murmur2 is a non-cryptographic 32-bit hash chosen for speed and distribution. The critical detail is the &lt;strong&gt;modulo operation&lt;/strong&gt;: partition assignment is &lt;code&gt;hash(key) % N&lt;/code&gt;, where &lt;code&gt;N&lt;/code&gt; is the partition count. Any change to &lt;code&gt;N&lt;/code&gt; reshuffles the entire mapping, which breaks ordering guarantees for existing keys. Every Kafka practitioner guide repeats this warning, and for good reason.&lt;/p&gt;
&lt;h3 id=&quot;null-keys&quot;&gt;Null keys&lt;/h3&gt;
&lt;p&gt;Null-keyed records are handled differently depending on your Kafka version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-Kafka 2.4&lt;/strong&gt;: round-robin per record, producing even distribution but small, inefficient batches.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka 2.4+ (KIP-480)&lt;/strong&gt;: the &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; pins to one partition until the batch fills, then rotates. Better batching, but the original implementation worsened skew when a broker was slow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka 3.3+ (KIP-794)&lt;/strong&gt;: &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; is &lt;code&gt;@Deprecated&lt;/code&gt;. The current recommendation is to remove the partitioner class configuration and set &lt;code&gt;partitioner.ignore.keys=true&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Null keys are appropriate when ordering is irrelevant and downstream consumers have no per-entity locality requirements, such as fire-and-forget log shipping. They are a problem when the topic is compacted, when consumers expect per-entity ordering, or when you anticipate adding stateful stream processing later.&lt;/p&gt;
&lt;h3 id=&quot;log-compaction&quot;&gt;Log compaction&lt;/h3&gt;
&lt;p&gt;With &lt;code&gt;cleanup.policy=compact&lt;/code&gt;, Kafka’s log-cleaner retains only the latest value per key per partition. This requires non-null keys. The broker exposes &lt;code&gt;NoKeyCompactedTopicRecordsPerSec&lt;/code&gt; precisely so you can alert when null-keyed records land on a compacted topic. This metric should always be zero on compacted topics.&lt;/p&gt;
&lt;p&gt;Compaction is also per-partition. If the same logical entity ends up under different keys due to a mutable field change, or on different partitions because partition count increased, compaction will not deduplicate across those entries. Compacted topics need immutable keys.&lt;/p&gt;
&lt;h2 id=&quot;kafka-producer-partition-strategies&quot;&gt;Kafka producer partition strategies&lt;/h2&gt;
&lt;p&gt;The table below covers the built-in options:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default (murmur2 on key, sticky on null)&lt;/td&gt;
&lt;td&gt;General use; correct for most workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UniformStickyPartitioner (deprecated 3.3+)&lt;/td&gt;
&lt;td&gt;High-throughput unkeyed writes pre-3.3 only. Never use for keyed traffic: it explicitly ignores keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;partitioner.ignore.keys=true (3.3+)&lt;/td&gt;
&lt;td&gt;Modern equivalent of sticky for unkeyed writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoundRobinPartitioner&lt;/td&gt;
&lt;td&gt;Forces round-robin even with keys present; rarely appropriate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Partitioner&lt;/td&gt;
&lt;td&gt;Priority routing, geographic affinity, multi-tenant isolation, weighted distribution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Custom partitioners give you precise control but come with real implementation complexity. The cross-language hashing problem described below is the canonical example of how easily a custom partitioning scheme can break. Confluent’s guidance is to stick with the default unless you have a clear, measurable reason to change.&lt;/p&gt;
&lt;h3 id=&quot;the-cross-language-hashing-problem&quot;&gt;The cross-language hashing problem&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;librdkafka&lt;/code&gt; (the C library underlying Python, Go, and other non-JVM Kafka clients) defaults to CRC32, not murmur2. A Python producer and a Java producer writing to the same topic with the same logical key will land records on different partitions unless you explicitly set &lt;code&gt;partitioner=murmur2_random&lt;/code&gt; in librdkafka.&lt;/p&gt;
&lt;p&gt;This matters in mixed-language environments, and it affects Kafka Connect tasks and CDC connectors, which are typically JVM-based and will use murmur2. If you have multiple producer languages writing to the same topic, pin the partitioner explicitly in every client.&lt;/p&gt;
&lt;h2 id=&quot;kafka-partition-key-best-practices&quot;&gt;Kafka partition key best practices&lt;/h2&gt;
&lt;h3 id=&quot;choose-a-high-cardinality-immutable-identifier&quot;&gt;Choose a high-cardinality, immutable identifier&lt;/h3&gt;
&lt;p&gt;The most important thing you can do is choose the right key before the topic goes into production. The decision frames are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What is the smallest entity that must stay ordered together?&lt;/strong&gt; That entity’s ID is your key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is that field immutable?&lt;/strong&gt; If it can change (email address, username, account status, IP address), do not use it as a key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does it have enough distinct values?&lt;/strong&gt; For even distribution, you generally need at least 20 times as many distinct key values as partitions. With fewer than that, the binomial variance is high and some partitions will get significantly more load than others.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;System-generated identifiers (UUIDs, customer IDs, aggregate IDs, order IDs) satisfy all three criteria. User-editable fields almost never do.&lt;/p&gt;
&lt;h3 id=&quot;avoid-mutable-fields&quot;&gt;Avoid mutable fields&lt;/h3&gt;
&lt;p&gt;Using a mutable field as a partition key creates two compounding problems.&lt;/p&gt;
&lt;p&gt;First, ordering breaks. Events for the same logical entity before and after the field changes hash to different partitions. Any stateful consumer that expects per-entity ordering will process those events out of sequence, silently.&lt;/p&gt;
&lt;p&gt;Second, log compaction breaks. A compacted topic keyed by &lt;code&gt;email&lt;/code&gt; accumulates stale entries whenever users change their email address. The old key and the new key are distinct, so the log-cleaner cannot merge them. The compacted log grows indefinitely without correctly representing the latest state of each entity.&lt;/p&gt;
&lt;p&gt;The well-established guidance from Confluent, Cloudurable, and others: key compacted topics by an immutable, system-generated entity ID, never by anything the application or user can mutate.&lt;/p&gt;
&lt;h3 id=&quot;watch-key-cardinality&quot;&gt;Watch key cardinality&lt;/h3&gt;
&lt;p&gt;Low-cardinality keys (region, country, plan tier, status enum) guarantee hot partitions. If you only have five regions and thirty partitions, at most five partitions will ever receive traffic.&lt;/p&gt;
&lt;p&gt;High-cardinality keys can also cause hot partitions when the distribution is skewed. Tenant IDs where one enterprise customer generates orders of magnitude more traffic than others, or user IDs with celebrity users, follow a Zipf distribution. The math looks fine on average, but in practice one or two keys drive the majority of throughput.&lt;/p&gt;
&lt;p&gt;Both cases require intervention, but through different mechanisms.&lt;/p&gt;
&lt;h3 id=&quot;handle-hot-partitions-with-composite-keys-or-salting&quot;&gt;Handle hot partitions with composite keys or salting&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Composite keys&lt;/strong&gt; combine two fields: &lt;code&gt;tenantId|entityId&lt;/code&gt;, &lt;code&gt;region|customerId&lt;/code&gt;, &lt;code&gt;shardKey|primaryKey&lt;/code&gt;. This widens the cardinality of the routing key while preserving ordering for the inner entity. Use composite keys when one field alone does not have enough cardinality, or when one field has a skewed distribution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key salting&lt;/strong&gt; appends a random bucket suffix to a hot key: &lt;code&gt;userId#&amp;lt;0..K-1&amp;gt;&lt;/code&gt;. This distributes a single hot entity across K partitions. The trade-off is that per-entity ordering is lost across those salt buckets, so consumers must be designed to merge by the un-salted prefix. Salting works well when downstream processing is associative and order-insensitive (counts, sums, deduplication to a database).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time-window bucketing&lt;/strong&gt; adds a time component: &lt;code&gt;userId|2025-10-21T10:00&lt;/code&gt;. This is useful for bursty single keys where the burst is time-bounded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dedicated topics&lt;/strong&gt; for celebrity keys are the most expensive option but fully isolate their load. Reserve this for cases where the other approaches are not viable.&lt;/p&gt;
&lt;p&gt;New Relic’s events pipeline is a documented example: the top 1.5% of query identifiers drove roughly 90% of events on their aggregation topic. Their fix was a composite key combining query ID with a time-window start time, spreading hot queries across partitions in time-bounded chunks.&lt;/p&gt;
&lt;h3 id=&quot;do-not-use-uniformstickypartitioner-for-keyed-traffic&quot;&gt;Do not use &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; for keyed traffic&lt;/h3&gt;
&lt;p&gt;This warrants a specific call-out because it is a common mistake. &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; explicitly ignores partition keys. Records with the same key are not guaranteed to land on the same partition. The class has been &lt;code&gt;@Deprecated&lt;/code&gt; since Kafka 3.3, but it appears in older documentation and can still be set explicitly. If you see &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; in your producer configuration and your topic has keyed traffic, remove it.&lt;/p&gt;
&lt;h3 id=&quot;over-provision-partitions-at-topic-creation&quot;&gt;Over-provision partitions at topic creation&lt;/h3&gt;
&lt;p&gt;Adding partitions to a live topic with keyed traffic breaks the &lt;code&gt;hash(key) % N&lt;/code&gt; mapping. Keys that previously routed to partition X will route to a different partition after the count changes. For stateful Kafka Streams applications, this is worse: state stores partition by the same hash, so an expansion also corrupts the state mapping.&lt;/p&gt;
&lt;p&gt;Start with more partitions than you need today. It is much cheaper to over-provision upfront than to migrate later. The practical upper bound is around 4,000 partitions per broker on conservative deployments. Clusters with 100,000+ partitions have caused outages even under no traffic, because each partition represents file handles, metadata, and replica-fetcher threads.&lt;/p&gt;
&lt;h3 id=&quot;be-careful-with-null-keys-on-compacted-topics&quot;&gt;Be careful with null keys on compacted topics&lt;/h3&gt;
&lt;p&gt;If your topic has &lt;code&gt;cleanup.policy=compact&lt;/code&gt;, monitor &lt;code&gt;NoKeyCompactedTopicRecordsPerSec&lt;/code&gt;. A value above zero means null-keyed records are reaching the topic and compaction is doing nothing for them. Producers sending null keys to a compacted topic are usually misconfigured. Alert on this metric; it should always be zero.&lt;/p&gt;
&lt;h3 id=&quot;standardise-the-hash-function-across-all-producers&quot;&gt;Standardise the hash function across all producers&lt;/h3&gt;
&lt;p&gt;If only one language produces to a topic, the default hash function is whatever that client uses. If multiple languages produce to the same topic and rely on consistent key-to-partition routing, you must pin the hash function explicitly. Set &lt;code&gt;partitioner=murmur2_random&lt;/code&gt; in librdkafka-based clients so they match the JVM default. Verify CDC connectors and Kafka Connect tasks, which are JVM-based and use murmur2 regardless of other producer languages in your stack.&lt;/p&gt;
&lt;h2 id=&quot;how-to-change-your-key-strategy-safely&quot;&gt;How to change your key strategy safely&lt;/h2&gt;
&lt;p&gt;Once a topic is in production, you cannot change the partition count or key field in place without breaking ordering. The documented migration pattern, used by teams at AppsFlyer and described in Confluent’s documentation, is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Run a side consumer&lt;/strong&gt; that reads the live topic and simulates the new key, computing what partition each record would land on. Collect distribution metrics to validate the new key before committing to it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a new topic&lt;/strong&gt; with the new partition count and key schema.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dual-write via a feature flag.&lt;/strong&gt; Producers emit to both the old and new topic simultaneously.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bring up new consumers&lt;/strong&gt; reading from the new topic. Verify they produce the same outputs as the old consumers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drain old consumers&lt;/strong&gt;, cut traffic over, decommission the old topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communicate the migration plan&lt;/strong&gt; to all downstream teams before flipping the flag. Partition keys are a cross-team contract. Changing them affects every service that reads from the topic, every stateful processor, and potentially every database that receives derived output.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the cutover, re-tune &lt;code&gt;linger.ms&lt;/code&gt;, &lt;code&gt;batch.size&lt;/code&gt;, and &lt;code&gt;buffer.memory&lt;/code&gt;. Different keys have different temporal clustering properties, and batching dynamics change in ways that are not always immediately obvious.&lt;/p&gt;
&lt;h2 id=&quot;monitoring-partition-key-health&quot;&gt;Monitoring partition key health&lt;/h2&gt;
&lt;p&gt;The metrics that matter most for key-related issues:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;kafka.cluster:type=Partition,topic={t},name=Size,partition={p}&lt;/td&gt;
&lt;td&gt;Per-partition byte rate variance is the primary skew signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;td&gt;Per-partition LAG from kafka-consumer-groups.sh –describe or records-lag-max&lt;/td&gt;
&lt;td&gt;Key skew often shows up in lag before it shows up in disk usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;UnderReplicatedPartitions, IsrShrinksPerSec&lt;/td&gt;
&lt;td&gt;Hot partitions frequently manifest as ISR shrinks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compaction&lt;/td&gt;
&lt;td&gt;NoKeyCompactedTopicRecordsPerSec&lt;/td&gt;
&lt;td&gt;Should be 0 on every compacted topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;BytesInPerSec,topic={t}&lt;/td&gt;
&lt;td&gt;Top-line throughput comparison across topics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;Operationally, alert when the max-to-average per-partition byte rate ratio exceeds 1.5 to 2.0. A ratio under 1.2 generally requires no action. A ratio above 2.0 is worth treating as urgent: investigate whether to apply a composite key, salting, or a dedicated topic for celebrity keys.&lt;/p&gt;
&lt;p&gt;For topic-level partition counts referenced in the &lt;a href=&quot;/articles/kafka-topic-partition-best-practices&quot;&gt;Kafka topic partition best practices guide&lt;/a&gt;, the same thresholds apply: per-broker partition counts approaching 3,500 warrant attention regardless of traffic levels.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-with-partition-key-monitoring&quot;&gt;How Kpow helps with partition key monitoring&lt;/h2&gt;
&lt;p&gt;Choosing a good partition key is a design-time decision, but validating and maintaining it is an operational one. &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides the visibility that makes this practical at scale.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe9d75c7eb99ede0788a51_kpow-consumers-details.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Within Kpow’s consumer group views, you can inspect per-partition lag across all consumer groups on a topic, which is the most direct signal of key skew in production. When one or two partitions consistently lag behind the rest, the distribution analysis starts there. Kpow’s multi-cluster support means you can compare partition health across environments in a single view rather than running &lt;code&gt;kafka-consumer-groups.sh&lt;/code&gt; queries against each cluster separately.&lt;/p&gt;
&lt;p&gt;For compacted topics, Kpow’s topic inspection surfaces message-level metadata including keys, which makes it straightforward to identify producers sending null-keyed records to compacted topics before &lt;code&gt;NoKeyCompactedTopicRecordsPerSec&lt;/code&gt; metrics are fully instrumented.&lt;/p&gt;
&lt;p&gt;Kpow also supports broker-level health monitoring, so when a hot partition starts manifesting as ISR shrinks or under-replicated partitions, the signal is visible alongside the consumer-group lag data that points to the likely cause.&lt;/p&gt;
&lt;p&gt;You can try Kpow for yourself with a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. It connects to any Kafka cluster and deploys via Docker, Helm, or JAR.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The decisions that matter most, in order of when you make them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Before topic creation&lt;/strong&gt;: choose an immutable, high-cardinality, system-generated identifier. Estimate &lt;code&gt;distinct(key) / num_partitions&lt;/code&gt; and target at least 20. Over-provision partitions; it is much cheaper than migrating later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When building producers&lt;/strong&gt;: standardise the hash function across all client languages. Verify CDC connectors explicitly. Do not use &lt;code&gt;UniformStickyPartitioner&lt;/code&gt; on keyed topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In production&lt;/strong&gt;: monitor per-partition byte rate variance and consumer-group lag skew. Alert on max/avg ratio above 1.5. Alert on &lt;code&gt;NoKeyCompactedTopicRecordsPerSec &amp;gt; 0&lt;/code&gt; for compacted topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When skew appears&lt;/strong&gt;: identify whether it is a data distribution problem or a broker placement problem. For data: composite keys first, then salting, then dedicated topics for intractable cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When migration is necessary&lt;/strong&gt;: new topic, dual-write, validation, cutover. Never change partition count on a live keyed topic in place.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka cluster management: A practical guide for engineers</title><link>https://factorhouse.io/articles/kafka-cluster-management/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-cluster-management/</guid><description>A practical guide to Kafka cluster management: architecture sizing, day-to-day operations, performance tuning, KRaft migration, and monitoring for production clusters.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The decisions you make at cluster creation time, specifically partition counts, replication factor, broker sizing, and KRaft vs ZooKeeper, determine whether you scale cleanly or accumulate operational debt that is difficult to unwind.&lt;/li&gt;
&lt;li&gt;Apache Kafka 4.0 (released March 2025) removed ZooKeeper entirely. KRaft is now the only supported mode. If you are still running ZooKeeper-based Kafka 3.x, your migration path is mandatory.&lt;/li&gt;
&lt;li&gt;The most impactful monitoring signals are under-replicated partitions (URPs), ActiveControllerCount, OfflinePartitionsCount, and consumer lag trend. Alert on these; log everything else.&lt;/li&gt;
&lt;li&gt;Producer compression combined with sensible batching is the highest-leverage performance tuning available without architectural changes.&lt;/li&gt;
&lt;li&gt;Tools like &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; can provide visibility into partition health and URP status, and allow you to manage partition reassignments and leader elections without dropping to the CLI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding Kafka at a conceptual level is one thing. Operating it in production is something else entirely. At a handful of topics and a few brokers, the configuration feels manageable. Once you are running dozens of topics across multiple teams, with different retention requirements, latency SLOs, and replication topologies, the operational surface area expands considerably. &lt;a href=&quot;/articles/kafka-topic-partition-best-practices&quot;&gt;Partition count&lt;/a&gt; decisions become difficult to reverse. Monitoring gaps surface during incidents rather than during planning. Rebalancing a cluster without causing ISR shrinkage requires care. None of this is obvious from the documentation alone.&lt;/p&gt;
&lt;p&gt;This guide covers the practical decisions and operational patterns that matter most when running Kafka clusters at scale: how to size and structure clusters from day one, what to watch for in production, how to tune producers, consumers, and brokers, and how the shift to KRaft has changed the management picture.&lt;/p&gt;
&lt;h2 id=&quot;what-is-kafka-cluster-management&quot;&gt;&lt;strong&gt;What is Kafka cluster management?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kafka cluster management covers the end-to-end operational work of keeping one or more Kafka clusters running correctly: provisioning and sizing brokers, choosing partition strategies and replication settings, managing consumer groups and offsets, handling rolling operations and partition reassignments, monitoring for cluster health, tuning configuration for performance, enforcing access control, and planning for the architectural shifts like the ZooKeeper-to-KRaft migration.&lt;/p&gt;
&lt;p&gt;It is not a single task but a continuous discipline. The configuration choices you make at topic creation time constrain what you can do later. The monitoring infrastructure you build before incidents determines how quickly you can diagnose them. And the operational practices you establish early, things like how you handle rolling restarts, how you reassign partitions, and how you gate cluster mutations, determine whether your cluster remains manageable as it grows.&lt;/p&gt;
&lt;h2 id=&quot;how-architecture-and-sizing-decisions-affect-cluster-management&quot;&gt;&lt;strong&gt;How architecture and sizing decisions affect cluster management&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;start-with-three-brokers-across-three-availability-zones&quot;&gt;&lt;strong&gt;Start with three brokers across three availability zones&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The baseline production configuration is three brokers, one per availability zone, with replication.factor=3 and min.insync.replicas=2. This is the smallest topology that survives a single broker failure while still allowing producers with acks=all to make progress. RF=2 only survives zero failures during maintenance, which is not a reasonable production posture. Note that increasing the replication factor on a live topic adds meaningful network and disk pressure. Get it right at topic creation.&lt;/p&gt;
&lt;p&gt;For storage, the choice is straightforward for latency-sensitive workloads: NVMe/SSD or fast provisioned EBS (gp3 or io2 on AWS). HDD is acceptable only for cold archive workloads where tiered storage is in use. A single historical consumer reading from disk on a shared HDD broker can cause a 43% drop in producer throughput, based on Stanislav Kozlovski’s analysis of KIP-405 storage trade-offs. The memory guidance is consistent across most large operators: 4-8 GB JVM heap, with the remainder of RAM left to the OS page cache.&lt;/p&gt;
&lt;h3 id=&quot;partition-count-is-the-most-consequential-and-least-reversible-decision&quot;&gt;&lt;strong&gt;Partition count is the most consequential and least reversible decision&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For keyed topics, you cannot increase the partition count later without breaking hash(key) % num_partitions ordering. Confluent’s official guidance states up to approximately 4,000 partitions per broker and 200,000 per cluster on ZooKeeper-based deployments. On KRaft, lab tests demonstrated stable operation at 2 million partitions per cluster, though real-world production clusters typically run in the hundreds of thousands.&lt;/p&gt;
&lt;p&gt;For sizing, Confluent’s formula is a reasonable starting point: minimum partitions = max(target_throughput / per_partition_producer_throughput, target_throughput / per_partition_consumer_throughput). On modern hardware with LZ4 compression and acks=all, single-partition producer throughput is typically 10-50 MB/s; consumer throughput is 50-100+ MB/s. Add 2-3x headroom for hot-key skew.&lt;/p&gt;
&lt;p&gt;For small clusters under six brokers, starting at approximately 3x the broker count in partitions is a reasonable rule. For larger clusters above twelve brokers, 2x the broker count tends to work. Do not under-partition either: a single-partition topic caps you at one consumer instance for that topic-level parallelism.&lt;/p&gt;
&lt;h3 id=&quot;cluster-isolation-by-workload-type&quot;&gt;&lt;strong&gt;Cluster isolation by workload type&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;LinkedIn, Netflix, Pinterest, and Cloudflare all operate many smaller clusters rather than a single large one. Netflix explicitly maintains a rule of a maximum of 200 brokers per cluster, preferring more clusters of moderate size. The operational argument is compelling: a single large cluster creates a blast radius that affects every consumer and producer on it. Isolating by traffic class (tracking, logging, metrics, CDC) limits the impact of a misconfigured producer or a runaway topic.&lt;/p&gt;
&lt;h3 id=&quot;multi-cluster-topology-comes-before-tool-selection&quot;&gt;&lt;strong&gt;Multi-cluster topology comes before tool selection&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you are planning for geo-replication or disaster recovery, choose your topology first: active-passive, active-active, or aggregation. Then select the tool. MirrorMaker 2 (MM2) is the open-source option; it runs on Kafka Connect and handles topic replication via MirrorSourceConnector, offset translation via MirrorCheckpointConnector, and heartbeats via MirrorHeartbeatConnector. The main operational catch is that offset sync is periodic (default 60 seconds), so consumers may reprocess up to that interval’s messages on failover. Make your consumers idempotent regardless of which replication tool you use, and test failover at least quarterly. At LinkedIn or Uber scale, MM2 becomes an operational burden; purpose-built tools like Brooklin (LinkedIn) or uReplicator (Uber) were built to handle per-partition error isolation that MM2 cannot provide.&lt;/p&gt;
&lt;p&gt;For Confluent Platform or Cloud users, Cluster Linking provides byte-for-byte replication that preserves offsets exactly, at the cost of vendor lock-in.&lt;/p&gt;
&lt;h2 id=&quot;day-to-day-cluster-operations&quot;&gt;&lt;strong&gt;Day-to-day cluster operations&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;rolling-restarts&quot;&gt;&lt;strong&gt;Rolling restarts&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Rolling restarts are the standard approach for applying broker configuration changes or upgrades without downtime. The procedure is straightforward: drain, restart, wait for ISR recovery, repeat. Before restarting a broker, confirm that UnderReplicatedPartitions is zero cluster-wide. If it is not, wait. After restarting, wait for the broker to fully rejoin the ISR before proceeding to the next node. Restarting a broker while another is still recovering from a previous restart risks losing min.insync.replicas headroom, which will cause producers with acks=all to start receiving NotEnoughReplicasException.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Check cluster-wide under-replicated partitions before each step&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-topics.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--describe --under-replicated-partitions&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;‍&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Graceful broker shutdown&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-server-stop.sh&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;partition-reassignment&quot;&gt;&lt;strong&gt;Partition reassignment&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Adding brokers to a cluster does not automatically rebalance existing partitions. Only new partitions land on new brokers by default. After adding capacity, you need to explicitly reassign partitions using either Cruise Control (LinkedIn’s open-source rebalancing tool, the production standard) or kafka-reassign-partitions.sh. Always throttle reassignments to avoid saturating broker network and triggering ISR shrinkage during the move.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Generate a reassignment plan for specified topics&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-reassign-partitions.sh \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--topics-to-move-json-file topics.json \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--broker-list &quot;0,1,2,3&quot; \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--generate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Execute with throttle (bytes/sec)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-reassign-partitions.sh \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--reassignment-json-file reassign.json \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--throttle 50000000 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--execute&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Verify completion&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-reassign-partitions.sh \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--reassignment-json-file reassign.json \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--verify&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Cruise Control automates this with goal-driven rebalancing: it accounts for rack awareness, leader replica distribution, bytes-in distribution, and disk capacity targets, reducing the manual overhead significantly on clusters with dozens of brokers.&lt;/p&gt;
&lt;h3 id=&quot;consumer-group-and-offset-management&quot;&gt;&lt;strong&gt;Consumer group and offset management&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The most common operational tasks around consumer groups are offset resets (after a deployment rollback, a poison message event, or a schema change) and lag investigation. Disable auto-commit for any consumer doing meaningful processing. The robust pattern is enable.auto.commit=false, process the message, then commit explicitly after the result is durable.&lt;/p&gt;
&lt;p&gt;For investigating consumer group state:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# List all consumer groups&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Describe a specific group, including lag per partition&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-consumer-groups.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--group my-consumer-group --describe&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Reset offsets to latest (requires group to be inactive)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-consumer-groups.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--group my-consumer-group \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--topic my-topic \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--reset-offsets --to-latest \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--execute&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Static membership (KIP-345) is worth enabling for stateful consumers. Set group.instance.id to a stable per-instance identifier. When the consumer restarts, the broker waits for session.timeout.ms before triggering a rebalance, and when the consumer rejoins with the same instance ID, it receives the same partition assignments back. This eliminates rebalances caused by rolling deployments. When using static membership, increase session.timeout.ms to 10-30 minutes to prevent false-positive rebalances.&lt;/p&gt;
&lt;p&gt;For the assignor, switch to CooperativeStickyAssignor (KIP-429, available since Kafka 2.4) to avoid the stop-the-world rebalance behaviour of the eager protocol. On Kafka 4.0+, the next-generation rebalance protocol (KIP-848) is GA and moves rebalance coordination entirely server-side, measured by Instaclustr at up to 20x faster than eager rebalancing.&lt;/p&gt;
&lt;h2 id=&quot;observing-clusters-for-health&quot;&gt;&lt;strong&gt;Observing clusters for health&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Effective cluster monitoring distinguishes between three things: what you need to page on, what warrants a ticket, and what you log and review periodically.&lt;/p&gt;
&lt;h3 id=&quot;alert-worthy-signals&quot;&gt;&lt;strong&gt;Alert-worthy signals&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;These are the metrics that indicate active cluster failure or imminent unavailability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions&lt;/strong&gt;: Sustained non-zero across many brokers indicates a broker is offline or severely degraded. This is the single most critical cluster-level metric.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kafka.controller:type=KafkaController,name=ActiveControllerCount&lt;/strong&gt;: Must be exactly 1 cluster-wide. A value of 0 or greater than 1 requires immediate investigation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kafka.controller:type=KafkaController,name=OfflinePartitionsCount&lt;/strong&gt;: Must be 0. Any offline partitions mean producers and consumers cannot access that data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kafka.cluster:type=Partition,name=UnderMinIsr&lt;/strong&gt;: Partitions below min.insync.replicas. Producers with acks=all cannot write to these partitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer lag trending upward&lt;/strong&gt;: A consumer that is falling further behind over time, not just experiencing a momentary spike, is an application-level incident.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For consumer lag monitoring, LinkedIn’s Burrow is the most robust approach in the open-source space, and often included in &lt;a href=&quot;/articles/best-kafka-management-tools&quot;&gt;Kafka management tools&lt;/a&gt;. Rather than alerting on raw offset distance (which produces false positives during batch catch-up), Burrow evaluates lag over a sliding window of commits and classifies consumer groups as OK, WARNING, or ERROR based on whether the lag trend is decreasing, stable, or growing. This significantly reduces alert fatigue. For more detail on consumer lag monitoring strategies and tooling, see &lt;a href=&quot;/articles/how-to-monitor-kafka-consumer-lag&quot;&gt;how to monitor Kafka consumer lag&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ticket-worthy-signals&quot;&gt;&lt;strong&gt;Ticket-worthy signals&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;These indicate degradation worth investigating before it becomes a page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kafka.server:type=ReplicaManager,name=IsrShrinksPerSec / IsrExpandsPerSec: Frequent ISR shrink/expand events on a single broker usually mean that broker has a disk or network issue. Sustained shrinks across all brokers usually mean aggregate load or a network problem.&lt;/li&gt;
&lt;li&gt;kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent: Below 0.7 (30% utilisation) means performance is degrading. Below 0.5 means the broker is approaching saturation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;monitoring-tooling&quot;&gt;&lt;strong&gt;Monitoring tooling&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The standard production stack is &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;JMX&lt;/a&gt; exported to Prometheus via the JMX Exporter, visualised in Grafana. OpenTelemetry Collector with the JMX receiver is increasingly common as an alternative. For companies already running Datadog, the Kafka integration covers the core broker and consumer metrics. Confluent Control Center is the natural choice on Confluent Platform.&lt;/p&gt;
&lt;p&gt;For visibility into cluster health and &lt;a href=&quot;/articles/best-kafka-monitoring-tools&quot;&gt;Kafka monitoring&lt;/a&gt; more broadly, the key principle from engineering teams at Cloudflare and Pinterest is to avoid alert fatigue: broad threshold alerts on per-topic metrics fire too frequently to be actionable. Focus alerts on the signals listed above, and treat per-topic byte rates and partition commit latencies as log-level observability.&lt;/p&gt;
&lt;h2 id=&quot;tuning-producers-consumers-and-brokers-for-performance&quot;&gt;&lt;strong&gt;Tuning producers, consumers, and brokers for performance&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;producer-configuration&quot;&gt;&lt;strong&gt;Producer configuration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Compression combined with larger batch sizes is the highest-leverage producer tuning available. Cloudflare’s production data is instructive: Snappy plus approximately 1-second batching reduced a metrics topic from 800 Mbps to 170 Mbps. At 100 TB/month, a 3x compression ratio represents a 67% cost reduction on network egress, replication, storage, and consumer transfer.&lt;/p&gt;
&lt;p&gt;Key producer settings:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Compression: lz4 for speed, zstd for ratio&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;compression.type=lz4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Larger batch size (default 16 KB is too small for high throughput)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;batch.size=131072&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Wait for batches to fill before sending&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;linger.ms=10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Durability&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;acks=all&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;enable.idempotence=true&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Compatible with idempotence&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max.in.flight.requests.per.connection=5&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;One critical gotcha: if your producer compression.type does not match the topic’s compression.type, the broker decompresses and recompresses every batch. Set the topic-level configuration to producer to pass through the producer’s codec, or ensure they match explicitly.&lt;/p&gt;
&lt;p&gt;For schema format, Avro and Protobuf compress dramatically better than JSON and have cheaper deserialisation. Shopify and Cloudflare both use Protobuf as their standard wire format.&lt;/p&gt;
&lt;h3 id=&quot;consumer-configuration&quot;&gt;&lt;strong&gt;Consumer configuration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# Process explicitly, commit explicitly&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;enable.auto.commit=false&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Tune for your processing capacity&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max.poll.records=500&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Batch reading for throughput&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch.min.bytes=1024&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Cooperative rebalancing&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Static membership (for stateful consumers)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;group.instance.id=consumer-instance-1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;session.timeout.ms=600000&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Set max.poll.records to a value you can reliably process within max.poll.interval.ms. If processing a batch takes longer than max.poll.interval.ms, the consumer is removed from the group and a rebalance triggers.&lt;/p&gt;
&lt;h3 id=&quot;broker-configuration&quot;&gt;&lt;strong&gt;Broker configuration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# Increase for high-connection environments&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;num.network.threads=8&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Scale with disk count&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;num.io.threads=16&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Larger socket buffers for high-bandwidth networks&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;socket.send.buffer.bytes=1048576&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;socket.receive.buffer.bytes=1048576&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Leave log.flush.interval.messages and log.flush.interval.ms at their defaults. Kafka relies on OS page cache and replication for durability. Forcing fsync eliminates that benefit and significantly reduces throughput.&lt;/p&gt;
&lt;p&gt;At the OS level, set vm.swappiness=1 to prevent the kernel from consuming page cache for swap, vm.max_map_count=262144 or higher (each log segment requires two memory-mapped areas, and the default of 65536 is quickly exhausted on brokers with thousands of partitions), and ensure file descriptor limits are set to 100,000 or above. Mount filesystems with noatime and prefer XFS for large disks.&lt;/p&gt;
&lt;p&gt;JVM heap should be 6-8 GB with G1GC. Never exceed 32 GB or you lose compressed ordinary object pointers, which has a meaningful impact on GC overhead.&lt;/p&gt;
&lt;h2 id=&quot;security-and-multi-tenancy&quot;&gt;&lt;strong&gt;Security and multi-tenancy&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;acls&quot;&gt;&lt;strong&gt;ACLs&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kafka ACLs bind principals (users, service accounts) to resources (topics, consumer groups, clusters) with specific operations (Read, Write, Create, Describe, Alter, Delete). They work well at moderate scale. As the number of teams and topics grows, the number of ACL rules grows correspondingly, and ACL management can become a significant operational overhead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Grant a service account read access to a topic&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-acls.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--add \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--allow-principal User:my-service \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--operation Read \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--topic my-topic&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# List ACLs for a topic&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-acls.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--list --topic my-topic&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;multi-tenancy-patterns&quot;&gt;&lt;strong&gt;Multi-tenancy patterns&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;At the cluster level, multi-tenancy in Kafka typically means some combination of namespace conventions for topics (for example, team-name.topic-name), ACLs restricting access to namespaced resources, and quota configurations to prevent any single tenant from saturating broker network or request handler threads.&lt;/p&gt;
&lt;p&gt;Kafka quotas allow you to set byte-rate limits and request-rate limits per client ID or user principal:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# Set a producer byte-rate quota for a client&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kafka-configs.sh --bootstrap-server localhost:9092 \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--alter \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--add-config &apos;producer_byte_rate=10485760&apos; \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--entity-type clients \&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--entity-name my-producer-client&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For larger organisations, dedicated clusters per business domain or per environment (development, staging, production) provide better blast radius containment than a multi-tenant shared cluster.&lt;/p&gt;
&lt;h2 id=&quot;kraft-and-what-it-changes-for-cluster-management&quot;&gt;&lt;strong&gt;KRaft and what it changes for cluster management&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;why-zookeeper-was-removed&quot;&gt;&lt;strong&gt;Why ZooKeeper was removed&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;ZooKeeper was a separately operated dependency: a second cluster to deploy, secure, monitor, and patch. It also became a scaling bottleneck. Every metadata change, broker registration, partition leader election, ACL update, and topic configuration update, flowed through ZooKeeper. For a 10,000-partition cluster, controller failover required reading all partition metadata from ZooKeeper during initialisation, adding approximately 20 seconds to the unavailability window during an unclean failure.&lt;/p&gt;
&lt;h3 id=&quot;how-kraft-works&quot;&gt;&lt;strong&gt;How KRaft works&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;KRaft replaces ZooKeeper with a Raft-based consensus protocol implemented inside Kafka itself. A dedicated controller quorum, typically three or five nodes, maintains an internal __cluster_metadata topic where each metadata change is written as an event. Brokers consume this metadata stream like a regular Kafka topic. Controller failover is near-instantaneous because the incoming active controller already has all committed metadata in memory. Confluent migrated all Confluent Cloud clusters to KRaft without customer-visible downtime. Aiven migrated 15,000 servers over three months with zero downtime.&lt;/p&gt;
&lt;p&gt;The operational benefits are concrete: Confluent reports approximately 40% reduction in cluster setup time and the ability to scale to millions of partitions in a single cluster (demonstrated at 2 million in lab conditions).&lt;/p&gt;
&lt;h3 id=&quot;migration-path-if-you-are-still-on-zookeeper&quot;&gt;&lt;strong&gt;Migration path if you are still on ZooKeeper&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kafka 4.0 (March 2025) removed ZooKeeper support entirely. If you are running Kafka 3.x with ZooKeeper, your path is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Upgrade to Kafka 3.9 (the bridge release with the most complete migration tooling).&lt;/li&gt;
&lt;li&gt;Provision a dedicated KRaft controller quorum with zookeeper.metadata.migration.enable=true.&lt;/li&gt;
&lt;li&gt;Roll restart brokers in migration mode. The active KRaft controller copies all metadata from ZooKeeper to __cluster_metadata.&lt;/li&gt;
&lt;li&gt;Complete the dual-write phase where KRaft is authoritative and ZooKeeper is a safety net. You can still roll back at this point.&lt;/li&gt;
&lt;li&gt;Finalize: reconfigure brokers for KRaft only. No rollback is possible after this step.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use kafka-migration-check for preflight and status checks, and monitor progress via the JMX MBean kafka.controller:type=KafkaController,name=ZkMigrationState. The final state should be MIGRATION_COMPLETED.&lt;/p&gt;
&lt;p&gt;Budget two to four weeks of dry-run testing in a non-production environment and one to three months for a full production rollout depending on cluster count.&lt;/p&gt;
&lt;p&gt;Key constraints: migration only supports isolated mode (dedicated controllers), not combined mode. JBOD support arrived in Kafka 3.7, so older KRaft versions did not support multiple log directories. Kafka 4.0 clients require brokers at version 2.1 or higher; upgrade all clients before moving brokers to 4.x.&lt;/p&gt;
&lt;h2 id=&quot;managing-topic-partitions-with-kpow&quot;&gt;&lt;strong&gt;Managing topic partitions with Kpow&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provides a UI layer for the partition management operations that are otherwise handled entirely through the CLI. This is particularly useful in environments where you want to limit direct CLI access for most operators, or where you need an audit trail of partition mutations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe9961b0c8180a1e940601_kpow-cluster-management.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;topic-and-partition-creation&quot;&gt;&lt;strong&gt;Topic and partition creation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kpow’s topic creation form lets you specify partition count, replication factor, and any additional configuration values. If you are not sure what configuration values are appropriate, the interface surfaces documentation inline, including the top five most common values currently set across your cluster for each parameter. This can be useful when creating topics that should match the configuration patterns already established for similar topics.&lt;/p&gt;
&lt;p&gt;If you prefer not to grant Kpow mutation permissions but still want the UI for planning, the form generates an equivalent kafka-topics.sh command that updates reactively as you fill in the form.&lt;/p&gt;
&lt;h3 id=&quot;partition-reassignment-and-leader-election&quot;&gt;&lt;strong&gt;Partition reassignment and leader election&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;From the Topics page, Kpow supports partition reassignment at the individual partition level: you select a topic partition, click the reassignment action, and choose target replicas. In-progress reassignments are visible in a dedicated Reassignment tab, where you can also cancel any in-flight reassignment. Full cluster and full topic reassignment support is on the roadmap.&lt;/p&gt;
&lt;p&gt;For leader election, Kpow supports both preferred and unclean election types. Preferred election is the standard operation for rebalancing leader distribution after a rolling restart. Unclean election is a last-resort option that allows a replica not in the ISR to become leader, with the trade-off of potential data loss.&lt;/p&gt;
&lt;h3 id=&quot;urp-detection&quot;&gt;&lt;strong&gt;URP detection&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kpow provides enhanced URP detection that correctly identifies partitions where the in-sync replica count is below the configured replication factor, even when a broker is offline and not visible to the AdminClient. URP totals are displayed on both the Brokers and Topics pages, and if the count is greater than zero, a detailed table expands to list every affected topic and partition. This makes it practical to identify URP scope quickly during an incident without running kafka-topics.sh –under-replicated-partitions manually across multiple brokers.&lt;/p&gt;
&lt;h3 id=&quot;consumer-group-management&quot;&gt;&lt;strong&gt;Consumer group management&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Kpow’s Groups UI handles consumer group offset management across the full range of dimensions: whole-group, host-level, topic-level, and partition-level. Offset resets are scheduled and held until the consumer group reaches an EMPTY state, which prevents modifications to an active group from causing consistency issues. For static consumer groups, you can also remove individual members to trigger a rebalance without waiting for session.timeout.ms to expire.&lt;/p&gt;
&lt;h2 id=&quot;moving-from-reactive-to-proactive-cluster-management&quot;&gt;&lt;strong&gt;Moving from reactive to proactive cluster management&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Most Kafka problems do not arrive suddenly. ISR shrink rates creep up before brokers start failing. Consumer lag trends upward before an application team reports an issue. Request handler idle percentages decline gradually before a broker becomes saturated. The pattern is almost always visible in the metrics before it becomes an incident, provided you are looking in the right place with the right resolution.&lt;/p&gt;
&lt;p&gt;The practical shift toward proactive cluster management involves three things. First, instrumenting the signals that matter at the cluster level, the ones listed in the monitoring section above, and ensuring they flow into an alerting layer before they become visible to end users. Second, running rebalancing and maintenance operations on a schedule rather than in response to imbalance. Tools like Cruise Control are designed to run continuously and maintain goal-based cluster health rather than waiting for manual intervention. Third, maintaining an observation layer that gives you a complete picture of partition health, consumer group state, and broker configuration across all clusters in one place, rather than assembling that picture from multiple CLI commands during an incident.&lt;/p&gt;
&lt;p&gt;If you want to explore what a unified observation layer looks like in practice, Kpow offers a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; that you can connect to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
&lt;h2 id=&quot;checklist-greenfield-cluster-in-2026&quot;&gt;&lt;strong&gt;Checklist: greenfield cluster in 2026&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;For reference, here are the baseline configuration choices validated against the practices of teams at LinkedIn, Pinterest, Netflix, Cloudflare, and Stripe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kafka 4.0+, KRaft only, isolated mode: three dedicated controllers plus three or more brokers across three AZs.&lt;/li&gt;
&lt;li&gt;replication.factor=3, min.insync.replicas=2, acks=all, enable.idempotence=true.&lt;/li&gt;
&lt;li&gt;NVMe/SSD or provisioned EBS; 6-8 GB JVM heap; rest to OS page cache.&lt;/li&gt;
&lt;li&gt;LZ4 producer compression, batch.size=131072, linger.ms=10-20.&lt;/li&gt;
&lt;li&gt;CooperativeStickyAssignor and group.instance.id on every stateful consumer.&lt;/li&gt;
&lt;li&gt;Cruise Control (or equivalent) for rebalancing from day one.&lt;/li&gt;
&lt;li&gt;JMX to Prometheus to Grafana, plus Burrow for consumer lag trend analysis.&lt;/li&gt;
&lt;li&gt;Tiered storage enabled at topic creation for analytics or long-retention topics.&lt;/li&gt;
&lt;li&gt;enable.auto.commit=false; commit offsets explicitly after processing is durable.&lt;/li&gt;
&lt;li&gt;Avro or Protobuf with a Schema Registry.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scale up brokers when sustained BytesInPerSec plus ReplicationBytesInPerSec exceeds 60% of NIC capacity, or when RequestHandlerAvgIdlePercent drops below 0.5. Consider adding a second cluster when approaching 200 brokers in one cluster, when a single noisy tenant repeatedly causes cross-tenant impact, or when regulatory data residency requirements make cluster isolation necessary.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Kafka topic partition best practices</title><link>https://factorhouse.io/articles/kafka-topic-partition-best-practices/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-topic-partition-best-practices/</guid><description>Size Kafka topic partitions correctly from day one. Covers the throughput formula, the keyed topic asymmetry, KRaft-era limits, and operational best practices.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Partitions are the foundational unit of parallelism, ordering, storage, and consumer-group fan-out in Kafka. Every performance and reliability decision you make about a topic flows from how partitions work at the broker level. Getting partition count wrong at topic creation is costly because the recoverable and unrecoverable failure modes are asymmetric: adding partitions is mechanically trivial, but on a keyed topic it permanently breaks ordering guarantees for every downstream consumer.&lt;/p&gt;
&lt;p&gt;This guide covers what partitions actually do, how to size them correctly, what breaks when you get it wrong, and what the most operationally mature Kafka deployments in production actually do.&lt;/p&gt;
&lt;h2 id=&quot;what-kafka-topic-partitions-actually-do&quot;&gt;&lt;strong&gt;What Kafka topic partitions actually do&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A Kafka topic is a logical label. The partition is the real unit of work: a physically replicated, append-only log on disk. Four properties stack on top of that single primitive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ordering.&lt;/strong&gt; Kafka guarantees message order within a single partition only. There is no global order across partitions. This has been true since the original 2011 paper by Kreps, Narkhede and Rao, and it has not changed. If your consumers depend on ordering guarantees, they depend on partition-level ordering, not topic-level ordering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parallelism.&lt;/strong&gt; Producers and brokers write to different partitions fully in parallel. CPU-intensive work like compression scales with partition count. On the consumer side, parallelism is strictly bounded: one consumer thread per partition per consumer group. Extra consumers beyond the partition count sit idle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer-group assignment.&lt;/strong&gt; A group coordinator broker assigns each partition to exactly one consumer in the group. The default assignor since Kafka 3.0 is CooperativeStickyAssignor (KIP-429), which performs incremental rebalancing instead of revoking all partitions from all consumers on every membership change. This does not change the ceiling, but it makes living at the ceiling less operationally painful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log segment mechanics.&lt;/strong&gt; Each partition is a directory on disk. That directory contains a sequence of segment files (.log plus .index and .timeindex companions). The active segment is the only one currently being written to. A segment rolls when it hits log.segment.bytes (default 1 GB) or log.segment.ms (default 7 days). Critically, Kafka holds an open file descriptor to every segment in every partition, including inactive ones. That is the mechanical basis for the “more partitions = more open file descriptors” constraint. Cloudera’s documented formula for minimum file descriptors is (number of partitions) × (partition size / segment size). A starting point of 100,000 file descriptors per broker is the common recommendation from both Cloudera and LinkedIn.&lt;/p&gt;
&lt;h2 id=&quot;kafka-topic-partition-best-practices&quot;&gt;&lt;strong&gt;Kafka topic partition best practices&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;size-partitions-for-two-years-of-peak-throughput-not-todays-traffic&quot;&gt;&lt;strong&gt;Size partitions for two years of peak throughput, not today’s traffic&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The single most consequential partition sizing decision is the time horizon you plan against. Because adding partitions to a keyed topic breaks per-key ordering guarantees permanently, the production pattern at every large-scale Kafka deployment (LinkedIn, Confluent, Shopify, Cloudflare) is to over-provision at topic creation and avoid –alter on keyed topics entirely.&lt;/p&gt;
&lt;p&gt;The canonical formula, from Jun Rao’s 2015 Confluent post and still the foundation of Confluent’s official documentation:&lt;/p&gt;
&lt;p&gt;partitions ≥ max(t/p, t/c)&lt;/p&gt;
&lt;p&gt;Where t is your target throughput, p is per-partition produce throughput, and c is per-partition consume throughput.&lt;/p&gt;
&lt;p&gt;A more complete version for production sizing:&lt;/p&gt;
&lt;p&gt;P_writes  = ceil(W / P_part_safe)        # P_part_safe = 60–70% of measured peak per-partition produce throughput&lt;/p&gt;
&lt;p&gt;P_consume = ceil(W / C_per_consumer)     # consumer compute/IO bound, often 1–5 MB/s in practice&lt;/p&gt;
&lt;p&gt;P_consumers = N_consumers_at_peak        # physical parallelism you need to run&lt;/p&gt;
&lt;p&gt;P = max(P_writes, P_consume, P_consumers) × 1.5–2  # growth headroom&lt;/p&gt;
&lt;p&gt;Round the result up to a number with many divisors: 12, 24, 30, 60 are common choices. Avoid primes because they don’t divide evenly across brokers, which creates leadership imbalance. Document the partition count and the assumptions (peak MB/s, consumer count, retention) on the topic itself. These assumptions are what a future engineer needs when evaluating whether to alter the topic or create a new one.&lt;/p&gt;
&lt;h3 id=&quot;benchmark-per-partition-throughput-in-your-actual-cluster&quot;&gt;&lt;strong&gt;Benchmark per-partition throughput in your actual cluster&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;What is realistic for per-partition throughput? The answer is highly workload-specific, which makes the formula only as good as the input numbers you use.&lt;/p&gt;
&lt;p&gt;Confluent’s published guidance states that a single partition can generally sustain “10s of MB/sec.” LinkedIn’s Kreps benchmark on three commodity machines achieved ~50 MB/s sustained per partition on a six-partition topic. Conduktor’s 2025 guidance puts modern Kafka (3.0+) at 50–100+ MB/s per partition with proper tuning, calling earlier estimates conservative.&lt;/p&gt;
&lt;p&gt;Netflix Keystone, operating at approximately 2 trillion messages per day, uses 0.5–1 MB/s per partition as their planning constant. A 10 MB/s topic gets 10 partitions. The reason the number is conservative relative to what Kafka can physically sustain is that the bottleneck is rarely the broker: it is usually the consumer.&lt;/p&gt;
&lt;p&gt;Uber’s message-queue workload demonstrates this precisely. Their consumer logic involved synchronous RPC calls to payment processors, which limited each partition to approximately one event per second. Achieving 1,000 events/s required 1,000 partitions. The per-partition throughput number in your formula should reflect your slowest consumer path, not your broker’s disk ceiling.&lt;/p&gt;
&lt;p&gt;Run kafka-producer-perf-test.sh with realistic message sizes and acks=all against your actual cluster before committing to a partition count. Take 60–70% of the peak result as your safe planning constant to account for bursts and replication pressure.&lt;/p&gt;
&lt;h3 id=&quot;account-for-replication-factor-in-your-broker-load-estimate&quot;&gt;&lt;strong&gt;Account for replication factor in your broker load estimate&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Replication factor multiplies broker-side cost directly. With RF=3, every partition leader has two follower replicas continuously fetching from it. Producing at 50 MB/s creates 100 MB/s of replication fetch traffic on top of the write.&lt;/p&gt;
&lt;p&gt;Pinterest measured 50% background CPU on a 10,000-partition test cluster with RF=3 and no actual production load, purely from follower fetching. This is the cost that the older “100 partitions per broker” rule of thumb was trying to capture, and it is still present regardless of what version of Kafka you are running.&lt;/p&gt;
&lt;p&gt;Standard configuration for production: RF=3, min.insync.replicas=2, acks=all. RF=2 is the floor; RF=1 is benchmark-only. Increasing RF on a live topic is more disruptive than adding partitions; it introduces immediate network and disk pressure that can affect in-sync replica sets across the cluster. Treat replication factor changes as maintenance operations, not on-the-fly adjustments.&lt;/p&gt;
&lt;p&gt;Rack-awareness (KIP-881, Kafka 3.4+) extends replica placement into the CooperativeStickyAssignor so consumers can prefer same-AZ replicas via follower fetching. At cloud scale, this materially reduces inter-AZ data transfer costs. Pinterest spreads replicas across three availability zones to survive two simultaneous broker failures per cluster.&lt;/p&gt;
&lt;h3 id=&quot;understand-the-consumer-parallelism-ceiling-and-its-current-exceptions&quot;&gt;&lt;strong&gt;Understand the consumer-parallelism ceiling and its current exceptions&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The rule is exact: the number of active consumer threads in one consumer group cannot exceed the number of partitions of the topics it subscribes to. Extras sit idle. They still incur join/leave overhead and hold a slot in the group.&lt;/p&gt;
&lt;p&gt;Under-provisioning consumers is the more common and more visible failure mode: consumer lag rises until you scale up to the partition ceiling. Over-provisioning consumers is silently wasteful and is easy to miss without partition-level lag monitoring.&lt;/p&gt;
&lt;p&gt;Two Kafka features reduce the operational pain of this ceiling without eliminating it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Static group membership&lt;/strong&gt; (group.instance.id, KIP-345) allows a consumer to leave and rejoin within session.timeout.ms without triggering a rebalance. This is the standard pattern for stateful consumers running Kubernetes rolling deployments, where the pod restart cycle would otherwise trigger repeated rebalances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-848&lt;/strong&gt; (default in Kafka 4.0) redesigns the rebalance protocol around a broker-side assignor, eliminating the global rebalance barrier so offsets can commit even mid-rebalance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KIP-932 Share Groups&lt;/strong&gt; (early access in 4.0, preview in 4.1, GA in 4.2) is the first feature that actually relaxes the ceiling itself. Share groups allow multiple consumers per partition with per-record acknowledgement, providing queue semantics on top of Kafka. If your consumer concurrency is bounded by partition count and you cannot add more partitions (because the topic is keyed), share groups are the right next step. They are not yet recommended for production clusters as of 4.1.&lt;/p&gt;
&lt;h3 id=&quot;know-what-breaking-partition-count-actually-breaks&quot;&gt;&lt;strong&gt;Know what breaking partition count actually breaks&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Adding partitions to a topic runs in seconds: kafka-topics.sh –alter –partitions N. The mechanics work. What breaks depends on whether the topic is keyed.&lt;/p&gt;
&lt;p&gt;For topics with null keys or round-robin partitioning, adding partitions is safe. Producers are unaffected; consumer groups rebalance automatically.&lt;/p&gt;
&lt;p&gt;For keyed topics, adding partitions permanently breaks murmur2(key) % numPartitions for every key. Old data stays where it was; Kafka is append-only and immutable. New data with the same key may land on a different partition. From that point forward, per-key ordering guarantees are broken across the alter boundary. Stateful stream processors using Kafka Streams or ksqlDB use the partition as the key/state co-location boundary; RocksDB state stores, changelog topics, and repartition topics are all keyed by partition. Repartitioning the source topic does not reshuffle existing state.&lt;/p&gt;
&lt;p&gt;Compacted topics have an additional failure mode: if a key was previously hashed to partition 2 and now hashes to partition 5, two values exist for the same key in different partitions. Log compaction does not see across partition boundaries, so the “latest value per key” semantic breaks.&lt;/p&gt;
&lt;p&gt;Kafka does not support reducing partition count. This is documented explicitly. Plan upward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The standard pattern for keyed topics that have genuinely outgrown their partition count:&lt;/strong&gt; create a new topic with the desired partition count, dual-write from producers to both, drain consumers off the old topic, then decommission it. Cloudflare, Shopify, and Confluent all follow this pattern. It is operationally heavier but preserves per-key ordering guarantees and stateful consumer correctness.&lt;/p&gt;
&lt;p&gt;For Kafka Streams pipelines, design downstream repartition() operations into the topology so the source topic’s partition count is decoupled from processing parallelism.&lt;/p&gt;
&lt;h3 id=&quot;understand-the-kraft-era-cluster-partition-limits&quot;&gt;&lt;strong&gt;Understand the KRaft-era cluster partition limits&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The ZooKeeper-era rules (4,000 partitions per broker, 200,000 per cluster) were driven by a specific bottleneck: controller failover. When a ZooKeeper controller failed, the new controller had to load the full cluster state from ZooKeeper before serving traffic. With 200,000 partitions, that took 14 seconds in Kafka 1.1. It grew linearly with partition count.&lt;/p&gt;
&lt;p&gt;KRaft (KIP-500, production-ready in 3.3, mandatory in 4.0) replaces ZooKeeper with an event-sourced metadata log. Follower controllers hold the current state in memory. Failover is near-instantaneous regardless of partition count. Confluent’s lab has demonstrated a 2-million-partition cluster running on KRaft. Instaclustr created approximately 600,000 partitions on a single KRaft broker.&lt;/p&gt;
&lt;p&gt;These numbers are not operating targets. They are ceiling demonstrations. The constraints that now bind you are resource-level, not metadata-level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File descriptors:&lt;/strong&gt; two per segment per partition; can blow past default ulimits quickly at high retention and throughput. Minimum ulimit recommendation is 100,000 per broker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vm.max_map_count:&lt;/strong&gt; Kafka memory-maps index files; the Linux default of 65,530 is a hard ceiling at modest partition counts. Set it to 1,000,000 or higher for high-partition-density clusters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JVM heap and OS page cache:&lt;/strong&gt; standard recommendation is 4–8 GB JVM heap, the rest for page cache.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replica-fetcher threads:&lt;/strong&gt; each follower fetches from leaders; more partitions demand more fetcher threads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Producer and consumer client memory:&lt;/strong&gt; Jun Rao’s guidance is to allocate at least a few tens of KB per partition being produced. A 100,000-partition topic can exhaust producer buffer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replication CPU:&lt;/strong&gt; RF=3 essentially triples steady-state broker work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The practical consequence for 2026: if you are on ZooKeeper, the 200K cluster cap is real and you should migrate to KRaft before evaluating partition counts. If you are on KRaft, the previous caps no longer bind you, but the resource costs that motivated the conservative rules still accumulate. Most production clusters still target hundreds to low-thousands of partitions per broker, not millions.&lt;/p&gt;
&lt;h3 id=&quot;choose-numbers-with-good-divisor-properties&quot;&gt;&lt;strong&gt;Choose numbers with good divisor properties&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Avoid partition counts that are prime numbers. Kafka distributes partition leadership across brokers; prime numbers don’t divide evenly, producing leadership imbalance that over-stresses specific brokers.&lt;/p&gt;
&lt;p&gt;Prefer numbers with many divisors: 12, 24, 30, 48, 60. These divide cleanly across 2, 3, 4, 6, and 12 brokers, giving you flexibility as your cluster scales and making rebalancing predictable.&lt;/p&gt;
&lt;p&gt;The “3 × number of brokers” rule of thumb has a kernel of truth: it balances partition leadership at the time of topic creation. It is not a substitute for the throughput formula and it does not account for consumer parallelism requirements.&lt;/p&gt;
&lt;h3 id=&quot;set-cluster-level-guardrails-not-just-per-topic-settings&quot;&gt;&lt;strong&gt;Set cluster-level guardrails, not just per-topic settings&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Broker configuration defaults affect every topic on the cluster. Platform teams should enforce a consistent baseline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RF=3, min.insync.replicas=2, acks=all on all production topics.&lt;/li&gt;
&lt;li&gt;File descriptor ulimit ≥ 100,000 per broker.&lt;/li&gt;
&lt;li&gt;vm.max_map_count ≥ 1,000,000 on Linux brokers.&lt;/li&gt;
&lt;li&gt;4–8 GB JVM heap, remainder for OS page cache.&lt;/li&gt;
&lt;li&gt;CooperativeStickyAssignor and group.instance.id for stateful consumers on Kubernetes.&lt;/li&gt;
&lt;li&gt;Per-partition lag and throughput monitoring, not just per-topic. Consumer group lag at the topic level masks partition skew.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LinkedIn’s engineering team built custom tooling specifically because, as they documented, “Kafka does not natively provide partition throughput metrics.” At scale, per-topic aggregate metrics are not sufficient to detect which partitions are saturated.&lt;/p&gt;
&lt;h3 id=&quot;avoid-common-anti-patterns&quot;&gt;&lt;strong&gt;Avoid common anti-patterns&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Using one partition for global ordering.&lt;/strong&gt; This is almost always the wrong design. Global ordering requires 1 partition, which means 1 broker handles all writes and 1 consumer in any group handles all reads. You need per-key ordering, not global ordering, and a key-based partitioner with enough partitions to distribute load achieves that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hot keys.&lt;/strong&gt; The right partition count does not help you if your key distribution is skewed. One merchant with 90% of payment events in a merchant_id-keyed topic reproduces all the failure modes of a single-partition topic on whichever broker holds that partition. Key selection and partition count are coupled problems. See our separate guide on &lt;a href=&quot;/articles/kafka-partition-key-best-practices&quot;&gt;Kafka partition key best practices&lt;/a&gt; for detailed coverage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treating partition count as set-and-forget.&lt;/strong&gt; Size for one to two years of peak growth, then review quarterly. A topic sized correctly at 1× traffic is almost certainly under-partitioned at 50× traffic, and the fix becomes progressively more disruptive the longer it is deferred.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using null keys when ordering is required.&lt;/strong&gt; When a producer omits message keys, sticky or round-robin partitioning is used. If downstream consumers have ordering assumptions, those assumptions silently break. This is a common bug, particularly when producers are refactored across teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increasing replication factor as a quick fix.&lt;/strong&gt; It works mechanically, but the disk and network pressure shock can trigger ISR shrinks across the cluster. Plan it as a maintenance operation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Running ZooKeeper in 2026.&lt;/strong&gt; ZooKeeper mode was deprecated in Kafka 3.5 and removed in 4.0. If you are still running it, the 200,000-partition cluster cap is a real operational constraint, and you are accumulating tech debt against a hard end-of-life.&lt;/p&gt;
&lt;h2 id=&quot;what-well-known-organisations-actually-do&quot;&gt;&lt;strong&gt;What well-known organisations actually do&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The following is a representative sample of how production Kafka deployments at scale approach partitioning. The common thread is that all of them treat partition count as a design parameter set upfront, not a runtime knob.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; runs 100+ clusters, 4,000+ brokers, 100,000+ topics, and 7+ million partitions handling more than 7 trillion messages per day (2023 figures). They run Cruise Control on every cluster for partition rebalancing and goal-based optimisation. They built custom tooling to emit per-partition throughput metrics specifically because Kafka does not expose them natively, and because partition-count-based load distribution was wrong at their scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Netflix Keystone&lt;/strong&gt; handles approximately 2 trillion messages per day and uses 0.5–1 MB/s per partition as their planning constant. Their architectural principle for partition scaling and configuration changes is instructive: they treat them as simulated failures, failing traffic over to a new cluster rather than reconfiguring the live one. A max of ~200 nodes per cluster; beyond that, they provision a new cluster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shopify&lt;/strong&gt; on Black Friday / Cyber Monday 2024 hit 66 million messages per second peak. Their engineering team explicitly identified partition increases, not larger batch sizes, as the lever to maintain ETL data freshness during traffic spikes. Their CDC architecture creates one Kafka topic per logical database table, partitioned by primary key, deliberately decoupling source database sharding from consumer-side partitioning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Uber&lt;/strong&gt; operates at trillions of messages per day. They hit the hard consumer-parallelism limit when their payment processing consumers could handle approximately one event per second per partition. At 1,000 events/s throughput requirements, that meant 1,000 partitions per topic, and the ZooKeeper-era 200K cluster cap bounded them to roughly 200 topics per cluster at that scale. Their solution (uForwarder) was a push-based gRPC consumer proxy that decoupled consumer concurrency from partition count, anticipating what KIP-932 share groups formalise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt; runs 14 clusters with ~330 nodes. Their public partition lessons: back-of-the-napkin throughput math before a topic reaches production is not optional; partition skew is real and they have had incidents from it; and Snappy compression gave them 2.25× ingress reduction on their highest-throughput topic without increasing producer or consumer CPU.&lt;/p&gt;
&lt;h2 id=&quot;how-kpow-helps-you-manage-kafka-topic-partitions&quot;&gt;&lt;strong&gt;How Kpow helps you manage Kafka topic partitions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Partition decisions span topic creation, ongoing monitoring, and operational intervention when something is off. Kpow covers each of those phases.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fe95d992a57ef02cacfcf7_kpow-topics.avif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topic creation with guardrails.&lt;/strong&gt; Kpow’s topic creation UI lets you set partition count, replication factor, and all topic-level configuration before a topic reaches production. It exposes cluster-default configuration for each option inline and shows the top-5 most common values set across your cluster, which makes it easier to stay consistent with your existing topology. If you want to review a configuration option before setting it (for example, cleanup.policy or compression.type), the documentation accordion surfaces the config item’s type, default, and allowed values without leaving the form.&lt;/p&gt;
&lt;p&gt;If you prefer not to grant Kpow direct mutation access, the Topic Create form generates the equivalent kafka-topics.sh command reactively as you fill it in, which you can pipe directly into your cluster tooling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuration management across topics.&lt;/strong&gt; Kpow’s topic configuration view gives you a filterable table of configuration across all topics in a cluster. You can filter by topic name, config key, source (dynamic vs. default), importance, and whether the item is read-only. Editing a config item brings up the same inline documentation, making it straightforward to review the implications of a change before committing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Under-replicated partition detection.&lt;/strong&gt; Kpow tracks under-replicated partitions (URPs) at both the broker and topic level. Its URP calculation correctly identifies partitions where the in-sync replica count is below the configured replication factor even when a broker is offline and not visible to the AdminClient, a subtle but important distinction for accurate cluster health visibility. URP totals appear on both the Brokers and Topics pages; if the count is above zero, a detailed table lists every affected topic and partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partition replica management.&lt;/strong&gt; From the Topic Details page, Kpow lets you elect preferred or unclean leaders for individual partitions and manage partition reassignments. During a reassignment, you can monitor progress and cancel an in-flight operation from the Reassignment tab. Full cluster-wide reassignment is on the product roadmap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer group lag at partition granularity.&lt;/strong&gt; The consumer group topology view in Kpow shows lag at the group, host, topic, and partition level. This matters because per-topic aggregate lag masks partition skew, a common failure mode where one partition is saturated while others are idle, and the topic-level metric looks acceptable. Kpow also surfaces lag for empty consumer groups, which is relevant when a poison message has taken a consumer group offline and you need to reset offsets before restarting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Offset management.&lt;/strong&gt; When you need to reset, clear, or skip offsets at the partition level, including after a partition count change or a consumer migration to a new topic version, Kpow handles this from the Consumers workflow UI. Offset mutations are scheduled and execute once the consumer group reaches the EMPTY state, which prevents accidental offset resets on running consumers.&lt;/p&gt;
&lt;p&gt;If you want to see how Kpow fits into your Kafka operations workflow, you can start a &lt;a href=&quot;/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Partition count is a design-time decision with long operational consequences. The practical guidance distills to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Size for one to two years of peak throughput using the throughput formula with measured per-partition numbers from your actual cluster.&lt;/li&gt;
&lt;li&gt;Round up to a number with good divisor properties; avoid primes.&lt;/li&gt;
&lt;li&gt;On keyed topics, over-provision rather than alter. Adding partitions breaks per-key ordering permanently.&lt;/li&gt;
&lt;li&gt;RF=3, min.insync.replicas=2, acks=all is the standard baseline. Treat replication factor changes as maintenance operations.&lt;/li&gt;
&lt;li&gt;The ZooKeeper-era 200K partition cluster cap is a KRaft-era non-issue. The resource constraints (file descriptors, mmap limits, replication CPU) that motivated conservative rules are not.&lt;/li&gt;
&lt;li&gt;Monitor lag at the partition level. Topic-level aggregate lag hides partition skew.&lt;/li&gt;
&lt;li&gt;When a keyed topic has genuinely outgrown its partition count, create a new topic and dual-write rather than altering.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>The Complete Guide to Kafka Change Data Capture (CDC)</title><link>https://factorhouse.io/articles/kafka-cdc-change-data-capture/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-cdc-change-data-capture/</guid><description>Learn how to implement change data capture with Kafka using Debezium. Includes working PostgreSQL CDC examples, architecture patterns, and monitoring.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Change Data Capture (CDC) tracks row-level changes in a database and publishes them as events. When paired with Apache Kafka, CDC turns your databases into real-time event streams without modifying application code or adding polling overhead.&lt;/p&gt;
&lt;p&gt;This guide covers CDC fundamentals, compares the dominant implementation patterns, walks through a working PostgreSQL-to-Kafka setup, and shows where CDC fits in a broader data mesh architecture.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CDC captures row-level changes from databases and streams them as events to Kafka, eliminating the latency and resource waste of traditional batch ETL.&lt;/li&gt;
&lt;li&gt;Log-based CDC reads the database transaction log directly, capturing all changes including deletes with near-zero impact on production workloads.&lt;/li&gt;
&lt;li&gt;Debezium and Kafka Connect JDBC are the two main CDC patterns for Kafka; Debezium is the preferred choice for most production use cases that require complete change capture and low latency.&lt;/li&gt;
&lt;li&gt;CDC fits naturally in enterprise data mesh architectures, letting domain teams publish database changes as Kafka events without modifying application code.&lt;/li&gt;
&lt;li&gt;Keeping CDC pipelines running reliably at scale requires operational tooling: Kpow provides real-time visibility into connector health, consumer lag, and automatic restarts across your entire Kafka environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-change-data-capture-cdc&quot;&gt;What is Kafka change data capture (CDC)?&lt;/h2&gt;
&lt;p&gt;Traditional data integration relies on batch ETL: extract everything from the source, transform it, load it into the target. This approach has well-known problems. It introduces latency (hours or days), wastes resources by re-reading unchanged data, and puts load on the source database during extraction windows.&lt;/p&gt;
&lt;p&gt;CDC solves these problems by capturing only the rows that changed (inserts, updates, deletes) and streaming them as individual events. The source database is read through its internal replication mechanism, typically the write-ahead log (WAL), so the impact on production workloads is minimal.&lt;/p&gt;
&lt;p&gt;In practice, CDC gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sub-second propagation of database changes to downstream systems&lt;/li&gt;
&lt;li&gt;A complete, ordered history of every mutation, useful for audit trails and temporal queries&lt;/li&gt;
&lt;li&gt;Decoupled producers and consumers, since changes flow through Kafka topics rather than direct database connections&lt;/li&gt;
&lt;li&gt;The ability to rebuild derived datastores by replaying the change log from a known offset&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For real-time data pipelines, CDC eliminates the gap between “data at rest” and “data in motion.” Your analytical systems, search indexes, caches, and microservices all see changes as they happen rather than in delayed batches.&lt;/p&gt;
&lt;h2 id=&quot;log-based-versus-query-based-cdc-for-kafka&quot;&gt;Log-based versus query-based CDC for Kafka&lt;/h2&gt;
&lt;p&gt;There are two fundamental approaches to capturing changes from a database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log-based CDC&lt;/strong&gt; reads the database’s internal transaction log (WAL in PostgreSQL, binlog in MySQL, redo log in Oracle). This is the preferred method. The database already writes these logs for crash recovery and replication, so CDC reads them with near-zero overhead. Log-based CDC captures all changes including deletes, preserves the exact ordering of transactions, and works without schema modifications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Query-based CDC&lt;/strong&gt; periodically polls the source table using SQL queries, typically filtering by a &lt;code&gt;last_modified&lt;/code&gt; timestamp column. This is simpler to set up but has significant limitations: it cannot reliably detect deletes, requires a timestamp or incrementing column on every tracked table, misses rapid intermediate changes between polls, and puts read load on the source database.&lt;/p&gt;
&lt;p&gt;For most production use cases, log-based CDC is the correct choice. Query-based CDC can work for simple, append-only tables where deletes are not a concern.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-kafka-cdc-connector&quot;&gt;Choosing a Kafka CDC connector&lt;/h2&gt;
&lt;p&gt;The two most common ways to implement CDC with Kafka are Debezium (log-based) and the Kafka Connect JDBC Source Connector (query-based). They serve different purposes.&lt;/p&gt;
&lt;h3 id=&quot;debezium-cdc-for-kafka&quot;&gt;Debezium CDC for Kafka&lt;/h3&gt;
&lt;p&gt;Debezium is an open-source CDC platform built on Kafka Connect. It reads database transaction logs directly and produces change events to Kafka topics.&lt;/p&gt;
&lt;p&gt;Key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reads WAL/binlog/redo logs directly; no schema changes required on source tables&lt;/li&gt;
&lt;li&gt;Captures inserts, updates, and deletes with full before/after images of each row&lt;/li&gt;
&lt;li&gt;Provides exactly-once semantics when combined with Kafka’s transactional features&lt;/li&gt;
&lt;li&gt;Supports PostgreSQL, MySQL, MongoDB, SQL Server, Oracle, Db2, and others&lt;/li&gt;
&lt;li&gt;Emits a structured envelope containing the operation type, before state, after state, source metadata, and transaction info&lt;/li&gt;
&lt;li&gt;Handles initial snapshots of existing data before switching to log streaming&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;kafka-connect-jdbc&quot;&gt;Kafka Connect JDBC&lt;/h3&gt;
&lt;p&gt;The JDBC Source Connector uses SQL queries to poll for changes at a configured interval.&lt;/p&gt;
&lt;p&gt;Key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requires a &lt;code&gt;timestamp&lt;/code&gt; column, an &lt;code&gt;incrementing&lt;/code&gt; column, or both to detect changes&lt;/li&gt;
&lt;li&gt;Cannot capture deletes (the row is gone before the next poll)&lt;/li&gt;
&lt;li&gt;Simpler setup: no database-level replication configuration needed&lt;/li&gt;
&lt;li&gt;Higher latency, bounded by the poll interval&lt;/li&gt;
&lt;li&gt;Puts periodic query load on the source database&lt;/li&gt;
&lt;li&gt;Works with any JDBC-compatible database&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;debezium-versus-kafka-connect-jdbc&quot;&gt;Debezium versus Kafka Connect JDBC&lt;/h3&gt;
&lt;p&gt;Use Debezium when you need complete change capture (including deletes), low latency, minimal source database impact, and accurate ordering of changes. This covers most CDC use cases.&lt;/p&gt;
&lt;p&gt;Use the JDBC Source Connector when you have a simple append-only or update-only table, cannot configure database-level replication permissions, or need a quick prototype before investing in log-based CDC infrastructure.&lt;/p&gt;
&lt;h2 id=&quot;working-example-postgresql-to-kafka-with-debezium&quot;&gt;Working Example: PostgreSQL to Kafka with Debezium&lt;/h2&gt;
&lt;p&gt;The following walks through a complete setup using PostgreSQL’s logical replication and Debezium.&lt;/p&gt;
&lt;h3 id=&quot;step-1-configure-postgresql-for-logical-replication&quot;&gt;Step 1: Configure PostgreSQL for Logical Replication&lt;/h3&gt;
&lt;p&gt;Edit &lt;code&gt;postgresql.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;wal_level &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; logical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;max_replication_slots &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;max_wal_senders &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a replication user and grant permissions:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; ROLE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; debezium &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;WITH&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; REPLICATION&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; LOGIN&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;dbz_password&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;GRANT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; USAGE&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; ON&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SCHEMA&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; public &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TO&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; debezium;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;GRANT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SELECT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; ON&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; ALL&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; TABLES&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; IN&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SCHEMA&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; public &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TO&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; debezium;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ALTER&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; DEFAULT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; PRIVILEGES&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; IN&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SCHEMA&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; public &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;GRANT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SELECT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; ON&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; TABLES&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; TO&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; debezium;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a publication for the tables you want to track:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; PUBLICATION&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; dbz_publication &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;FOR&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; orders, customers, products;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-2-deploy-kafka-connect-with-debezium&quot;&gt;Step 2: Deploy Kafka Connect with Debezium&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;docker-compose.yml&lt;/code&gt; snippet for the Kafka Connect worker with the Debezium PostgreSQL connector plugin:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  image&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: debezium&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;2.5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  ports&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;8083:8083&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  environment&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9092&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    GROUP_ID&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: connect&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    CONFIG_STORAGE_TOPIC&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: connect&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;configs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    OFFSET_STORAGE_TOPIC&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: connect&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;offsets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    STATUS_STORAGE_TOPIC&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: connect&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    KEY_CONVERTER&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: org.apache.kafka.connect.json.JsonConverter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    VALUE_CONVERTER&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: org.apache.kafka.connect.json.JsonConverter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  depends_on&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; kafka&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; postgres&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-3-register-the-debezium-connector&quot;&gt;Step 3: Register the Debezium Connector&lt;/h3&gt;
&lt;p&gt;Submit the connector configuration via the Kafka Connect REST API:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;pg-cdc-connector&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;config&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;connector.class&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;io.debezium.connector.postgresql.PostgresConnector&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;database.hostname&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;database.port&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;5432&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;database.user&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;debezium&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;database.password&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dbz_password&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;database.dbname&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;app_db&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;topic.prefix&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cdc&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;schema.include.list&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;table.include.list&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;public.orders,public.customers,public.products&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;publication.name&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dbz_publication&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;slot.name&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;debezium_slot&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;plugin.name&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;pgoutput&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;publication.autocreate.mode&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;filtered&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;snapshot.mode&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;initial&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;tombstones.on.delete&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;key.converter&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;org.apache.kafka.connect.json.JsonConverter&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;value.converter&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;org.apache.kafka.connect.json.JsonConverter&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;key.converter.schemas.enable&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;value.converter.schemas.enable&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;curl &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;X&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; POST&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//localhost:8083/connectors \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;H&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Content-Type: application/json&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;d @pg&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;cdc&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;connector.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-4-verify-the-pipeline&quot;&gt;Step 4: Verify the Pipeline&lt;/h3&gt;
&lt;p&gt;Check the connector status:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;curl &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;s &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//localhost:8083/connectors/pg-cdc-connector/status | jq .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the connector and its tasks in &lt;code&gt;RUNNING&lt;/code&gt; state. Insert a row into the source table:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;INSERT&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; INTO&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; orders&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (customer_id, product_id, quantity, total)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;VALUES&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;149.97&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consume from the CDC topic to see the change event:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;consumer &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;bootstrap&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;server &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9092&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;topic cdc.public.orders \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;beginning \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;max&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;messages &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting event will contain an envelope with &lt;code&gt;op: &quot;c&quot;&lt;/code&gt; (create), the &lt;code&gt;after&lt;/code&gt; field with the full row state, and source metadata including the LSN (Log Sequence Number), transaction ID, and timestamp.&lt;/p&gt;
&lt;p&gt;A Debezium change event for an insert looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;before&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;after&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1001&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;customer_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;product_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;quantity&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;total&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;149.97&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;source&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;version&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;2.5.0.Final&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;connector&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;postgresql&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cdc&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;ts_ms&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1704067200000&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;db&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;app_db&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;schema&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;table&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;orders&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;lsn&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;234567890&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;txId&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;5678&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;op&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;ts_ms&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1704067200123&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For updates, &lt;code&gt;op&lt;/code&gt; is &lt;code&gt;&quot;u&quot;&lt;/code&gt; and both &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; are populated. For deletes, &lt;code&gt;op&lt;/code&gt; is &lt;code&gt;&quot;d&quot;&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; is null.&lt;/p&gt;
&lt;h2 id=&quot;cdc-events-in-kafka-structure-and-consumption&quot;&gt;&lt;strong&gt;CDC events in Kafka: structure and consumption&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Every change Debezium captures is published to Kafka as a structured CDC event. Understanding the shape of these events is essential for building reliable consumers.&lt;/p&gt;
&lt;p&gt;Each CDC event in Kafka carries an envelope with four key fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;op&lt;/strong&gt; indicates the operation type: &lt;code&gt;&quot;c&quot;&lt;/code&gt; for create (insert), &lt;code&gt;&quot;u&quot;&lt;/code&gt; for update, &lt;code&gt;&quot;d&quot;&lt;/code&gt; for delete, and &lt;code&gt;&quot;r&quot;&lt;/code&gt; for a read during the initial snapshot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;before&lt;/strong&gt; contains the full row state before the change. This is &lt;code&gt;null&lt;/code&gt; for inserts and snapshot reads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;after&lt;/strong&gt; contains the full row state after the change. This is &lt;code&gt;null&lt;/code&gt; for deletes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;source&lt;/strong&gt; provides metadata from the database, including the connector name, table, transaction ID, log sequence number (LSN), and commit timestamp.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consumers that process CDC events in Kafka typically follow one of two patterns. Filter-and-apply consumers inspect the &lt;code&gt;op&lt;/code&gt; field and route each event to the appropriate handler (insert, update, or delete logic). Event-sourcing consumers treat the full stream as an ordered log and reconstruct state by replaying events from a known offset, making it straightforward to rebuild derived data stores after failures or schema migrations.&lt;/p&gt;
&lt;p&gt;When publishing CDC events to Kafka, topic naming conventions matter for downstream discoverability. A common pattern is &lt;code&gt;{prefix}.{schema}.{table}&lt;/code&gt;, such as &lt;code&gt;cdc.public.orders&lt;/code&gt;. This makes it easy for consumers to subscribe to specific tables and for schema registry tooling to apply per-topic compatibility rules.&lt;/p&gt;
&lt;h2 id=&quot;kafka-cdc-in-an-enterprise-data-mesh-architecture&quot;&gt;Kafka CDC in an enterprise data mesh architecture&lt;/h2&gt;
&lt;p&gt;In a data mesh, domain teams own their data products and expose them through well-defined interfaces. CDC fits naturally into this model because it allows teams to publish their database changes as domain events without building custom event-producing application code.&lt;/p&gt;
&lt;p&gt;A typical architecture looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/698bce78a2f6e9c7775be06c_cdc-architecture-diagram.png&quot; alt=&quot;CDC Architecture Diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each domain team runs a Debezium connector for its database. The change events land in Kafka topics namespaced by domain (e.g., &lt;code&gt;cdc.orders.*&lt;/code&gt;). A schema registry enforces contracts on the event format. Downstream teams consume from these topics to build analytics stores, search indexes, materialized views, audit logs, or ML feature stores.&lt;/p&gt;
&lt;p&gt;This decouples data producers from consumers entirely. The Orders team does not need to know that the Analytics team reads their changes, and vice versa. Kafka acts as the durable, replayable log that connects the domains.&lt;/p&gt;
&lt;p&gt;In practice, you also need governance over topic naming, schema evolution rules, data classification, and access controls. CDC topics often carry sensitive data (customer PII, payment details), so encryption, RBAC, and audit logging are essential at the Kafka layer.&lt;/p&gt;
&lt;h2 id=&quot;common-kafka-cdc-pitfalls&quot;&gt;Common Kafka CDC pitfalls&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Replication slot growth.&lt;/strong&gt; If a consumer falls behind or a connector stops, PostgreSQL retains WAL segments for the replication slot indefinitely. This can fill the disk. Monitor &lt;code&gt;pg_replication_slots&lt;/code&gt; and set &lt;code&gt;max_slot_wal_keep_size&lt;/code&gt; as a safety limit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema evolution.&lt;/strong&gt; When a source table schema changes (added columns, type changes), the CDC events change shape. Use a schema registry with compatibility checks (backward, forward, or full) to prevent breaking downstream consumers. Debezium integrates with Confluent Schema Registry and Apicurio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Snapshot handling.&lt;/strong&gt; On first startup, Debezium takes an initial snapshot of the existing data. For large tables, this can take hours and produce a burst of messages. Plan capacity accordingly and consider using &lt;code&gt;snapshot.mode=no_data&lt;/code&gt; if you only need changes going forward.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ordering guarantees.&lt;/strong&gt; Debezium produces events in commit order within a single table partition. If you need strict ordering across tables or across partitions of the same table, you need to handle this in your consumer logic or use single-partition topics (which limits throughput).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tombstone records.&lt;/strong&gt; Debezium emits a tombstone (null value) after a delete event by default. This is required for Kafka log compaction to work correctly, removing the key entirely from the compacted topic. Make sure your consumers handle null values.&lt;/p&gt;
&lt;h2 id=&quot;monitoring-kafka-cdc-pipelines-with-kpow&quot;&gt;Monitoring Kafka CDC pipelines with Kpow&lt;/h2&gt;
&lt;p&gt;CDC connectors are long-running processes, and they fail silently more often than you would like. A connector task might enter a &lt;code&gt;FAILED&lt;/code&gt; state because of a WAL slot issue, a schema change, or a network partition. Without monitoring, these failures go unnoticed until downstream systems start serving stale data.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you real-time visibility into Kafka Connect clusters alongside the rest of your Kafka infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/698bd45f6ecab252fb70d09c_kpow-connector-state.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reviewing Kafka Connect connector and task status in Kpow&lt;/p&gt;
&lt;p&gt;Specifically for CDC pipelines, Kpow helps with:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connector and task health.&lt;/strong&gt; Kpow surfaces the status of every connector and task (RUNNING, PAUSED, FAILED, UNASSIGNED) in a single view. You can set up Prometheus alerts via Kpow’s metrics endpoint to fire when a connector task enters an error state, so your team gets notified immediately rather than discovering the failure through downstream symptoms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer lag tracking.&lt;/strong&gt; The CDC topics produced by Debezium are consumed by downstream services. Kpow tracks consumer group lag across all topics and partitions, letting you see at a glance whether any consumer is falling behind. For CDC, rising lag means your derived datastore is diverging from the source of truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatic connector restarts.&lt;/strong&gt; Kpow can automatically restart failed connectors at configurable intervals. You specify which connectors to auto-restart (by exact name or wildcard pattern), and Kpow monitors them at one-minute intervals. All restart actions are logged in the audit trail and can be forwarded to Slack for team visibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Operational controls.&lt;/strong&gt; From Kpow’s UI or OpenAPI-based REST API, you can pause, resume, restart, or delete connectors, inspect task stack traces when errors occur, and view or edit connector configurations. This gives your on-call engineers a single interface for triaging CDC issues without needing to hit the Kafka Connect REST API directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-cluster support.&lt;/strong&gt; If you run CDC connectors across multiple Kafka clusters or environments (dev, staging, production), Kpow manages them all from a single instance with role-based access controls and audit logging.&lt;/p&gt;
&lt;p&gt;CDC is one of those patterns where the setup is the easy part. Keeping it running reliably at scale, across multiple source databases and dozens of connectors, is where the operational complexity lives. Kpow reduces that complexity by consolidating connector health, consumer lag, topic throughput, and cluster metrics into a single tool.&lt;/p&gt;
&lt;p&gt;You can try Kpow with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30 day trial&lt;/a&gt; or explore the full documentation at &lt;a href=&quot;https://docs.factorhouse.io/&quot;&gt;docs.factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;kafka-cdc-faq&quot;&gt;&lt;strong&gt;Kafka CDC FAQ&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;is-a-managed-cdc-or-custom-kafka-consumers-less-work&quot;&gt;&lt;strong&gt;Is a managed CDC or custom Kafka consumers less work?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Managed CDC tools like Debezium handle snapshotting, ordering, schema changes, and replication slot management out of the box. Custom Kafka consumers require you to implement all of this logic yourself. For most teams, managed CDC is significantly less work to operate at scale.&lt;/p&gt;
&lt;h3 id=&quot;is-it-possible-to-implement-cdc-without-managing-kafka-infrastructure&quot;&gt;&lt;strong&gt;Is it possible to implement CDC without managing Kafka infrastructure?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Yes. Managed Kafka services such as Confluent Cloud, Amazon MSK, or Aiven for Apache Kafka handle cluster provisioning, scaling, and operations for you. Debezium also offers a cloud service. This lets teams focus on CDC pipeline configuration rather than infrastructure management.&lt;/p&gt;
&lt;h3 id=&quot;how-to-deal-with-schema-change-runbook-and-pre-deploy-contract-checks&quot;&gt;&lt;strong&gt;How to deal with “schema-change runbook and pre-deploy contract checks”?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Register every CDC topic with a schema registry and enforce compatibility rules (backward or full) before deploying schema changes. This ensures downstream consumers are notified of changes in advance. Pair this with a runbook covering steps to update connectors and consumers when breaking changes are unavoidable.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Enhanced Under-Replicated Partition Detection in Kpow</title><link>https://factorhouse.io/articles/enhanced-urp-detection/</link><guid isPermaLink="true">https://factorhouse.io/articles/enhanced-urp-detection/</guid><description>Kpow now offers enhanced under-replicated partition (URP) detection for more accurate Kafka health monitoring. Our improved calculation correctly identifies URPs even when brokers are offline, providing a true, real-time view of your cluster&apos;s fault tolerance. This helps you proactively mitigate risks and ensure data durability.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In a distributed system like Apache Kafka, data is partitioned and replicated across multiple brokers to ensure high availability and fault tolerance. A partition is considered an &lt;strong&gt;under-replicated partition (URP)&lt;/strong&gt; when the number of in-sync replicas (ISRs) falls below the configured replication factor. This scenario can arise from various issues, including broker failures, network partitions, or high load on specific brokers.&lt;/p&gt;
&lt;p&gt;The presence of URPs is a significant concern as it indicates a degradation in your topics’ fault tolerance. If another broker fails before the cluster recovers, you risk permanent data loss. A key challenge in Kafka management is accurately detecting these URPs in real-time, especially during common operational events like a broker failure. Standard monitoring methods can sometimes lag, creating a temporary but dangerous blind spot where a cluster appears healthy even though its resilience has been compromised.&lt;/p&gt;
&lt;p&gt;This is precisely the challenge that Kpow’s enhanced URP detection is designed to solve. By providing a more accurate and immediate assessment of your cluster’s true fault tolerance, this feature delivers significant benefits. It gives you the confidence to act quickly on reliable data, the ability to proactively mitigate risks before they escalate, and ultimately, the power to ensure the resilience and durability of your critical data pipelines.&lt;em&gt;&lt;strong&gt;‍&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Enhancement of URP detection is implemented in Release 94.5. For an overview of all the changes, check out the release note:&lt;/strong&gt;&lt;/em&gt; &lt;a href=&quot;https://factorhouse.io/blog/releases/94-5/&quot;&gt;&lt;em&gt;&lt;strong&gt;Release 94.5: New Factor House docs, enhanced data inspection, and URP &amp;amp; KRaft improvements&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;a href=&quot;https://factorhouse.io/blog/releases/94-5/&quot;&gt;&lt;em&gt;&lt;strong&gt;Release 94.5: New Factor House docs, enhanced data inspection, and URP &amp;amp; KRaft improvements&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;enhanced-calculation-for-more-accurate-health-monitoring&quot;&gt;Enhanced calculation for more accurate health monitoring&lt;/h2&gt;
&lt;p&gt;Ensuring the fault tolerance of your Kafka clusters requires a precise and accurate count of under-replicated partitions. A challenging but common operational scenario can arise where a broker becomes unavailable, yet the overall cluster health status does not immediately reflect this change. This can mask a critical degradation in data durability, leading operations teams to believe their cluster is healthier than it actually is. Making decisions based on this incomplete information can delay necessary interventions.&lt;/p&gt;
&lt;p&gt;To provide a more reliable and trustworthy view, Kpow has enhanced its calculation for under-replicated partitions. Instead of calculating replication status by iterating through each &lt;em&gt;broker&lt;/em&gt;—a method that can be incomplete if a broker is offline and unreachable—our new calculation iterates directly through every &lt;em&gt;topic-partition&lt;/em&gt; defined in the cluster.&lt;/p&gt;
&lt;p&gt;This partition-centric approach provides a more comprehensive and authoritative view of the cluster’s state. It is precisely this change that allows the system to correctly detect partitions with fewer in-sync replicas than the configured replication factor, &lt;strong&gt;even when brokers are offline and not reported by the AdminClient.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This enhancement ensures that Kpow’s health monitoring is a precise reflection of your cluster’s real-time condition. It gives you the confidence to trust the metrics you see and act decisively to investigate and resolve replication issues, thereby maintaining a resilient and robust system.&lt;/p&gt;
&lt;h2 id=&quot;surfacing-urp-details-in-kpow&quot;&gt;Surfacing URP details in Kpow&lt;/h2&gt;
&lt;p&gt;This vital health information, now powered by our more accurate calculation, continues to be clearly presented on both the &lt;strong&gt;Brokers&lt;/strong&gt; and &lt;strong&gt;Topics&lt;/strong&gt; pages of the user interface. We’ve retained this dual perspective as it remains essential for diagnosing problems from different angles—whether you’re investigating a single problematic broker or assessing the health of a critical application’s topic.&lt;/p&gt;
&lt;p&gt;On both pages, summary statistics displays the total number of under-replicated partitions. If this count is greater than zero, it serves as an immediate visual alert. A detailed table automatically appears, listing all affected topics along with their specific URPs. This allows you to quickly identify which topics are at risk and gather the necessary context to restore full replication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On the Brokers Page:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e387aeab3c7c526b925e_urp-brokers.png&quot; alt=&quot;URP - Brokers&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On the Topics Page:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e387aeab3c7c526b925b_urp-topics.png&quot; alt=&quot;URP - Topics&quot;&gt;&lt;/p&gt;
&lt;p&gt;To further strengthen monitoring and alerting capabilities, new Prometheus metrics have been introduced to track under-replicated partitions. These metrics integrate seamlessly with your existing observability stack and provide more granular insights for automated alerting and historical trend analysis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;broker_urp&lt;/strong&gt;: The total number of under replicated topic partitions belonging to this broker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic_urp&lt;/strong&gt;: The total number of under replicated partitions belonging to this topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic_urp_total&lt;/strong&gt;: The total number of under replicated partitions of all topics in the Kafka cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Accurate and timely detection of under-replicated partitions (URPs) is fundamental to maintaining a resilient and reliable Apache Kafka cluster. With its enhanced calculation, Kpow provides a more precise and immediate understanding of your cluster’s health by correctly identifying replication issues, particularly in scenarios involving broker failures. This enhanced detection, combined with detailed visibility in the Kpow UI and new Prometheus metrics for automated alerting, empowers you to proactively address replication issues, mitigate the risk of data loss, and ensure the continuous high performance of your real-time data pipelines. This feature update reaffirms Kpow’s commitment to providing comprehensive and intuitive tooling for Kafka management and monitoring.&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>How to monitor Kafka consumer lag: 5 options</title><link>https://factorhouse.io/articles/how-to-monitor-kafka-consumer-lag/</link><guid isPermaLink="true">https://factorhouse.io/articles/how-to-monitor-kafka-consumer-lag/</guid><description>Learn what Kafka consumer lag is, why it occurs, and how to monitor it using built-in tools, custom solutions, and Kafka monitoring platforms.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kafka consumer lag is one of the most common operational signals teams rely on to understand whether their event-driven systems are healthy. It is also one of the most common misunderstandings. When teams refer to consumer lag operationally, it means the following to them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consumer lag represents how far a consumer group is behind the latest data in a Kafka topic.&lt;/li&gt;
&lt;li&gt;Lag is not inherently bad, but unbounded or growing lag usually points to a system issue.&lt;/li&gt;
&lt;li&gt;Monitoring lag effectively requires understanding partitions, offsets, and consumer group behavior.&lt;/li&gt;
&lt;li&gt;There are multiple ways to monitor consumer lag, ranging from built-in tooling to dedicated monitoring products.&lt;/li&gt;
&lt;li&gt;Reducing lag often involves addressing downstream constraints, not Kafka itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are seeing lag increase, stay flat, or behave unexpectedly, it is not always obvious whether you are looking at a real problem, a temporary backlog, or a misleading metric. This article explains what Kafka consumer lag is, why it happens, and several practical ways to monitor it.&lt;/p&gt;
&lt;h2 id=&quot;what-is-kafka-consumer-lag&quot;&gt;What is Kafka consumer lag?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69eabd6c985782c5a82af4eb_consumer-lag.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Kafka consumer lag is the difference between:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the latest offset written to a partition (the &lt;em&gt;log end offset&lt;/em&gt;), and&lt;/li&gt;
&lt;li&gt;the offset most recently committed by a consumer group for that partition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practical terms, lag indicates how many records a consumer group has yet to process. If a topic partition has a latest offset of 10,000 and the consumer group has committed offset 9,500, the lag for that partition is 500.&lt;/p&gt;
&lt;p&gt;Lag is tracked per partition, but it is often aggregated to give a topic-level or consumer-group-level view.&lt;/p&gt;
&lt;p&gt;It is important to note that lag is not a measure of time. A lag of 10,000 messages could represent milliseconds of work or several hours, depending on message size, processing cost, and consumer throughput.&lt;/p&gt;
&lt;h2 id=&quot;causes-of-consumer-lag&quot;&gt;Causes of consumer lag&lt;/h2&gt;
&lt;p&gt;Consumer lag occurs whenever producers write data faster than consumers can process it. This can happen for many reasons, including the following.&lt;/p&gt;
&lt;h3 id=&quot;insufficient-consumer-capacity&quot;&gt;Insufficient consumer capacity&lt;/h3&gt;
&lt;p&gt;If there are fewer consumer instances than partitions, some partitions cannot be processed in parallel and will inevitably fall behind. Lag can also build up when consumers are under-provisioned in terms of CPU, memory, or I/O, even if the number of instances matches the partition count. In these cases, consumers may technically be healthy but unable to sustain the required throughput.&lt;/p&gt;
&lt;h3 id=&quot;slow-downstream-systems&quot;&gt;Slow downstream systems&lt;/h3&gt;
&lt;p&gt;Kafka promotes decoupling, but consumers frequently interact with external systems such as databases, APIs, or object storage. When these downstream dependencies become slow or intermittently unavailable, consumer processing time increases and messages spend longer waiting to be handled. Over time, this reduced throughput shows up as growing lag, even if the Kafka cluster itself is operating normally.&lt;/p&gt;
&lt;h3 id=&quot;rebalances-and-restarts&quot;&gt;Rebalances and restarts&lt;/h3&gt;
&lt;p&gt;Consumer group rebalances temporarily pause consumption while partitions are reassigned. Occasional rebalances are expected, but repeated rebalances—often caused by frequent deployments, crashes, or unstable group membership—can significantly reduce effective processing time. In these situations, lag may fluctuate or accumulate despite sufficient consumer capacity.&lt;/p&gt;
&lt;h3 id=&quot;uneven-partition-load&quot;&gt;Uneven partition load&lt;/h3&gt;
&lt;p&gt;Kafka distributes records by partition, but traffic is rarely uniform across them. A small number of partitions may receive a disproportionate share of messages, creating so-called “hot partitions.” Even when overall throughput appears healthy, lag can accumulate on these partitions and increase end-to-end processing latency for downstream systems.&lt;/p&gt;
&lt;h3 id=&quot;intentional-backlog&quot;&gt;Intentional backlog&lt;/h3&gt;
&lt;p&gt;In some scenarios, consumer lag is expected and planned for. Batch-oriented consumers, replay jobs, or backfill processes often operate behind the head of the log by design. In these cases, lag reflects scheduling or workflow decisions rather than a system failure and should be interpreted in that context.&lt;/p&gt;
&lt;h2 id=&quot;benefits-of-consumer-lag-monitoring&quot;&gt;Benefits of consumer lag monitoring&lt;/h2&gt;
&lt;p&gt;Monitoring consumer lag provides early signals that your system is under pressure. Unlike error logs or alerts triggered after a failure, lag typically increases before users notice an issue.&lt;/p&gt;
&lt;p&gt;Effective lag monitoring helps you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;detect throughput mismatches between producers and consumers&lt;/li&gt;
&lt;li&gt;identify which consumer groups are affected&lt;/li&gt;
&lt;li&gt;understand whether lag is temporary or growing over time&lt;/li&gt;
&lt;li&gt;correlate processing delays with deployments or infrastructure changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When combined with partition-level visibility and operational context, lag metrics can also help distinguish between expected backlogs and genuine processing bottlenecks. For example, a brief spike during a rebalance may be benign, while steadily increasing lag after a deployment can point to a configuration or capacity issue. Used correctly, lag becomes a diagnostic tool rather than just a number on a dashboard, supporting investigation and decision-making instead of generating noise.&lt;/p&gt;
&lt;h2 id=&quot;how-to-monitor-kafka-consumer-lag&quot;&gt;How to monitor Kafka consumer lag&lt;/h2&gt;
&lt;p&gt;There are several ways to monitor consumer lag, each with different levels of effort and visibility.&lt;/p&gt;
&lt;h3 id=&quot;1-using-a-kafka-ui-like-kpow&quot;&gt;1. Using a Kafka UI like Kpow&lt;/h3&gt;
&lt;p&gt;Tools such as &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt; provide consumer group visibility out of the box, including per-partition lag, historical trends, rebalance activity, and group state transitions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69eabd056fbc54e8cd87a1cd_kpow-consumer-lag.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Kpow connects directly to a Kafka cluster and automatically discovers brokers, topics, partitions, and consumer groups. A key differentiator of Kpow is that it generates metrics internally without relying on JMX. This means the data it exposes is consistent across Kafka resources, avoiding the gaps and inconsistencies that often arise from JMX-based scraping pipelines. For teams integrating Kafka observability into Grafana dashboards, this makes Kpow a high-fidelity telemetry source - the approach is described in detail in &lt;a href=&quot;/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to lag visualisation, Kpow provides operational context that is difficult to reconstruct from metrics alone, such as rebalance history and consumer group lifecycle events. This helps distinguish between temporary backlogs caused by rebalances and sustained lag driven by throughput constraints.&lt;/p&gt;
&lt;p&gt;Kpow also exposes administrative capabilities commonly used during troubleshooting, including consumer group offset inspection and reset operations. These actions are available directly in the UI and documented in the &lt;a href=&quot;https://docs.factorhouse.io/kpow/management&quot;&gt;Kpow management guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see an example of consumer group exploration in the &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;publicly available demo instance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For teams that want to explore this approach, Kpow is available with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;2-using-kafkas-command-line-tools&quot;&gt;2. Using Kafka’s command-line tools&lt;/h3&gt;
&lt;p&gt;Kafka ships with built-in scripts for inspecting consumer group offsets, such as:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;consumer&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;groups.sh \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;bootstrap&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;server broker&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9092&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;describe \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    --&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;group my&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;consumer&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;group&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output shows committed offsets, log end offsets, and lag per partition. While useful for ad-hoc inspection, it is not well suited to continuous monitoring or historical analysis.&lt;/p&gt;
&lt;h3 id=&quot;3-using-jmx-metrics&quot;&gt;3. Using JMX metrics&lt;/h3&gt;
&lt;p&gt;Kafka exposes consumer metrics via JMX, including lag-related measurements. Teams often scrape these metrics into Prometheus and visualize them in Grafana.&lt;/p&gt;
&lt;p&gt;This approach offers flexibility but requires careful configuration, metric selection, and alert tuning. It also shifts the responsibility for correctness and interpretation onto the engineering team.&lt;/p&gt;
&lt;h3 id=&quot;4-building-custom-consumer-instrumentation&quot;&gt;4. Building custom consumer instrumentation&lt;/h3&gt;
&lt;p&gt;Some teams instrument lag tracking directly within their consumers by periodically querying committed offsets and log end offsets using the Kafka Admin API.&lt;/p&gt;
&lt;p&gt;This can provide fine-grained control, but it introduces additional complexity and maintenance overhead. In practice, most teams reserve this approach for specialized scenarios.&lt;/p&gt;
&lt;h3 id=&quot;5-cloud-managed-monitoring-integrations&quot;&gt;5. Cloud-managed monitoring integrations&lt;/h3&gt;
&lt;p&gt;Managed Kafka offerings such as Amazon MSK integrate with CloudWatch, exposing offset and lag-related metrics at the service level. These integrations can be helpful but often lack the granularity needed for diagnosing consumer behavior at the application level.&lt;/p&gt;
&lt;h2 id=&quot;best-practices-for-monitoring-consumer-lag&quot;&gt;Best practices for monitoring consumer lag&lt;/h2&gt;
&lt;h3 id=&quot;monitor-trends-not-just-absolute-values&quot;&gt;Monitor trends, not just absolute values&lt;/h3&gt;
&lt;p&gt;A stable lag that remains constant over time may be acceptable, depending on your use case. A steadily increasing lag almost always requires investigation. Set up trend-based alerts that fire when lag has been growing continuously for a defined period - for example, increasing for more than five minutes - rather than alerting on any non-zero value. This reduces alert fatigue while catching genuine problems early.&lt;/p&gt;
&lt;h3 id=&quot;watch-lag-by-partition&quot;&gt;Watch lag by partition&lt;/h3&gt;
&lt;p&gt;Aggregate lag can hide partition-level issues. A single slow or hot partition can impact processing latency even if total group lag looks manageable. Ensure your monitoring system exposes per-partition lag so you can identify which partitions are falling behind and investigate the cause - whether that is skewed message distribution, a slow consumer instance, or an upstream producer spike.&lt;/p&gt;
&lt;h3 id=&quot;correlate-lag-with-deployments-and-rebalances&quot;&gt;Correlate lag with deployments and rebalances&lt;/h3&gt;
&lt;p&gt;Sudden changes in lag often coincide with consumer restarts, rebalances, or configuration changes. Observability is strongest when metrics are combined with deployment events and group state changes. Tools that surface rebalance history alongside lag trends make it significantly easier to diagnose whether a lag spike is transient or structural.&lt;/p&gt;
&lt;h3 id=&quot;avoid-alerting-on-every-spike&quot;&gt;Avoid alerting on every spike&lt;/h3&gt;
&lt;p&gt;Short-lived spikes are common during rebalances or traffic bursts and are often self-correcting within seconds. Alerts should focus on sustained lag growth rather than momentary increases. Using a rolling window or rate-of-change metric - rather than a raw threshold - typically produces more actionable alerts. Reserve high-priority alerts for situations where lag exceeds a threshold and remains elevated for a meaningful duration.&lt;/p&gt;
&lt;h2 id=&quot;how-to-reduce-consumer-lag&quot;&gt;How to reduce consumer lag&lt;/h2&gt;
&lt;h3 id=&quot;scale-consumers-horizontally&quot;&gt;Scale consumers horizontally&lt;/h3&gt;
&lt;p&gt;Adding consumer instances increases parallelism, as long as there are enough partitions to support it. Each partition in Kafka can only be assigned to one consumer within a group, so the maximum useful parallelism is bounded by the partition count. If you have already scaled to match partition count and lag persists, the bottleneck is likely in processing logic or downstream dependencies rather than consumer capacity.&lt;/p&gt;
&lt;h3 id=&quot;optimize-consumer-processing&quot;&gt;Optimize consumer processing&lt;/h3&gt;
&lt;p&gt;Profiling message handling, batching operations, and reducing synchronous calls can significantly improve throughput. Common gains come from reducing per-message latency through asynchronous I/O, pre-fetching lookups, or processing records in micro-batches rather than one at a time. Even modest improvements in per-message processing time can translate to substantial lag reduction at scale.&lt;/p&gt;
&lt;h3 id=&quot;tune-kafka-fetch-and-commit-settings&quot;&gt;Tune Kafka fetch and commit settings&lt;/h3&gt;
&lt;p&gt;Configuration such as max.poll.records, fetch.min.bytes, and commit frequency can affect how efficiently consumers process data. Increasing max.poll.records allows consumers to fetch more messages per poll cycle, which can improve throughput in high-volume scenarios. However, increasing this value also extends the time between commits, so it should be balanced against your at-least-once delivery requirements and processing latency targets.&lt;/p&gt;
&lt;h3 id=&quot;address-downstream-bottlenecks&quot;&gt;Address downstream bottlenecks&lt;/h3&gt;
&lt;p&gt;Lag is often a symptom of slow databases, APIs, or storage systems. Improving these dependencies - through connection pooling, caching, batching writes, or upgrading infrastructure - can have a larger impact on lag reduction than any Kafka-side tuning. Always profile your consumer to understand where time is being spent before making changes.&lt;/p&gt;
&lt;h2 id=&quot;common-misconceptions-about-consumer-lag&quot;&gt;Common misconceptions about consumer lag&lt;/h2&gt;
&lt;p&gt;Consumer lag is widely referenced, but often misinterpreted. A few common misunderstandings are worth addressing directly.&lt;/p&gt;
&lt;h3 id=&quot;lag-is-not-a-reliability-indicator-on-its-own&quot;&gt;Lag is not a reliability indicator on its own&lt;/h3&gt;
&lt;p&gt;High lag does not automatically mean messages are being lost or skipped. Kafka retains data independently of consumer progress. A consumer group can fall behind and still process every record correctly once it catches up, provided retention limits are not exceeded.&lt;/p&gt;
&lt;p&gt;Conversely, low or zero lag does not guarantee correctness. Consumers can commit offsets prematurely, encounter silent processing failures, or drop messages downstream while still appearing “caught up” from Kafka’s perspective.&lt;/p&gt;
&lt;p&gt;Lag should be interpreted alongside error rates, commit behaviour, and downstream outcomes.&lt;/p&gt;
&lt;h3 id=&quot;lag-can-decrease-without-work-being-done&quot;&gt;Lag can decrease without work being done&lt;/h3&gt;
&lt;p&gt;Lag shrinking does not always mean consumers are processing records faster. During rebalances, offset resets, or consumer group recreation, committed offsets can move forward abruptly. This can make lag appear to recover even though records were skipped or reassigned.&lt;/p&gt;
&lt;p&gt;If lag drops suddenly without a corresponding increase in consumer throughput, it is worth verifying offset commit patterns and rebalance history.&lt;/p&gt;
&lt;h3 id=&quot;aggregate-lag-hides-operational-detail&quot;&gt;Aggregate lag hides operational detail&lt;/h3&gt;
&lt;p&gt;Most dashboards default to total or average lag across a topic. While useful at a glance, these aggregates can obscure issues in individual partitions or consumer instances. A single partition with sustained lag is often enough to increase end-to-end processing delay for dependent systems. Monitoring at the partition level is essential for accurate diagnosis.&lt;/p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;Kafka consumer lag is a simple metric that carries a lot of operational context. Understanding what it represents, why it changes, and how to monitor it effectively can make the difference between reactive firefighting and proactive system management.&lt;/p&gt;
&lt;p&gt;If you want to explore consumer lag monitoring with a dedicated Kafka UI, you can &lt;a href=&quot;/products/kpow&quot;&gt;try Kpow with a free 30-day trial&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Guides</category><author>Gaurav Bhatt</author></item><item><title>Integrate Kpow with WarpStream</title><link>https://factorhouse.io/articles/integrate-kpow-with-warpstream/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-warpstream/</guid><description>Integrate Kpow with WarpStream in minutes. Gain unified visibility and control over your BYOC Kafka data plane and Schema Registry through our market-leading engineering console.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.warpstream.com/&quot;&gt;WarpStream&lt;/a&gt; is a Kafka-compatible, BYOC (Bring Your Own Cloud) streaming data platform built directly on top of object storage (such as AWS S3). It delivers high durability, exceptional elasticity, and significant cost savings by eliminating the need for local disks and traditional broker replication.&lt;/p&gt;
&lt;p&gt;WarpStream operates on a highly efficient split architecture: a &lt;strong&gt;Control Plane&lt;/strong&gt; (managed by WarpStream) handles cluster lifecycles, IAM, and metadata, while your &lt;strong&gt;Data Plane&lt;/strong&gt; (the Agents deployed in your own cloud) handles the actual streaming traffic.&lt;/p&gt;
&lt;p&gt;While WarpStream eliminates the infrastructure headache of managing Kafka brokers, engineering teams still need a dedicated, intuitive interface to monitor, explore, and manage their Kafka data in real-time. That is where Kpow comes in. By leveraging standard Kafka protocols, Kpow connects directly to your Data Plane endpoints and BYOC Schema Registry, delivering a unified, single-pane-of-glass experience without the need for proprietary plugins or sidecars.&lt;/p&gt;
&lt;p&gt;Kpow connects natively to a wide range of Kafka vendors and managed service providers. See our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider&quot;&gt;Kafka Providers documentation&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to WarpStream, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running WarpStream Data Plane:&lt;/strong&gt; Your WarpStream Agents must be deployed and running in your cloud environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Connection Details:&lt;/strong&gt; Your Virtual Cluster Bootstrap URL (exposed via your Data Plane, &lt;em&gt;not&lt;/em&gt; the Control Plane URL).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Authentication:&lt;/strong&gt; Your SASL username and password (generated in the WarpStream Console).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WarpStream Schema Registry (Optional):&lt;/strong&gt; The Schema Registry URL and its associated credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to WarpStream is using Docker.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific cluster details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;WarpStream Kafka&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;&amp;#x3C;WARPSTREAM_VIRTUAL_CLUSTER_BOOTSTRAP_URL&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;PLAIN&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&apos;org.apache.kafka.common.security.plain.PlainLoginModule required username=&quot;&amp;#x3C;SASL_USERNAME&gt;&quot; password=&quot;&amp;#x3C;SASL_PASSWORD&gt;&quot;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; REPLICATION_FACTOR=&quot;1&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; NUM_PARTITIONS=&quot;1&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cluster Authentication:&lt;/strong&gt; This guide demonstrates connecting using &lt;code&gt;SASL/PLAIN&lt;/code&gt;, which is the standard for credentials generated in the WarpStream Console. Kpow also fully supports &lt;strong&gt;SCRAM, Mutual TLS (mTLS), and OAuthBearer&lt;/strong&gt; for cluster authentication. For a detailed walkthrough, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/warpstream&quot;&gt;WarpStream provider guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal Topic Configuration (Crucial):&lt;/strong&gt; Because WarpStream’s diskless architecture writes directly to object storage rather than utilizing traditional broker replication, the replication factor acts as a logical durability configuration. You &lt;strong&gt;must&lt;/strong&gt; configure Kpow to use &lt;code&gt;REPLICATION_FACTOR=&quot;1&quot;&lt;/code&gt;. We also highly recommend setting &lt;code&gt;NUM_PARTITIONS=&quot;1&quot;&lt;/code&gt; to conserve your WarpStream logical partition limits, as Kpow’s internal telemetry topics do not require multi-partition compute fanout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, open a browser and navigate to &lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000&lt;/a&gt;. You will immediately see your WarpStream topics, consumer groups, and cluster telemetry.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69fa84067c907d47f915ec2f_kpow-warpstream.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;If you use WarpStream’s BYOC Schema Registry, you can seamlessly integrate it into Kpow by appending the following environment variables to your deployment command.&lt;/p&gt;
&lt;h3 id=&quot;warpstream-schema-registry&quot;&gt;WarpStream Schema Registry&lt;/h3&gt;
&lt;p&gt;WarpStream’s Schema Registry requires Basic Authentication (&lt;code&gt;USER_INFO&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;By default, Kpow uses an optimized “Version 2” observation strategy that queries the bulk schema listing endpoint (&lt;code&gt;/schemas&lt;/code&gt;). Depending on your WarpStream BYOC version and deployment, this bulk endpoint may not be implemented (which will return a 404 Not Found error in Kpow’s logs). If you experience this, you may need to instruct Kpow to fall back to the legacy “Version 1” strategy (&lt;code&gt;/subjects&lt;/code&gt;) by setting SCHEMA_REGISTRY_OBSERVATION_VERSION=“1”. See the &lt;a href=&quot;https://docs.factorhouse.io/kpow/configuration/schema-registry#observation-version&quot;&gt;Schema Registry configuration docs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;WarpStream Schema Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;&amp;#x3C;SCHEMA_REGISTRY_URL&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_AUTH=&quot;USER_INFO&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_USER=&quot;&amp;#x3C;SCHEMA_USERNAME&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_PASSWORD=&quot;&amp;#x3C;SCHEMA_PASSWORD&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_OBSERVATION_VERSION=&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;managed-data-pipelines&quot;&gt;Managed Data Pipelines&lt;/h3&gt;
&lt;p&gt;WarpStream’s “Managed Data Pipelines” (their Kafka Connect alternative) is not currently supported by Kpow.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters, we recommend using our official Helm Charts. You can store your Aiven &lt;code&gt;ca.pem&lt;/code&gt; file as a Kubernetes Secret and securely mount it into the Kpow pod.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your WarpStream Data Plane. By using standard Kafka protocols and respecting WarpStream’s unique diskless architecture configurations, you can unify your virtual clusters and Schema Registry environments in minutes.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your WarpStream integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Introduction to Factor House Local</title><link>https://factorhouse.io/articles/intro-to-factor-house-local/</link><guid isPermaLink="true">https://factorhouse.io/articles/intro-to-factor-house-local/</guid><description>Jumpstart your journey into modern data engineering with Factor House Local. Explore pre-configured Docker environments for Kafka, Flink, Spark, and Iceberg, enhanced with enterprise-grade tools like Kpow and Flex. Our hands-on labs guide you step-by-step, from building your first Kafka client to creating a complete data lakehouse and real-time analytics system. It&apos;s the fastest way to learn, prototype, and build sophisticated data platforms.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/factorhouse/factorhouse-local&quot;&gt;&lt;strong&gt;Factor House Local&lt;/strong&gt;&lt;/a&gt; is a collection of pre-configured Docker Compose environments that demonstrate modern data platform architectures. Each setup is purpose-built around a specific use case and incorporates widely adopted technologies such as Kafka, Flink, Spark, Iceberg, and Pinot. These environments are further enhanced by enterprise-grade tools from Factor House: &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow&lt;/strong&gt;&lt;/a&gt;, for Kafka management and control, and &lt;a href=&quot;https://factorhouse.io/flex/&quot;&gt;&lt;strong&gt;Flex&lt;/strong&gt;&lt;/a&gt;, for seamless integration with Flink.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7cf7f7a570e17c74fe23f_factorhouse-local.png&quot; alt=&quot;Factor House Local&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;data-stack&quot;&gt;Data Stack&lt;/h2&gt;
&lt;h3 id=&quot;kafka-development--monitoring-with-kpow&quot;&gt;Kafka Development &amp;amp; Monitoring with Kpow&lt;/h3&gt;
&lt;p&gt;This stack provides a comprehensive, locally deployable &lt;strong&gt;Apache Kafka environment&lt;/strong&gt; designed for robust development, testing, and operations. It utilizes Confluent Platform components, featuring a high-availability 3-node Kafka cluster, Zookeeper, Schema Registry for data governance, and Kafka Connect for data integration.&lt;/p&gt;
&lt;p&gt;The centerpiece of the stack is &lt;strong&gt;Kpow&lt;/strong&gt; (by Factorhouse), an enterprise-grade management and observability toolkit. Kpow offers a powerful web UI that provides deep visibility into brokers, topics, and consumer groups. Key features include real-time monitoring, advanced data inspection using kJQ (allowing complex queries across various data formats like Avro and Protobuf), and management of Schema Registry and Kafka Connect. Kpow also adds critical enterprise features such as Role-Based Access Control (RBAC), data masking/redaction for sensitive information, and audit logging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ideal For:&lt;/strong&gt; Building and testing microservices, managing data integration pipelines, troubleshooting Kafka issues, and enforcing data governance in event-driven architectures.&lt;/p&gt;
&lt;h3 id=&quot;unified-analytics-platform-flex-flink-spark-iceberg--hive-metastore&quot;&gt;Unified Analytics Platform (Flex, Flink, Spark, Iceberg &amp;amp; Hive Metastore)&lt;/h3&gt;
&lt;p&gt;This architecture establishes a modern &lt;strong&gt;Data Lakehouse&lt;/strong&gt; that seamlessly integrates real-time stream processing and large-scale batch analytics. It eliminates data silos by allowing both &lt;strong&gt;Apache Flink&lt;/strong&gt; (for streaming) and &lt;strong&gt;Apache Spark&lt;/strong&gt; (for batch) to operate on the same data.&lt;/p&gt;
&lt;p&gt;The foundation is built on &lt;strong&gt;Apache Iceberg&lt;/strong&gt; tables stored in MinIO (S3-compatible storage), providing ACID transactions and schema evolution. A &lt;strong&gt;Hive Metastore&lt;/strong&gt;, backed by PostgreSQL, acts as the unified catalog for both Flink and Spark. The PostgreSQL instance is also configured for Change Data Capture (CDC), enabling real-time synchronization from transactional databases into the lakehouse.&lt;/p&gt;
&lt;p&gt;The stack includes &lt;strong&gt;Flex&lt;/strong&gt; (by Factorhouse), an enterprise toolkit for managing and monitoring Apache Flink, offering enhanced security, multi-tenancy, and deep insights into Flink jobs. A Flink SQL Gateway is also included for interactive queries on live data streams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ideal For:&lt;/strong&gt; Unified batch and stream analytics, real-time ETL, CDC pipelines from operational databases, fraud detection, and interactive self-service analytics on a single source of truth.&lt;/p&gt;
&lt;h3 id=&quot;apache-pinot-real-time-olap-cluster&quot;&gt;Apache Pinot Real-Time OLAP Cluster&lt;/h3&gt;
&lt;p&gt;This stack deploys the core components of &lt;strong&gt;Apache Pinot&lt;/strong&gt;, a distributed OLAP (Online Analytical Processing) datastore specifically engineered for &lt;strong&gt;ultra-low-latency analytics&lt;/strong&gt; at high throughput. Pinot is designed to ingest data from both batch sources (like S3) and streaming sources (like Kafka) and serve analytical queries with millisecond response times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ideal For:&lt;/strong&gt; Powering real-time, interactive dashboards; user-facing analytics embedded within applications (where immediate feedback is crucial); anomaly detection; and rapid A/B testing analysis.&lt;/p&gt;
&lt;h3 id=&quot;centralized-observability--data-lineage&quot;&gt;Centralized Observability &amp;amp; Data Lineage&lt;/h3&gt;
&lt;p&gt;This stack provides a complete solution for understanding both system health and data provenance. It combines &lt;strong&gt;Marquez&lt;/strong&gt;, the reference implementation of the &lt;strong&gt;OpenLineage&lt;/strong&gt; standard, with the industry-standard monitoring suite of &lt;strong&gt;Prometheus, Grafana, and Alertmanager&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At its core, &lt;strong&gt;OpenLineage&lt;/strong&gt; enables automated data lineage tracking for Kafka, Flink, and Spark workloads by providing a standardized API for emitting metadata about jobs and datasets. &lt;strong&gt;Marquez&lt;/strong&gt; consumes these events to build a living, interactive map of your data ecosystem. This allows you to trace how datasets are created and consumed, making it invaluable for impact analysis and debugging. The &lt;strong&gt;Prometheus&lt;/strong&gt; stack complements this by collecting time-series metrics from all applications, visualizing them in &lt;strong&gt;Grafana&lt;/strong&gt; dashboards, and using &lt;strong&gt;Alertmanager&lt;/strong&gt; to send proactive notifications about potential system issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ideal For:&lt;/strong&gt; Tracking data provenance, performing root cause analysis for data quality issues, monitoring the performance of the entire data platform, and providing a unified view of both data lineage and system health.&lt;/p&gt;
&lt;h2 id=&quot;factor-house-local-labs&quot;&gt;Factor House Local Labs&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7cf7f7a570e17c74fe23c_fh-local-labs.png&quot; alt=&quot;Factor House Local Labs&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/fh-local-labs&quot;&gt;&lt;strong&gt;Factor House Local labs&lt;/strong&gt;&lt;/a&gt; are a series of 12 hands-on tutorials designed to guide developers through building real-time data pipelines and analytics systems. The labs use a common dataset of &lt;strong&gt;&lt;code&gt;orders&lt;/code&gt;&lt;/strong&gt; from a Kafka topic to demonstrate a complete, end-to-end workflow, from data ingestion to real-time analytics.&lt;/p&gt;
&lt;p&gt;The labs are organized around a few key themes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lab 1 - Streaming with Confidence:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn to produce and consume Avro data using Schema Registry. This lab helps you ensure data integrity and build robust, schema-aware Kafka streams.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lab 2 - Building Data Pipelines with Kafka Connect:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discover the power of Kafka Connect! This lab shows you how to stream data from sources to sinks (e.g., databases, files) efficiently, often without writing a single line of code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Labs 3, 4, 5 - From Events to Insights:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unlock the potential of your event streams! Dive into building real-time analytics applications using powerful stream processing techniques. You’ll work on transforming raw data into actionable intelligence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Labs 6, 7, 8, 9, 10 - Streaming to the Data Lake:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build modern data lake foundations. These labs guide you through ingesting Kafka data into highly efficient and queryable formats like Parquet and Apache Iceberg, setting the stage for powerful batch and ad-hoc analytics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Labs 11, 12 - Bringing Real-Time Analytics to Life:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;See your data in motion! You’ll construct reactive client applications and dashboards that respond to live data streams, providing immediate insights and visualizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, the labs provide a practical, production-inspired journey, showing how to leverage Kafka, Flink, Spark, Iceberg, and Pinot together to build sophisticated, real-time data platforms.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Factor House Local is more than just a collection of Docker containers; it represents a holistic learning and development ecosystem for modern data engineering.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;pre-configured stacks&lt;/strong&gt; serve as the ready-to-use “what,” providing the foundational architecture for today’s data platforms. The &lt;strong&gt;hands-on labs&lt;/strong&gt; provide the practical “how,” guiding users step-by-step through building real-world data pipelines that solve concrete problems.&lt;/p&gt;
&lt;p&gt;By bridging the gap between event streaming (Kafka), large-scale processing (Flink, Spark), modern data storage (Iceberg), and low-latency analytics (Pinot), Factor House Local demystifies the complexity of building integrated data systems. Furthermore, the inclusion of enterprise-grade tools like Kpow and Flex demonstrates how to operate these systems with the observability, control, and security required for production environments.&lt;/p&gt;
&lt;p&gt;Whether you are a developer looking to learn new technologies, an architect prototyping a new design, or a team building the foundation for your next data product, Factor House Local provides the ideal starting point to accelerate your journey.&lt;/p&gt;
</content:encoded><category>Company</category><author>Factor House</author></item><item><title>KIP-1150 Diskless Topics: Rethinking Storage and Cloud Costs in Kafka</title><link>https://factorhouse.io/articles/kip-1150-diskless-topics-explained/</link><guid isPermaLink="true">https://factorhouse.io/articles/kip-1150-diskless-topics-explained/</guid><description>Discover how Kafka&apos;s KIP-1150 Diskless Topics aim to bring cloud-native scalability and cost-efficiency by natively utilizing object storage, and what it means for your streaming architecture.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Operating Apache Kafka in public clouds at massive scale has traditionally meant grappling with high infrastructure costs, specifically around block storage and inter-Availability Zone (AZ) network transfer. While the introduction of Tiered Storage (KIP-405) helped offload historical data, active replication still forces engineers to pay a premium for high-performance disks and network bandwidth.&lt;/p&gt;
&lt;p&gt;Recently accepted as a consensus document, &lt;strong&gt;KIP-1150: Diskless Topics&lt;/strong&gt; represents a major architectural pivot for the Apache Kafka community. By proposing native object storage for active segments, Diskless Topics aim to fundamentally change how data is stored and replicated. If successfully implemented, this architecture will bring true cloud-native scalability and cost-efficiency to Kafka.&lt;/p&gt;
&lt;p&gt;Blending object storage semantics with real-time streaming requires a shift in operational thinking. In this article, we will explore what KIP-1150 proposes, the cloud infrastructure problems it targets, how it aligns with broader industry trends, and the new monitoring complexities teams will need to manage as the design becomes a reality.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduction in cross-AZ costs:&lt;/strong&gt; Diskless topics bypass standard broker-to-broker replication, significantly reducing network fees. However, this trades replication costs for object storage API and transfer fees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“Diskless” is like “Serverless”:&lt;/strong&gt; It does not mean zero disks. Brokers will still use disks for KRaft metadata, caching, and batch metadata, but user data is written through to highly durable object storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost vs. Latency tradeoff:&lt;/strong&gt; Operators will have the ability to choose between low-latency classic block-storage topics and highly cost-optimized diskless topics on a per-topic basis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New monitoring horizons:&lt;/strong&gt; Abstracting storage away from local disks into remote object storage requires entirely new visibility strategies for caching, ingestion engines, and object store performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;background-shift-to-decoupled-architecture&quot;&gt;Background: Shift to Decoupled Architecture&lt;/h2&gt;
&lt;p&gt;To fully understand the necessity of KIP-1150, we must look at three converging forces reshaping the data landscape. The most immediate catalyst is the financial friction between traditional Kafka architecture and modern cloud economics. Beyond pure cost optimization, this evolution is in line with the broader industry shift toward separating compute from storage, alongside mounting market pressure from new object-storage-first streaming vendors. Together, these factors make Diskless Topics a critical step forward.&lt;/p&gt;
&lt;h3 id=&quot;traditional-architecture-vs-cloud-economics&quot;&gt;Traditional Architecture vs. Cloud Economics&lt;/h3&gt;
&lt;p&gt;Kafka was originally designed for commodity hardware. It relies on low-durability local block storage and direct broker-to-broker replication to ensure high availability and data safety. However, modern public clouds financially penalize this specific design. Major cloud providers like AWS and GCP charge heavily for cross-Availability Zone network traffic, often between $0.01 to $0.02 per GiB. Furthermore, provisioned block storage is considerably more expensive than object storage like Amazon S3 or Google Cloud Storage.&lt;/p&gt;
&lt;p&gt;While the introduction of Tiered Storage (KIP-405) was a fantastic milestone, it only solved the cost problem for inactive, historical data. Active data still requires expensive local disk provisioning and incurs high replication network costs.&lt;/p&gt;
&lt;h3 id=&quot;industry-shift-to-decoupling&quot;&gt;Industry Shift to Decoupling&lt;/h3&gt;
&lt;p&gt;This friction between legacy architecture and cloud pricing is not unique to event streaming. The broader data ecosystem solved this exact problem years ago by fundamentally decoupling compute resources from storage layers. Modern data warehouse solutions like Snowflake, Amazon Redshift, and Google BigQuery revolutionized cloud analytics by moving their ultimate source of truth to highly durable, low-cost object storage. This architectural shift proved that data platforms could achieve massive scale and cost efficiency without sacrificing reliability.&lt;/p&gt;
&lt;h3 id=&quot;market-pressure-in-event-streaming&quot;&gt;Market Pressure in Event Streaming&lt;/h3&gt;
&lt;p&gt;Naturally, this separation of compute and storage has now arrived in the real-time streaming space. Recognizing the financial burden of running traditional Kafka in the cloud, several alternative vendors capitalized on this architectural gap to build object-storage-first streaming engines. Platforms like AutoMQ and WarpStream built Kafka-compatible engines backed entirely by object storage. Similarly, StreamNative recently expanded beyond Apache Pulsar to offer a Kafka-compatible engine that also leverages a decoupled storage architecture.&lt;/p&gt;
&lt;h3 id=&quot;kip-1150-as-kafkas-native-response&quot;&gt;KIP-1150 as Kafka’s Native Response&lt;/h3&gt;
&lt;p&gt;Rather than allowing alternative platforms to dictate the future of cloud-native streaming, the Apache Kafka community accepted KIP-1150 as a roadmap to incorporate decoupled innovation natively. By planning to utilize object storage for active segments, KIP-1150 addresses these industry forces simultaneously. It tackles the financial friction of cloud block storage, aligns Kafka with the principle of separating compute from storage, and provides a robust answer to emerging competitors.&lt;/p&gt;
&lt;h2 id=&quot;what-are-kip-1150-diskless-topics&quot;&gt;What are KIP-1150 Diskless Topics?&lt;/h2&gt;
&lt;p&gt;KIP-1150 introduces a blueprint for a new type of topic equipped with a distinct ingestion engine. Instead of relying purely on local block storage for durability and replication, this proposed engine writes data through directly to object storage.&lt;/p&gt;
&lt;p&gt;It is important to clarify the terminology. “Diskless” does not mean broker disks vanish entirely. Disks are still utilized for KRaft metadata, caching consumer data, and short-term staging. However, the primary source of truth and data durability completely shifts from the local disk to the remote object store.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69e1c8264d26a0fcb90e8e40_what-is-it.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-does-kip-1150-solve&quot;&gt;What does KIP-1150 solve?&lt;/h2&gt;
&lt;p&gt;KIP-1150 directly targets the most significant infrastructure pain points that platform engineers face today.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69e1cdd321f84f362ce0be87_what-it-solves.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;drastically-reducing-replication-transfer-costs&quot;&gt;Drastically reducing replication transfer costs&lt;/h3&gt;
&lt;p&gt;By relying on object storage for durability, brokers will no longer need to constantly mirror gigabytes of data across availability zones to maintain replication factors. This largely bypasses the expensive cross-AZ network transfer bills that plague large-scale cloud deployments. It is worth noting that costs are not entirely eliminated. Operators trade heavy broker-to-broker network fees for object store API and data transfer costs, which are generally much cheaper but still require architectural consideration.&lt;/p&gt;
&lt;h3 id=&quot;infrastructure-optimization&quot;&gt;Infrastructure optimization&lt;/h3&gt;
&lt;p&gt;Operators can provision brokers with extremely small, highly performant disks (or even memory-backed storage) merely for caching. Relying on cheap object storage for actual data retention drastically reduces monthly block storage spend.&lt;/p&gt;
&lt;h3 id=&quot;decoupled-scaling&quot;&gt;Decoupled scaling&lt;/h3&gt;
&lt;p&gt;In traditional Kafka, scaling out a cluster means physically moving large volumes of data between broker disks, a process that is notoriously slow and network-intensive. With Diskless Topics, the canonical data safely resides in object storage. Brokers can scale up and rebalance significantly faster because they only need to update metadata and warm up their caches, completely bypassing heavy data migration.&lt;/p&gt;
&lt;h2 id=&quot;how-will-diskless-topics-work&quot;&gt;How will Diskless Topics work?&lt;/h2&gt;
&lt;p&gt;KIP-1150 is a consensus and architectural document. Its primary goal is to align the community on the need and the high-level concept. The actual codebase implementation is deliberately split into proposed follow-up designs, including KIP-1163 (Diskless Core) and KIP-1164 (Diskless Coordinator).&lt;/p&gt;
&lt;p&gt;At an architectural level, Diskless Topics are envisioned to operate via an ingestion engine running parallel to the classic topic engine. Data will remain accessible to consumers both from the ingestion engine cache and directly from tiered storage.&lt;/p&gt;
&lt;h3 id=&quot;anticipated-write-path&quot;&gt;Anticipated Write Path&lt;/h3&gt;
&lt;p&gt;To maintain reasonable performance while writing to high-latency object storage, the ingestion engine cannot simply block producer requests waiting for S3 HTTP responses. Instead, the architecture will heavily rely on local staging. When a producer sends data, the ingestion engine will likely use local memory or a highly optimized disk cache to stage the incoming records. Depending on the configured durability guarantees, the broker might acknowledge the producer immediately after local staging, or wait for an asynchronous background process to flush these batches to the remote object store. Balancing fast producer acknowledgments with the strict durability guarantees of object storage is where the real engineering complexity lies.&lt;/p&gt;
&lt;h2 id=&quot;what-complexities-does-kip-1150-introduce&quot;&gt;What complexities does KIP-1150 introduce?&lt;/h2&gt;
&lt;p&gt;While the financial and operational benefits of Diskless Topics are highly appealing, they introduce new tradeoffs and visibility challenges.&lt;/p&gt;
&lt;h3 id=&quot;ambitious-backward-compatibility&quot;&gt;Ambitious backward compatibility&lt;/h3&gt;
&lt;p&gt;The KIP states a goal of being entirely backwards compatible with existing Kafka APIs, including strict ordering guarantees, idempotency, and consumer group semantics. Achieving these semantics over a remote object-storage hot path is a massive, non-trivial engineering challenge. Ensuring consistency when the primary storage layer operates asynchronously over HTTP will require careful design and likely extensive real-world testing once implemented.&lt;/p&gt;
&lt;h3 id=&quot;latency-trade-offs&quot;&gt;Latency Trade-offs&lt;/h3&gt;
&lt;p&gt;Writing through to an object store naturally introduces higher produce latency compared to writing directly to a local NVMe drive. Applications that require strict, ultra-low microsecond latency will likely need to stick to classic block-storage topics. Platform teams will need to carefully categorize workloads to determine which topics should be diskless and which must remain classic.&lt;/p&gt;
&lt;h3 id=&quot;obscured-storage-visibility&quot;&gt;Obscured Storage Visibility&lt;/h3&gt;
&lt;p&gt;Troubleshooting local disk bottlenecks is a well-understood science. However, troubleshooting object storage API rate limits, write-through cache misses, and ingestion engine latency introduces a totally new paradigm. Traditional monitoring strategies relying on simple JMX disk metrics will no longer show the complete picture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Factor House is actively preparing for these new topologies.&lt;/strong&gt; As storage paradigms shift from local disks to remote object stores, we recognize that traditional monitoring tools will leave operations teams with significant blind spots. Factor House is actively tracking the development of KIP-1150 and its sub-KIPs to ensure Kpow will provide seamless, purpose-built observability into remote storage latencies, cache hit rates, and overall diskless topic performance.&lt;/p&gt;
&lt;h2 id=&quot;looking-ahead-future-of-diskless-kafka&quot;&gt;Looking Ahead: Future of Diskless Kafka&lt;/h2&gt;
&lt;p&gt;KIP-1150 serves as the foundational layer for a highly anticipated roadmap of future Kafka enhancements. Once the core diskless functionality is implemented, it unlocks several exciting possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lakehouse (e.g., Apache Iceberg) Integration:&lt;/strong&gt; By writing data natively to object storage, Kafka paves the way for massively parallel analytical processing on at-rest topic data using open table formats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic Type Changing:&lt;/strong&gt; Future updates aim to allow operators to dynamically convert topics back and forth between classic and diskless configurations as business needs evolve.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heterogeneous Clusters:&lt;/strong&gt; Because data will live securely in object storage, brokers in a cluster will no longer need to be identical clones of each other. Operators could provision distinct types of hardware for specific jobs, such as using high-CPU machines dedicated solely to ingesting incoming data, or high-memory machines exclusively for serving consumers, to maximize resource efficiency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;our-take-at-factor-house&quot;&gt;Our take at Factor House&lt;/h2&gt;
&lt;p&gt;KIP-1150 represents a critical proposed evolution for Apache Kafka. By embracing the separation of compute and storage, it lays the groundwork for Kafka to remain a highly competitive and cost-effective streaming engine regardless of shifting cloud architecture trends. If successful, it offers a massive win for organizations looking to drastically reduce their cloud infrastructure costs while achieving easier, faster cluster scalability.&lt;/p&gt;
&lt;p&gt;We believe that operational tooling must evolve at the same pace as the infrastructure it monitors. The transition from block storage to object storage introduces significant complexities. We are incredibly excited about this evolving era of cloud-native Kafka, and we are committed to updating Kpow right alongside Kafka’s core architecture to ensure you maintain total visibility and control.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment or preparing for upcoming architecture shifts, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Factor House</author></item><item><title>KIP-932 Queues for Kafka: Bridging the Gap Between Streaming and Messaging</title><link>https://factorhouse.io/articles/kip-932-queues-for-kafka-explained/</link><guid isPermaLink="true">https://factorhouse.io/articles/kip-932-queues-for-kafka-explained/</guid><description>Discover how Kafka&apos;s KIP-932 Share Groups bring native queue semantics to your event streaming architecture, and the new complexities engineers must manage.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Software engineers have long debated the architectural trade-offs between event streaming platforms and traditional message queues. Do you need the durable, replayable logs of Apache Kafka, or the point-to-point, competing-consumer flexibility of RabbitMQ or AWS SQS? Often, the answer was to use both. This approach unfortunately led to complex and sprawling infrastructure.&lt;/p&gt;
&lt;p&gt;With the introduction of &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-932%3A+Queues+for+Kafka&quot;&gt;&lt;strong&gt;KIP-932: Queues for Kafka&lt;/strong&gt;&lt;/a&gt;, the Apache Kafka community has dramatically reshaped this conversation. By introducing “&lt;em&gt;Share Groups&lt;/em&gt;”, Kafka now natively supports cooperative, point-to-point message queuing without sacrificing its underlying distributed log architecture.&lt;/p&gt;
&lt;p&gt;Blending queue semantics with log storage is not without its trade-offs. In this article, we will explore what KIP-932 is, the problems it solves, how to implement it, and the new operational complexities teams will need to manage as they adopt it.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Share Groups break the 1:1 rule:&lt;/strong&gt; Multiple consumers can now concurrently process messages from a single partition. This unlocks elastic scalability without the need for aggressive over-partitioning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Record-level acknowledgments:&lt;/strong&gt; Instead of committing bulk offsets, consumers now acquire temporary locks on individual messages and acknowledge them upon successful processing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Elimination of head-of-line blocking:&lt;/strong&gt; Slow-to-process messages no longer block the entire partition because other consumers in the Share Group can continue processing subsequent messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New monitoring requirements:&lt;/strong&gt; Managing states like “Acquired,” “Available,” and “Archived” requires new visibility paradigms. Factor House is actively building features to help engineers manage these new complexities as KIP-932 matures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;background-logs-vs-queues-in-kafka&quot;&gt;Background: Logs vs Queues in Kafka&lt;/h2&gt;
&lt;p&gt;To fully appreciate KIP-932, we must look at the historical divide between traditional messaging queues and event streaming logs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traditional Queues (e.g., RabbitMQ, ActiveMQ, SQS)&lt;/strong&gt; operate on a point-to-point basis. When a message is published to a queue, multiple worker services can listen to that queue. When a worker grabs a message, it is hidden from others, processed, and then fundamentally destroyed or removed upon acknowledgment. This allows for massive, dynamic scalability of consumers. If your queue backs up, you simply spin up more worker instances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache Kafka&lt;/strong&gt;, on the other hand, is a distributed commit log. Messages are appended immutably to partitions. To read data, applications use Consumer Groups. In a traditional Consumer Group, Kafka enforces a strict rule: there can only be one active consumer per partition within a group.&lt;/p&gt;
&lt;p&gt;If a topic has three partitions, you can only have three active consumers processing in parallel. If you spin up a fourth consumer, it sits completely idle. To scale up message processing throughput in Kafka, engineers have historically been forced to over-partition their topics. For instance, they might create 100 partitions just in case they need 100 consumers in the future. This taxes broker resources, increases ZooKeeper or KRaft metadata overhead, and complicates cluster management.&lt;/p&gt;
&lt;h3 id=&quot;what-is-kip-932-queues-for-kafka&quot;&gt;What is KIP-932 queues for Kafka?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69d7206621560c5ca605bd6e_log-vs-queue-resized.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;KIP-932 is a major architectural upgrade that brings point-to-point, cooperative queuing semantics natively to Apache Kafka. It introduces &lt;strong&gt;Share Groups&lt;/strong&gt;, a new consumption model where multiple consumers can dynamically collaborate to process messages from the exact same partition.&lt;/p&gt;
&lt;p&gt;Traditional Kafka Consumer Groups track progress using a single numerical offset to indicate how far down the log a consumer has read. In contrast, Share Groups track the state of individual records. When a consumer in a Share Group fetches a message, it does not just read it; it locks it. The message remains in the Kafka log, but the broker knows not to hand that specific message to any other consumer in the Share Group until the lock expires or the message is explicitly acknowledged.&lt;/p&gt;
&lt;p&gt;This effectively turns a Kafka partition into a high-throughput, competing-consumer queue. It blends the best of both streaming and messaging worlds.&lt;/p&gt;
&lt;h2 id=&quot;what-does-kip-932-solve&quot;&gt;What does KIP-932 solve?&lt;/h2&gt;
&lt;p&gt;KIP-932 directly targets several long-standing pain points that developers face when building scalable microservices on top of Kafka.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69d71fa31d7f544318921a42_what-queue-solves.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;solving-the-partition-bottleneck&quot;&gt;Solving the partition bottleneck&lt;/h3&gt;
&lt;p&gt;The most immediate benefit of KIP-932 is the separation of storage scale from processing scale. Previously, your partition count dictated your maximum concurrency limit. With Share Groups, you can have a single partition topic being processed concurrently by 50 different microservice instances. Applications can now dynamically scale horizontally in response to traffic spikes, such as Black Friday sales. Administrators no longer need to modify topic configurations or rebalance partition leaders.&lt;/p&gt;
&lt;h3 id=&quot;eradicating-head-of-line-blocking&quot;&gt;Eradicating head-of-line blocking&lt;/h3&gt;
&lt;p&gt;In a traditional consumer group, partitions are processed sequentially. If Consumer A reads Message 1, and processing that message requires a slow third-party API call taking 15 seconds, all subsequent messages are stuck waiting. This “head-of-line blocking” degrades latency. With KIP-932, Consumer A can lock and process Message 1 while other consumers immediately fetch and process the next messages concurrently.&lt;/p&gt;
&lt;h3 id=&quot;infrastructure-consolidation&quot;&gt;Infrastructure consolidation&lt;/h3&gt;
&lt;p&gt;Historically, architectures required a two-tier messaging strategy. Kafka was used as the central nervous system for high-throughput, durable event streaming, while tools like RabbitMQ were deployed at the edge for task routing and worker queues. KIP-932 enables teams to deprecate legacy messaging systems. This reduces infrastructure sprawl, cuts licensing costs, and simplifies the tech stack to just Apache Kafka.&lt;/p&gt;
&lt;h2 id=&quot;how-does-kip-932-work-in-kafka&quot;&gt;How does KIP-932 work in Kafka?&lt;/h2&gt;
&lt;p&gt;Under the hood, KIP-932 fundamentally alters how Kafka brokers and clients agree on what data has been read. It achieves this via Record-Level Acknowledgement and Acquisition Locks.&lt;/p&gt;
&lt;p&gt;When a Share Group is utilized, records cycle through distinct states:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Available:&lt;/strong&gt; The message is sitting in the partition and is ready to be processed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acquired:&lt;/strong&gt; A Share Consumer fetches the message. The broker applies a temporary lock (configurable, but defaulting to 30 seconds). During this window, the message is invisible to other consumers in the same Share Group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acknowledged:&lt;/strong&gt; The consumer successfully processes the message and sends an &lt;code&gt;ACCEPT&lt;/code&gt; acknowledgment to the broker. The message is marked as complete for that group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Released or Archived:&lt;/strong&gt; If the consumer fails and sends a &lt;code&gt;REJECT&lt;/code&gt;, or if the lock times out because the consumer crashed, the broker releases the lock. This returns the message to the Available state for another consumer to try. If it hits a predefined retry limit, it transitions to Archived (Kafka’s native equivalent of a Dead Letter Queue).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-complexities-does-kip-932-introduce&quot;&gt;What complexities does KIP-932 introduce?&lt;/h2&gt;
&lt;p&gt;While the benefits of Share Groups are immense, they introduce entirely new paradigms of state and error handling. Traditional Kafka monitoring strategies will no longer suffice.&lt;/p&gt;
&lt;h3 id=&quot;obscured-state-management-and-visibility&quot;&gt;Obscured State Management and Visibility&lt;/h3&gt;
&lt;p&gt;For years, monitoring Kafka consumers meant tracking a single metric: &lt;em&gt;Consumer Lag&lt;/em&gt;. If the partition log ended at offset 100, and your consumer was at offset 90, your lag was 10. It was simple arithmetic.&lt;/p&gt;
&lt;p&gt;With Share Groups, a group might have Acknowledged offsets 1, 2, 4, and 7, while offsets 3 and 6 are currently Acquired (locked), and offset 5 is Available. The concept of a single “lag” number is obsolete; you now have an in-flight state matrix.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Factor House is actively building the tools to manage the new complexities introduced by KIP-932.&lt;/strong&gt; Because Share Groups operate on an entirely different set of methods and properties at the AdminClient level, traditional monitoring tools that rely on simple offset math will essentially break or fail to show the complete picture. Recognizing that Share Groups represent an “entirely new world,” our engineering team is currently designing a dedicated, purpose-built experience within our market-leading toolkit, Kpow. Rather than forcing this new paradigm into legacy consumer group views, our goal is to provide a tailored interface that translates this complex state matrix into clear, actionable insights. This ensures your operations team will not be left guessing about in-flight states.&lt;/p&gt;
&lt;h3 id=&quot;dealing-with-poison-pills-and-archiving&quot;&gt;Dealing with Poison Pills and Archiving&lt;/h3&gt;
&lt;p&gt;In standard Kafka, a “poison pill” (a malformed message that crashes your consumer) would halt partition processing entirely until an engineer intervened. KIP-932 automatically handles this via the &lt;code&gt;group.share.delivery.attempt.limit&lt;/code&gt; configuration. If a message continually crashes consumers, its lock expires, it gets retried, and eventually, it is moved to the &lt;strong&gt;Archived&lt;/strong&gt; state.&lt;/p&gt;
&lt;p&gt;However, Kafka natively provides very little tooling to inspect or manage these Archived messages once they are sidelined. At Factor House, we are carefully evaluating how to bridge this gap. As KIP-932 matures and the APIs stabilize, we are looking at how to extend Kpow’s industry-leading data inspection and troubleshooting workflows to support these new archiving mechanics. Our vision is to ensure engineers will have the seamless ability to isolate, inspect, and recover archived records just as easily as they manage standard topic data today.&lt;/p&gt;
&lt;h3 id=&quot;loss-of-strict-ordering-guarantees&quot;&gt;Loss of Strict Ordering Guarantees&lt;/h3&gt;
&lt;p&gt;Because multiple consumers are pulling from the same partition concurrently, strict chronological ordering is completely broken. If order matters for your business logic (e.g., processing an “Account Created” event before an “Account Updated” event for the same user ID), Share Groups are not the right tool. Engineers must carefully evaluate their domain logic to ensure their microservices are genuinely idempotent and order-independent before migrating to KIP-932 queues.&lt;/p&gt;
&lt;h3 id=&quot;ecosystem-maturity&quot;&gt;Ecosystem Maturity&lt;/h3&gt;
&lt;p&gt;KIP-932 requires brand new API classes (like &lt;code&gt;KafkaShareConsumer&lt;/code&gt; in Java) to handle the new acknowledgment protocols (&lt;code&gt;ACCEPT&lt;/code&gt;, &lt;code&gt;RELEASE&lt;/code&gt;, &lt;code&gt;REJECT&lt;/code&gt;). While Confluent and the Apache community have prioritized the Java ecosystem, wrappers like &lt;code&gt;librdkafka&lt;/code&gt; (which power Python, Go, C++, and .NET) are taking longer to catch up. Organizations with polyglot architectures will face a transition period where only their Java-based microservices can leverage Share Groups. This could potentially lead to fragmented architectural patterns in the short term.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-kip-932-queues-for-kafka-share-groups&quot;&gt;How to implement KIP-932 queues for Kafka Share Groups&lt;/h2&gt;
&lt;p&gt;Implementing Share Groups requires a modern Kafka deployment and specific client-side code changes. Instead of relying on a standard &lt;code&gt;KafkaConsumer&lt;/code&gt;, your application must utilize the new Share Group APIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Apache Kafka 4.0 or newer.&lt;/li&gt;
&lt;li&gt;A compatible client (currently Java via &lt;code&gt;KafkaShareConsumer&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Enable Share Groups on the Broker&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ensure your broker configurations have the share group protocols enabled. This is enabled by default in newer KRaft-based Kafka releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Instantiate the Share Consumer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In your Java application, replace your traditional consumer with the new &lt;code&gt;KafkaShareConsumer&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Configure the Group ID&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of using &lt;code&gt;group.id&lt;/code&gt;, you bind your consumers together using the &lt;code&gt;share.group.id&lt;/code&gt; property.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Properties props &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; Properties&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;props.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;bootstrap.servers&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;props.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;share.group.id&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;my-first-share-group&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;props.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;key.deserializer&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;org.apache.kafka.common.serialization.StringDeserializer&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;props.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;value.deserializer&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;org.apache.kafka.common.serialization.StringDeserializer&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;KafkaShareConsumer&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;&gt; consumer &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaShareConsumer&amp;#x3C;&gt;(props);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;consumer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;subscribe&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(Collections.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;singletonList&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kip932-demo-topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Handle Acknowledgements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Update your application code logic to utilize the new record-level APIs. When a record is processed, explicit acknowledgments dictate what happens to the message next.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    ConsumerRecords&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;&gt; records &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; consumer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;poll&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(Duration.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;ofMillis&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (ConsumerRecord&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;&gt; record &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; records) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            // Process the message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            System.out.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Processing key = %s, value = %s%n&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(), record.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            // Explicitly accept the record upon success&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            consumer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;acknowledge&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(record, AcknowledgeType.ACCEPT);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (Exception &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            // Reject the record so it can be retried by another consumer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            consumer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;acknowledge&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(record, AcknowledgeType.REJECT);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Commit the acknowledgments to the broker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    consumer.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;commitSync&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By deploying multiple instances of this Java application, you will instantly see Kafka distribute the messages concurrently among the active instances. This completely bypasses the traditional partition limits.&lt;/p&gt;
&lt;h2 id=&quot;our-take-at-factor-house&quot;&gt;Our take at Factor House&lt;/h2&gt;
&lt;p&gt;KIP-932 is undeniably a milestone for Apache Kafka. By successfully merging event streaming with point-to-point queuing, it empowers engineering teams to vastly simplify their infrastructure and scale worker processes dynamically without jumping through the hoops of over-partitioning.&lt;/p&gt;
&lt;p&gt;We believe that operational tooling must evolve at the same pace as the infrastructure. The transition from simple offset tracking to complex, record-level state management introduces significant blind spots if teams rely on legacy monitoring tools. We are incredibly excited about this new era of Kafka, but we also recognize that such a fundamental shift requires equally evolved tooling.&lt;/p&gt;
&lt;p&gt;Because Share Groups introduce an entirely new set of methods and properties, we are actively developing the next generation of Kpow features to tackle this challenge head-on. Rather than trying to patch over legacy consumer group views, we are building a completely distinct and tailored UI experience to illuminate these new mechanics. Stay tuned as we continue to roll out dedicated support to help you safely and effectively navigate this exciting new streaming landscape.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Jaehyeon Kim</author></item><item><title>RBAC for Kafka: How to Implement and Key Considerations</title><link>https://factorhouse.io/articles/rbac-for-kafka/</link><guid isPermaLink="true">https://factorhouse.io/articles/rbac-for-kafka/</guid><description>Learn how to implement Kafka RBAC with practical steps, real-world configuration insights from a hands-on lab, and a clear comparison of RBAC vs ACLs at scale</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;key-takeaways&quot;&gt;Key takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Role-Based Access Control (RBAC) replaces unmanageable per-user ACL rules with role abstractions that scale across teams, topics, and clusters.&lt;/li&gt;
&lt;li&gt;Kafka’s native ACLs and RBAC via a management tool protect different layers. ACLs govern data plane access (producers, consumers, admin clients); RBAC controls what operators can do through the management UI. Both are typically needed.&lt;/li&gt;
&lt;li&gt;Management tools like Kpow connect to Kafka using their own service account, not as the logged-in user. This separation is what makes RBAC enforcement possible without granting every operator direct cluster access.&lt;/li&gt;
&lt;li&gt;A working RBAC setup with Kpow can run in under an hour. This guide walks you through every step.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-kafka-rbac&quot;&gt;What is Kafka RBAC?&lt;/h2&gt;
&lt;p&gt;Role-Based Access Control (RBAC) is a permission model that groups access rights into roles, and then assigns users to those roles. In a Kafka context, it determines what authenticated users can do through a management interface, rather than assigning permissions directly to individuals.&lt;/p&gt;
&lt;p&gt;I’ve seen the pattern multiple times. A team starts with five topics and three services. They write a handful of ACL rules. It works. Then the platform grew to ten teams, 100+ topics, producers and consumers multiplying across environments. Suddenly there were thousands of user-to-resource rules, nobody knew who had access to what, and onboarding a new engineer meant writing rules one by one. That’s when ACLs stop being a solution and become the problem.&lt;/p&gt;
&lt;p&gt;Role-Based Access Control uses a different model. Rather than assigning permissions directly between individual users and resources, it groups permissions into roles such as viewer, operator, or kafka-admin, and then assigns users to those roles. A single role change propagates to everyone assigned to it, and a single audit query shows you who can do what.&lt;/p&gt;
&lt;p&gt;In the Kafka ecosystem, RBAC doesn’t replace the broker’s native authorization. It layers on top of it, typically through a management tool like &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow&lt;/a&gt; that sits between your operators and the cluster. The broker still enforces ACLs at the protocol level. RBAC controls what humans can do through the operations interface.&lt;/p&gt;
&lt;p&gt;Here’s the core difference, visualized:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69d70c84bc97e1b1b456b92e_42e8b0b7.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, eight rules are reduced to two role assignments for three users. In larger environments, the reduction in individual rules can become even more significant.&lt;/p&gt;
&lt;h2 id=&quot;kafka-authorization-rbac-vs-acls&quot;&gt;Kafka authorization: RBAC vs ACLs&lt;/h2&gt;
&lt;p&gt;ACLs can be effective at certain scales. The key consideration is whether they fit the operational and governance needs of your environment as it grows.&lt;/p&gt;
&lt;p&gt;Kafka ships with StandardAuthorizer (KRaft) or AclAuthorizer (ZooKeeper). Both enforce allow/deny rules at the broker level: who can produce, consume, create topics, manage groups. They work directly on the Kafka protocol. Every client connection is evaluated against these rules.&lt;/p&gt;
&lt;p&gt;RBAC, as implemented by management tools like Kpow, operates at a different layer. It controls what authenticated users can do through the management UI: inspect topics, query data, create or delete resources, edit ACLs. The tool connects to Kafka using a service account with the necessary privileges. The logged-in user never touches the broker directly.&lt;/p&gt;
&lt;p&gt;This means they’re complementary, not competing. Most teams need both. ACLs for the data plane. RBAC for the &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;operational control plane&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Kafka ACLs&lt;/th&gt;
&lt;th&gt;RBAC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it protects&lt;/td&gt;
&lt;td&gt;Direct broker access (producers, consumers, admin clients)&lt;/td&gt;
&lt;td&gt;Operations UI access (inspect, query, mutate resources)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Granularity&lt;/td&gt;
&lt;td&gt;Per-user, per-resource, per-operation&lt;/td&gt;
&lt;td&gt;Per-role, per-resource taxon, per-action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale&lt;/td&gt;
&lt;td&gt;Rules grow linearly with users × resources&lt;/td&gt;
&lt;td&gt;Rules grow with roles (typically 3 to 10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;Broker logs (verbose, unstructured)&lt;/td&gt;
&lt;td&gt;Dedicated audit log (provided by the management tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Central management&lt;/td&gt;
&lt;td&gt;No, per-cluster only&lt;/td&gt;
&lt;td&gt;Yes, one config across managed clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Onboarding a new user&lt;/td&gt;
&lt;td&gt;Write N ACL rules&lt;/td&gt;
&lt;td&gt;Assign one role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical adopter&lt;/td&gt;
&lt;td&gt;Every Kafka installation&lt;/td&gt;
&lt;td&gt;Teams managing authorization at scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deny semantics&lt;/td&gt;
&lt;td&gt;Explicit DENY rules supported, DENY takes precedence over ALLOW&lt;/td&gt;
&lt;td&gt;Default-deny with allow-only policies. If no policy matches, access is denied.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;three-layers-of-kafka-governance&quot;&gt;Three layers of Kafka governance&lt;/h2&gt;
&lt;p&gt;Governance discussions often focus on two layers, but there is a third worth considering.&lt;/p&gt;
&lt;p&gt;The first layer is Kafka ACLs. They sit at the broker level and control what every client can do at the protocol level. Every producer, consumer, and admin tool goes through this gate. ACLs are not optional in a serious production environment.&lt;/p&gt;
&lt;p&gt;The second layer is RBAC. It controls what your operators can do through the management UI. Without it, every person with Kpow access implicitly has the same privileges as Kpow’s service account. That’s a broad blast radius for a misconfigured delete operation.&lt;/p&gt;
&lt;p&gt;The third layer is multi-tenancy. This is about isolating teams and resources from each other within a shared cluster. Who can see which topics. Which namespaces are off-limits. Kpow supports multi-tenancy as a feature separate from RBAC. See the &lt;a href=&quot;https://docs.factorhouse.io/kpow/multi-tenancy&quot;&gt;Kpow multi-tenancy documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Platform engineers who operate shared Kafka infrastructure need all three layers working together. Each one solves a different problem. None of them replaces the others.&lt;/p&gt;
&lt;h2 id=&quot;why-implement-kafka-rbac&quot;&gt;Why implement Kafka RBAC?&lt;/h2&gt;
&lt;p&gt;The technical comparison is useful. But the real driver is usually a business question: can we prove who had access to what, and when?&lt;/p&gt;
&lt;h3 id=&quot;governance-and-compliance&quot;&gt;Governance and compliance&lt;/h3&gt;
&lt;p&gt;Auditors don’t want to read thousands of ACL entries. They want role definitions and assignment logs. RBAC gives you a clean answer: “These three roles exist. Here’s who is assigned to each. Here’s the audit trail of every action.” That conversation takes five minutes instead of five days.&lt;/p&gt;
&lt;h3 id=&quot;fine-grained-authorization-at-the-ui-level&quot;&gt;Fine-grained authorization at the UI level&lt;/h3&gt;
&lt;p&gt;Not every operator needs the same access. A developer on the payments team should inspect payment topics and query messages for debugging. They should not be able to delete production topics or edit ACLs. With Kpow RBAC, I defined a viewer role with exactly three permissions TOPIC_INSPECT, TOPIC_DATA_QUERY, TOPIC_DATA_DOWNLOAD and a kafka-admin role with full CRUD plus ACL_EDIT. Two roles. Clean separation. Done.&lt;/p&gt;
&lt;h3 id=&quot;scaling-authorization-across-teams&quot;&gt;Scaling authorization across teams&lt;/h3&gt;
&lt;p&gt;This is where ACLs collapse. When a new engineer joins, you assign them a role. When someone changes teams, you change their role. When a team is decommissioned, you remove the role. No per-user rules to track. No orphaned ACLs rotting in the broker config.&lt;/p&gt;
&lt;h3 id=&quot;central-management-for-multiple-clusters&quot;&gt;Central management for multiple clusters&lt;/h3&gt;
&lt;p&gt;If you operate Kafka in dev, staging, and production, maintaining separate ACL sets per cluster is painful. Kpow’s RBAC config is external to the broker.  One YAML file defines your roles, and Kpow applies them to whichever cluster it connects to.&lt;/p&gt;
&lt;h3 id=&quot;authentication-method-flexibility&quot;&gt;Authentication method flexibility&lt;/h3&gt;
&lt;p&gt;Kafka supports SASL/PLAIN, SASL/SCRAM, SASL/GSSAPI (Kerberos), mTLS, and OAuthBearer for client authentication. Kpow adds its own auth layer on top supporting Jetty (file-based), OpenID Connect (which covers OAuth 2.0 providers like Okta and Azure AD), SAML, and LDAP. So yes, Kafka does support OAuth.&lt;/p&gt;
&lt;h2 id=&quot;what-to-consider-before-implementing-rbac&quot;&gt;What to consider before implementing RBAC&lt;/h2&gt;
&lt;p&gt;Before you start, give some consideration to the following five choices. Missteps can introduce security weaknesses that become apparent later, including during incidents.&lt;/p&gt;
&lt;h3 id=&quot;1-principle-of-least-privilege-polp&quot;&gt;1. Principle of Least Privilege (PoLP)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;‍&lt;/strong&gt;Start with zero permissions and add only what each role requires. I made the mistake of testing with an overly generous viewer role first. It worked, but it also let read-only users see data they shouldn’t. Strip it down. The right question isn’t “what could this role need?” It’s “what’s the minimum this role must have to do their job?”&lt;/p&gt;
&lt;h3 id=&quot;2-multi-tenancy-and-resource-isolation&quot;&gt;2. Multi-tenancy and resource isolation&lt;/h3&gt;
&lt;p&gt;If multiple teams share a cluster, your RBAC policies need resource-level scoping. Kpow’s taxon system supports this. You can restrict a role to specific topics by name or wildcard. But you have to define policies at every taxon depth (more on this in the implementation section). Don’t assume a cluster-level wildcard covers topic-level operations. It doesn’t.&lt;/p&gt;
&lt;h3 id=&quot;3-integration-with-your-identity-provider-idp&quot;&gt;3. Integration with your identity provider (IdP)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;‍&lt;/strong&gt;Kpow supports four auth providers: jetty, openid, saml, and LDAP (configured via the Jetty provider). Choose based on what you already run. If you have Okta or Azure AD, go OpenID. If you want a quick local setup for testing, go Jetty. The one thing you cannot do is reuse Kafka’s SASL users directly.  Kpow’s auth is a separate layer.&lt;/p&gt;
&lt;h3 id=&quot;4-data-masking-and-sensitive-topics&quot;&gt;4. Data masking and sensitive topics&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;‍&lt;/strong&gt;RBAC controls who can query topic data through the UI. But consider whether some topics contain PII or financial data that no role should access in plain text. Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow/data/data-policies&quot;&gt;data policies&lt;/a&gt; allow sensitive field values to be redacted, even for users who have TOPIC_DATA_QUERY permission.&lt;/p&gt;
&lt;h3 id=&quot;5-role-explosion&quot;&gt;5. Role explosion&lt;/h3&gt;
&lt;p&gt;RBAC should reduce the number of individual rules. If you end up with 30 roles for 30 teams, you’ve rebuilt ACLs with extra steps. Aim for 3 to 7 generic roles (viewer, operator, admin, maybe a data-steward) and use resource scoping for team isolation. The moment you create a role named payments-team-staging-read-only-except-topic-x, stop. Rethink.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-rbac&quot;&gt;How to implement RBAC&lt;/h2&gt;
&lt;p&gt;Kpow adds a user-facing access control layer on top of your Kafka cluster. Kpow’s RBAC lets you define what each user role can do within the Kpow UI: creating topics, querying data, editing consumer groups, and so on.&lt;/p&gt;
&lt;p&gt;The two systems are independent. Kafka ACLs govern what client applications (including Kpow’s own service account) can do at the broker. Kpow RBAC governs what your human operators can do through Kpow. Both are worth configuring; neither replaces the other.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How it works&lt;/h3&gt;
&lt;p&gt;Kpow RBAC maps roles from your identity provider to Allow, Deny, or Stage permissions on specific Kafka resources. Roles come from whatever authentication provider you’re using: Jetty (file, LDAP, or DB), SAML, or OpenID/OAuth. The RBAC configuration itself lives in a YAML file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RBAC_CONFIGURATION_FILE=/path/to/rbac-config.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Without RBAC configured, the default effect for all actions is an implicit Deny. With it enabled, you define exactly which roles can do what.&lt;/p&gt;
&lt;h3 id=&quot;defining-policies&quot;&gt;Defining policies&lt;/h3&gt;
&lt;p&gt;Each policy specifies a resource, an effect, a list of actions, and the role it applies to:&lt;code&gt;‍&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;`authorized_roles:&lt;br&gt;
 - “*”   # Allow all authenticated users into the UI&lt;/p&gt;
&lt;p&gt;admin_roles:&lt;br&gt;
 - “kafka-admin”&lt;/p&gt;
&lt;p&gt;policies:&lt;br&gt;
 # Read-only access for viewers&lt;br&gt;
 - role:     “viewer”&lt;br&gt;
   effect:   “Allow”&lt;br&gt;
   resource: [“cluster”, “*”]&lt;br&gt;
   actions:  [“TOPIC_INSPECT”]&lt;/p&gt;
&lt;p&gt; - role:     “viewer”&lt;br&gt;
   effect:   “Allow”&lt;br&gt;
   resource: [“cluster”, “&lt;em&gt;”, “topic”, “&lt;/em&gt;”]&lt;br&gt;
   actions:  [“TOPIC_INSPECT”, “TOPIC_DATA_QUERY”, “TOPIC_DATA_DOWNLOAD”]&lt;/p&gt;
&lt;p&gt; # Full control for admins&lt;br&gt;
 - role:     “kafka-admin”&lt;br&gt;
   effect:   “Allow”&lt;br&gt;
   resource: [“cluster”, “&lt;em&gt;”, “topic”, “&lt;/em&gt;”]&lt;br&gt;
   actions:  [“TOPIC_INSPECT”, “TOPIC_CREATE”, “TOPIC_EDIT”, “TOPIC_DELETE”,&lt;br&gt;
              “TOPIC_DATA_QUERY”, “TOPIC_DATA_DOWNLOAD”, “TOPIC_DATA_PRODUCE”]`&lt;/p&gt;
&lt;p&gt;A few things worth understanding before you write your first config:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Role names are from your auth provider, not usernames.&lt;/strong&gt; The &lt;code&gt;role&lt;/code&gt; field must match the role assigned in your identity provider (e.g. the role in your Jetty realm file, or a group from your SAML IdP), not an individual username.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Taxon depth matters.&lt;/strong&gt; Resources follow a four-part taxonomy: &lt;code&gt;[DOMAIN_TYPE, DOMAIN_ID, OBJECT_TYPE, OBJECT_ID]&lt;/code&gt;. A policy scoped to &lt;code&gt;[&quot;cluster&quot;, &quot;*&quot;]&lt;/code&gt; only covers cluster-level actions. Topic-level actions (querying data, producing messages) require a separate policy scoped to &lt;code&gt;[&quot;cluster&quot;, &quot;*&quot;, &quot;topic&quot;, &quot;*&quot;]&lt;/code&gt;. You need entries at each depth you want to cover.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deny takes precedence.&lt;/strong&gt; Where multiple policies apply to the same resource, Deny effects always win. Anything without a matching Allow is implicitly denied.&lt;/p&gt;
&lt;h3 id=&quot;debugging-access-issues&quot;&gt;Debugging access issues&lt;/h3&gt;
&lt;p&gt;Kpow surfaces permission errors directly in the UI when a user is denied access to a resource. For deeper debugging, every action is recorded in Kpow’s audit log topic (&lt;code&gt;__oprtr_audit_log&lt;/code&gt;), which includes the principal, the resource taxon, and the effect applied.&lt;/p&gt;
&lt;p&gt;For the full list of available actions and detailed configuration options, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;Kpow RBAC documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;try-rbac-for-free-with-kpow-by-factor-house&quot;&gt;Try RBAC for free with Kpow by Factor House&lt;/h2&gt;
&lt;p&gt;ACLs work well for some environments, but often become harder to manage over time. Without RBAC in your management tooling, every operator inherits the full permissions of the tool’s service account, regardless of their actual role.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow&lt;/a&gt; gives you what’s missing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fine-Grained Authorization.&lt;/strong&gt; Define exactly what each role can see, query, create, and delete down to specific topics and action types.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seamless Integration.&lt;/strong&gt; Plug into your existing identity provider OpenID Connect (Okta, Azure AD), SAML, LDAP, or file-based Jetty auth. No changes to your Kafka cluster configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational Control.&lt;/strong&gt; Every action flows through &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;RBAC policies&lt;/a&gt;, every action is audited, and sensitive operations can be staged for approval before execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Give Kpow a try for yourself with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;. You can connect it to any Kafka cluster in minutes and deploy via Docker, Helm, or JAR.&lt;/p&gt;
</content:encoded><category>Product</category><author>Moslem Chalfouh</author></item><item><title>Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics</title><link>https://factorhouse.io/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics/</link><guid isPermaLink="true">https://factorhouse.io/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics/</guid><description>Move beyond raw JMX noise and unlock business-relevant observability for your Kafka environment. This guide explores how to feed high-fidelity, pre-calculated metrics, such as consumer group lag in seconds, directly from Kpow into your Grafana dashboards for proactive capacity planning and incident response.</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In Part 1 of our observability series, we demonstrated how to close the Context Gap using Kpow’s unified diagnostic interface. Resolving active incidents quickly is crucial, but mature engineering teams also require robust historical analysis, capacity planning, and automated alerting. For these tasks, teams rely on monitoring stacks like Prometheus and Grafana.&lt;/p&gt;
&lt;p&gt;However, standard Kafka dashboards frequently suffer from a persistent issue known as the &lt;strong&gt;Quality Gap&lt;/strong&gt;. They are flooded with raw, low-level metrics that provide technical depth but lack the context required to understand true business impact.&lt;/p&gt;
&lt;p&gt;This guide explores how to close the Quality Gap by transitioning from scraping low-level infrastructure data to utilizing high-fidelity, pre-calculated telemetry that instantly reveals environment health.&lt;/p&gt;
&lt;p&gt;This is Part 2 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-observability-with-kpow-driving-operational-excellence&quot;&gt;Kafka Observability with Kpow: Driving Operational Excellence&lt;/a&gt; series.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/rapid-kafka-diagnostics-a-unified-workflow-for-root-cause-analysis&quot;&gt;Rapid Kafka Diagnostics: A Unified Workflow for Root Cause Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/operational-transparency-audit-trail-integrated-with-webhooks&quot;&gt;Operational Transparency: Real-Time Audit Trail integrated with Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-quality-gap-of-raw-metrics&quot;&gt;Problem: Quality Gap of Raw Metrics&lt;/h2&gt;
&lt;p&gt;The standard approach for routing Kafka metrics into Prometheus is to deploy a JMX Exporter. While this successfully moves data from the broker to the dashboard, it creates a significant Quality Gap.&lt;/p&gt;
&lt;p&gt;Raw JMX metrics provide deep technical visibility but offer very little business context. Metrics such as raw message offsets or byte counters are difficult to interpret during a critical incident. They do not directly convey user impact or service degradation.&lt;/p&gt;
&lt;p&gt;In practice, the most meaningful operational signals are derived metrics. For example, knowing a consumer’s exact lag reflects data freshness and downstream SLA risk. Knowing the active throughput delta of a topic indicates whether a producer has silently failed.&lt;/p&gt;
&lt;p&gt;Attempting to compute these high-fidelity metrics from raw JMX offsets using PromQL is notoriously difficult, fragile, and often inaccurate. As a result, teams are left with noisy, low-quality dashboards that hinder incident response and make automated alerting unreliable.&lt;/p&gt;
&lt;h2 id=&quot;solution-kpow-as-a-high-fidelity-telemetry-engine&quot;&gt;Solution: Kpow as a High-Fidelity Telemetry Engine&lt;/h2&gt;
&lt;p&gt;Kpow eliminates the Quality Gap by acting as a centralized, high-fidelity metrics engine. The unique advantage of Kpow is that it calculates its own telemetry by directly observing your Kafka cluster and related resources.&lt;/p&gt;
&lt;p&gt;This architectural choice provides several distinct advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No JMX Dependency:&lt;/strong&gt; Because Kpow does not rely on Kafka’s internal JMX metrics, it offers frictionless installation and configuration. You do not need to deploy sidecars or manage complex regular expression filters. Instead, Kpow exposes several dedicated, OpenMetrics-compliant egress endpoints. You simply configure your Prometheus scraper to target specific paths, such as the core telemetry endpoint, topic offsets, or consumer group offsets, as detailed in the &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/prometheus/overview&quot;&gt;Prometheus Integration Overview&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Derived, Actionable Metrics:&lt;/strong&gt; Kpow handles the heavy computational lifting. From its direct observations, it calculates complex, derived metrics that represent true business impact. As outlined in the &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/prometheus/metrics-glossary&quot;&gt;Kpow Metrics Glossary&lt;/a&gt;, these include precise consumer group lag (&lt;code&gt;group_offset_lag&lt;/code&gt;), production throughput rates (&lt;code&gt;topic_end_delta&lt;/code&gt;), and under-replicated partition counts (&lt;code&gt;topic_urp_total&lt;/code&gt;). These are exposed as clean, highly labeled time-series data ready for immediate visualization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unified Scope:&lt;/strong&gt; This frictionless pattern extends beyond just Kafka brokers. Kpow automatically calculates and exposes metrics across your entire streaming ecosystem. With &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/prometheus/overview#endpoints&quot;&gt;granular endpoint structures&lt;/a&gt;, you gain comprehensive observability into Kafka Connect, Schema Registry, Kafka Streams, and ksqlDB. This provides full-stack visibility without requiring additional agents or custom exporters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;putting-it-into-practice-ready-to-use-grafana-dashboards&quot;&gt;Putting it into Practice: Ready-to-Use Grafana Dashboards&lt;/h2&gt;
&lt;p&gt;To demonstrate the power of high-fidelity telemetry, we have published four ready-to-use Grafana dashboard templates. These dashboards consume Kpow’s Prometheus endpoint to provide immediate, actionable visibility across your infrastructure.&lt;/p&gt;
&lt;p&gt;You can instantly import these dashboards into your environment directly from the &lt;strong&gt;Grafana Community Gallery&lt;/strong&gt;, or explore the source templates and setup instructions in our dedicated &lt;a href=&quot;https://github.com/factorhouse/factor-telemetry&quot;&gt;&lt;strong&gt;Factor Telemetry GitHub repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;1-kafka-environment-health&quot;&gt;1. Kafka Environment Health&lt;/h3&gt;
&lt;p&gt;Designed for Platform Teams, this dashboard provides a high-level macro view of overall cluster stability and capacity.&lt;/p&gt;
&lt;p&gt;Rather than relying on raw byte counts, it surfaces derived operational health indicators. It tracks total online brokers, overall data on disk, total topics, and total consumer groups. It also visualizes cluster-wide production and consumption rates, and provides a detailed breakdown of topic activity and consumer group health (Stable, Rebalancing, Empty) to give you an instant read on the environment’s status.&lt;/p&gt;
&lt;p&gt;🌐 &lt;a href=&quot;https://grafana.com/grafana/dashboards/25103-kafka-environment/&quot;&gt;&lt;strong&gt;Import from Grafana Gallery (ID: 25103)&lt;/strong&gt;&lt;/a&gt; | 📁 &lt;a href=&quot;https://github.com/factorhouse/factor-telemetry/blob/main/grafana-templates/kpow/kafka-environment.json&quot;&gt;&lt;strong&gt;View JSON Template&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c4aaf7f20e1ea62bde5147_environment.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-kafka-topic-diagnostics&quot;&gt;2. Kafka Topic Diagnostics&lt;/h3&gt;
&lt;p&gt;Designed for data engineers and platform administrators, this dashboard provides granular visibility into the data layer.&lt;/p&gt;
&lt;p&gt;It tracks aggregate metrics like total topics, total replica disk usage, cluster-wide read/write throughput, and non-preferred leaders. Most importantly, it visualizes per-topic production and consumption rates over time, topic size growth, and isolates the exact topics experiencing consumer lag or Under Replicated Partitions (URPs) through detailed diagnostic tables.&lt;/p&gt;
&lt;p&gt;🌐 &lt;a href=&quot;https://grafana.com/grafana/dashboards/25104-kafka-topic/&quot;&gt;&lt;strong&gt;Import from Grafana Gallery (ID: 25104)&lt;/strong&gt;&lt;/a&gt; | 📁 &lt;a href=&quot;https://github.com/factorhouse/factor-telemetry/blob/main/grafana-templates/kpow/kafka-topic.json&quot;&gt;&lt;strong&gt;View JSON Template&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c4ab0e6f04353da6b7db48_topic.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-kafka-consumer-group-deep-dive&quot;&gt;3. Kafka Consumer Group Deep Dive&lt;/h3&gt;
&lt;p&gt;Designed for Application Teams, this dashboard focuses on micro-level Service Level Agreement (SLA) monitoring.&lt;/p&gt;
&lt;p&gt;Instead of generic host metrics, it visualizes the exact state of your data consumption. Key metrics include precise total lag (&lt;code&gt;group_offset_lag&lt;/code&gt;) and real-time consumption rates (&lt;code&gt;group_offset_delta&lt;/code&gt;). It details total assigned members and hosts, and features a clear status table tracking the exact state of every consumer group to help engineers spot stalling applications before downstream users are impacted.&lt;/p&gt;
&lt;p&gt;🌐 &lt;a href=&quot;https://grafana.com/grafana/dashboards/25105-kafka-consumer-group/&quot;&gt;&lt;strong&gt;Import from Grafana Gallery (ID: 25105)&lt;/strong&gt;&lt;/a&gt; | 📁 &lt;a href=&quot;https://github.com/factorhouse/factor-telemetry/blob/main/grafana-templates/kpow/kafka-consumer-group.json&quot;&gt;&lt;strong&gt;View JSON Template&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c4ab2257bc7edbb2242277_consumer-group.gif&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-kafka-connect-operations&quot;&gt;4. Kafka Connect Operations&lt;/h3&gt;
&lt;p&gt;Data pipeline reliability depends heavily on integration health. This dashboard targets Kafka Connect deployments, replacing tedious API queries with instant visual feedback.&lt;/p&gt;
&lt;p&gt;It tracks aggregate summary statistics alongside individual Connector and Task states. By mapping state labels directly to distinct visual alerts (RUNNING, PAUSED, FAILED, UNASSIGNED, UNREACHABLE), teams can immediately detect stalled integrations and isolate whether the failure exists at the connector or task level.&lt;/p&gt;
&lt;p&gt;🌐 &lt;a href=&quot;https://grafana.com/grafana/dashboards/25106-kafka-connect/&quot;&gt;&lt;strong&gt;Import from Grafana Gallery (ID: 25106)&lt;/strong&gt;&lt;/a&gt; | 📁 &lt;a href=&quot;https://github.com/factorhouse/factor-telemetry/blob/main/grafana-templates/kpow/kafka-connect.json&quot;&gt;&lt;strong&gt;View JSON Template&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c4add5d41c8600f8138f33_connect.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Standard monitoring approaches leave teams struggling with noisy, low-level data. This Quality Gap makes historical analysis difficult and accurate alerting nearly impossible.&lt;/p&gt;
&lt;p&gt;By bypassing raw JMX metrics and leveraging Kpow’s self-calculated telemetry, you can instantly upgrade your Grafana dashboards with high-fidelity, actionable insights. This approach provides frictionless installation while delivering precise business context across Kafka, Kafka Connect, Schema Registry, and ksqlDB.&lt;/p&gt;
&lt;p&gt;With real-time diagnostics and high-fidelity metrics established, the final step to operational maturity is administrative accountability. In &lt;strong&gt;Part 3: Operational Transparency&lt;/strong&gt;, we will demonstrate how to close the Governance Gap by streaming Kpow’s real-time audit trail directly into your team’s communication channels via Kpow’s webhook integration.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Kafka Observability with Kpow: Driving Operational Excellence</title><link>https://factorhouse.io/articles/kafka-observability-with-kpow-driving-operational-excellence/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-observability-with-kpow-driving-operational-excellence/</guid><description>Apache Kafka is the central nervous system of the modern enterprise, yet operating it at scale often leads to reactive maintenance cycles. Identifying three critical gaps in context, data quality, and governance, this article introduces a comprehensive strategy to transform reactive troubleshooting into proactive operational excellence with Kpow.</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In the modern enterprise, Apache Kafka has evolved from a simple messaging queue into the central nervous system of the technology stack. It facilitates real-time data flow across microservices, databases, and analytics engines. However, the distributed nature of Kafka introduces significant operational complexity. When performance degrades or a data pipeline stalls, the impact is spread instantly across the entire organization. For engineering and platform teams, the challenge is rarely a lack of data, but rather a lack of actionable insight.&lt;/p&gt;
&lt;p&gt;Many organizations find themselves trapped in a reactive cycle: resolving incidents only after they have impacted downstream consumers and spending hours manually correlating logs with technical metrics. To break this cycle, teams need a structured approach to operational excellence. This article introduces a comprehensive strategy designed to overcome the three critical gaps inherent in traditional Kafka monitoring, demonstrating how Kpow transforms operations into proactive observability.&lt;/p&gt;
&lt;p&gt;To implement this strategy in your own environment, look out for our upcoming three-part operational guide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/rapid-kafka-diagnostics-a-unified-workflow-for-root-cause-analysis&quot;&gt;Rapid Kafka Diagnostics: A Unified Workflow for Root Cause Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/operational-transparency-audit-trail-integrated-with-webhooks&quot;&gt;Operational Transparency: Real-Time Audit Trail integrated with Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69012715e1261eed471ea07c_kpow-hero-data%20(2).png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;three-critical-gaps-in-traditional-kafka-monitoring&quot;&gt;Three Critical Gaps in Traditional Kafka Monitoring&lt;/h2&gt;
&lt;p&gt;Achieving a mature observability posture is frequently hindered by three fundamental gaps in how Kafka environments are managed.&lt;/p&gt;
&lt;h3 id=&quot;the-context-gap-the-difficulty-of-correlating-distributed-data&quot;&gt;The Context Gap: The Difficulty of Correlating Distributed Data&lt;/h3&gt;
&lt;p&gt;In a distributed system, a single issue often manifests in multiple places. Infrastructure metrics (such as broker CPU, memory, or disk I/O) typically reside in one monitoring tool, while application-level performance data (such as consumer group lag or producer throughput) resides in another. During a production incident, engineers must manually aggregate and correlate these data points to find the root cause. Without a unified view, it is difficult to determine if a slow consumer is the result of a saturated broker, a network bottleneck, or a logic error within the application itself. This absence of context leads to fragmented investigations and extended resolution times.&lt;/p&gt;
&lt;h3 id=&quot;the-quality-gap-the-limitation-of-raw-technical-metrics&quot;&gt;The Quality Gap: The Limitation of Raw Technical Metrics&lt;/h3&gt;
&lt;p&gt;Standard Kafka monitoring often relies on raw broker JMX metrics, which provide deep technical visibility but limited business context. Metrics such as message offsets or byte counters are difficult to interpret during incidents and do not directly convey user impact. In practice, the more meaningful signal is consumer group lag, ideally expressed as time-based delay, which reflects data freshness and downstream SLA risk. Many legacy, JMX-centric monitoring setups fail to compute or surface these higher-fidelity, derived metrics. As a result, teams are left with noisy dashboards that hinder incident response and make long-term capacity and performance trend analysis unreliable.&lt;/p&gt;
&lt;h3 id=&quot;the-governance-gap-the-risk-of-opaque-administrative-changes&quot;&gt;The Governance Gap: The Risk of Opaque Administrative Changes&lt;/h3&gt;
&lt;p&gt;As Kafka adoption grows, more teams gain the ability to interact with the cluster. In many organizations, critical administrative actions (such as managing topics, editing Kafka ACLs, or resetting consumer offsets) occur in an architectural black box. When a configuration change leads to an outage, there is often no centralized audit trail to identify what was changed or who performed the action. This lack of transparency introduces significant operational risk and makes it difficult to satisfy security or compliance requirements. Without a formal record of changes, teams are forced to spend valuable time retracing steps rather than resolving issues.&lt;/p&gt;
&lt;h2 id=&quot;the-kpow-solution-implementing-the-strategy&quot;&gt;The Kpow Solution: Implementing the Strategy&lt;/h2&gt;
&lt;p&gt;To overcome these gaps, organizations require a platform that integrates real-time diagnostics, high-fidelity metrics, and administrative transparency into a single, cohesive workflow. Kpow addresses these requirements across three key operational dimensions.&lt;/p&gt;
&lt;h3 id=&quot;closing-the-context-gap-with-a-unified-diagnostic-ui&quot;&gt;Closing the Context Gap with a Unified Diagnostic UI&lt;/h3&gt;
&lt;p&gt;Kpow provides a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;comprehensive, real-time interface&lt;/a&gt; designed specifically for the complexities of Kafka, serving as the primary single pane of glass for the organization. Rather than navigating between terminal screens and disconnected monitoring tools, engineering teams gain an immediate, holistic view of cluster health. The UI allows users to visualize the intricate relationships between brokers, topics, and consumers in one place. By providing this unified context, Kpow enables teams to move from observing a symptom to identifying a cause in seconds. This turns a complex, multi-team investigation into a streamlined and methodical diagnostic process.&lt;/p&gt;
&lt;h3 id=&quot;closing-the-quality-gap-with-the-kpow-prometheus-endpoint&quot;&gt;Closing the Quality Gap with the Kpow Prometheus Endpoint&lt;/h3&gt;
&lt;p&gt;While Kpow’s UI provides a high-resolution view for immediate diagnostics, mature operations can also require long-term data retention to understand historical trends. Kpow bridges this gap by acting as a high-fidelity data source for your existing monitoring stack. Through its dedicated &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/prometheus/overview&quot;&gt;Prometheus integration&lt;/a&gt;, Kpow exposes critical, pre-calculated data points, such as accurate consumer group lag in seconds, that are otherwise difficult to extract. This allows teams to populate Grafana dashboards with business-relevant insights, ensuring that long-term historical analysis and capacity planning are based on the true health of the data pipelines, not just raw JMX noise.&lt;/p&gt;
&lt;h3 id=&quot;closing-the-governance-gap-with-real-time-webhook-integrations&quot;&gt;Closing the Governance Gap with Real-Time Webhook Integrations&lt;/h3&gt;
&lt;p&gt;Operational excellence is built on a foundation of accountability and transparency. Kpow solves the governance challenge by providing an automated, real-time audit log of every action taken within the platform. Through its &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/webhook&quot;&gt;webhook integration&lt;/a&gt;, Kpow can stream a live record of administrative events (such as truncating topics, deleting schemas, or editing connector configurations) directly to the communication tools your team already uses (including Slack or Microsoft Teams). This ensures that every stakeholder has visibility into cluster changes as they happen, transforming Kafka administration from an opaque process into a fully auditable and transparent operation.&lt;/p&gt;
&lt;h2 id=&quot;achieving-operational-maturity&quot;&gt;Achieving Operational Maturity&lt;/h2&gt;
&lt;p&gt;Transitioning from reactive troubleshooting to proactive excellence requires a shift in how you view your infrastructure. It requires a strategy that provides immediate context, ensures high-quality data for both real-time and historical analysis, and enforces administrative governance. By utilizing a unified interface for diagnostics, enriching your existing monitoring stack with better metrics, and automating your audit trail, you can significantly reduce operational risk and improve the reliability of your most critical data systems.&lt;/p&gt;
&lt;p&gt;This article has outlined the strategic foundation. For the practical implementation, stay tuned for our upcoming series of operational guides. We encourage you to revisit this space as we release step-by-step workflows designed to help you put these principles into practice.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Operational Transparency: Real-Time Audit Trail Integrated with Webhooks</title><link>https://factorhouse.io/articles/operational-transparency-audit-trail-integrated-with-webhooks/</link><guid isPermaLink="true">https://factorhouse.io/articles/operational-transparency-audit-trail-integrated-with-webhooks/</guid><description>Operating Kafka without a transparent audit trail creates a critical &quot;Governance Gap&quot;, leaving teams blind to administrative changes and vulnerable during incidents. This guide demonstrates how to replace opaque log parsing and restrictive bureaucracy with automated governance by streaming Kpow&apos;s real-time audit log via webhooks directly into communication tools like Slack.</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In Part 1 of our observability series, we demonstrated how to close the “Context Gap” using Kpow’s unified diagnostic interface. In Part 2, we addressed the “Quality Gap” by supercharging Grafana dashboards with high-fidelity, pre-calculated metrics.&lt;/p&gt;
&lt;p&gt;Now, we turn our attention to the final hurdle of mature Kafka operations: transparent governance. Observability goes beyond tracking system health. It also requires a clear understanding of user actions. As Kafka adoption grows across an organization, administrative actions like managing topics, editing ACLs, or resetting consumer offsets become a major operational risk.&lt;/p&gt;
&lt;p&gt;This guide explores how to close the “Governance Gap” by moving from an opaque black box of CLI commands to a fully transparent, real-time audit trail integrated directly into your team’s communication channels.&lt;/p&gt;
&lt;p&gt;This is Part 3 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-observability-with-kpow-driving-operational-excellence&quot;&gt;Kafka Observability with Kpow: Driving Operational Excellence&lt;/a&gt; series.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/rapid-kafka-diagnostics-a-unified-workflow-for-root-cause-analysis&quot;&gt;Rapid Kafka Diagnostics: A Unified Workflow for Root Cause Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; Operational Transparency: Real-Time Audit Trail integrated with Webhooks &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem-architectural-black-box&quot;&gt;Problem: Architectural Black Box&lt;/h2&gt;
&lt;p&gt;Consider a standard outage scenario. A critical topic is mysteriously deleted, or a rogue script resets consumer offsets and triggers a massive wave of data reprocessing.&lt;/p&gt;
&lt;p&gt;The immediate pain is that the engineering team is completely blind. There is no central record of who executed the change, when it happened, or why. Incident resolution stalls while platform engineers ask around to see if anyone changed anything that day.&lt;/p&gt;
&lt;p&gt;Furthermore, this lack of traceability presents a severe compliance risk. For regulated industries, operating critical data infrastructure without an immutable, searchable record of administrative changes fails basic security and compliance audits.&lt;/p&gt;
&lt;h2 id=&quot;generic-solution-bureaucracy--brittle-parsing&quot;&gt;Generic Solution: Bureaucracy &amp;amp; Brittle Parsing&lt;/h2&gt;
&lt;p&gt;To manage administrative risk and establish an audit trail, organizations without a dedicated tool typically fall back on a combination of two generic approaches, both of which introduce significant friction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option A (Bureaucracy):&lt;/strong&gt; Platform teams lock down the cluster completely. Every minor configuration change must go through a disconnected ticketing process, waiting for an approval. Without an automated approval workflow, this manual gatekeeping effectively kills developer velocity and ruins any chance of providing a self-service data platform.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option B (Log Parsing):&lt;/strong&gt; Teams attempt to parse Kafka’s complex, raw server logs to piece together an audit trail. This is a brittle, highly technical process. It is difficult to maintain and almost impossible to alert on in real time, meaning you only find out about a destructive change after the incident has already occurred.&lt;/p&gt;
&lt;h2 id=&quot;solution-data-governance-and-webhook-integration&quot;&gt;Solution: Data Governance and Webhook Integration&lt;/h2&gt;
&lt;p&gt;Kpow solves the “Governance Gap” seamlessly by providing a complete framework built on two pillars: preventative access control and real-time observability.&lt;/p&gt;
&lt;p&gt;First, Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;Role-Based Access Control (RBAC)&lt;/a&gt; allows you to safely delegate self-service capabilities by mapping your organization’s existing user groups to specific Kafka actions. Instead of locking down the cluster entirely, you can enforce granular permissions. For example, you can grant application teams full control to edit, produce to, and inspect their specific domain topics (such as &lt;code&gt;payments_*&lt;/code&gt;), while explicitly denying access to sensitive audit topics. Meanwhile, platform administrators retain the global authority to &lt;a href=&quot;https://docs.factorhouse.io/kpow/management/acls&quot;&gt;manage Kafka ACLs&lt;/a&gt; and oversee cluster-wide infrastructure.&lt;/p&gt;
&lt;p&gt;Second, as part of its &lt;a href=&quot;https://docs.factorhouse.io/kpow/workflow/data-governance&quot;&gt;Data Governance&lt;/a&gt; capabilities, Kpow automatically records every authorized user action in a secure audit log. This captures state-changing actions known as mutations (such as creating topics, resetting offsets, or modifying ACLs) as well as read-only queries (such as searching topic data via Data Inspect).&lt;/p&gt;
&lt;p&gt;Moreover, instead of forcing administrators to constantly check a centralized dashboard to see these logs, Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/webhook&quot;&gt;Webhook Integration&lt;/a&gt; pushes these events instantly to the communication tools your team already lives in. Kpow seamlessly integrates with Slack, Microsoft Teams, and any custom platform via generic HTTP webhooks.&lt;/p&gt;
&lt;h2 id=&quot;putting-it-into-practice-configuring-a-slack-audit-trail&quot;&gt;Putting it into Practice: Configuring a Slack Audit Trail&lt;/h2&gt;
&lt;p&gt;The following step-by-step walkthrough demonstrates how to turn Kafka administrative actions into instant Slack alerts.&lt;/p&gt;
&lt;h3 id=&quot;step-1-configure-the-slack-app-and-webhook&quot;&gt;Step 1: Configure the Slack App and Webhook&lt;/h3&gt;
&lt;p&gt;To integrate Kpow with Slack, you need to create a Slack App and generate an incoming webhook URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a Slack app&lt;/strong&gt;: Navigate to the &lt;a href=&quot;https://api.slack.com/apps&quot;&gt;Slack API website&lt;/a&gt; and click on “Create New App”. Choose to create it “From scratch”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded2251fd41ebd227efc9a_slack-01.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Name your app and choose a workspace&lt;/strong&gt;: Provide a name for your application and select the Slack workspace you want to post messages to.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded24ab58f1bd22be5b380_slack-02.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enable incoming webhooks&lt;/strong&gt;: In your app’s settings page, go to “Incoming Webhooks” under the “Features” section. Toggle the feature on and then click “Add New Webhook to Workspace”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded26554219da85a59c020_slack-03.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Select a channel&lt;/strong&gt;: Choose the channel where you want the Kpow notifications to be posted and click “Allow”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded278e3c294a02e0421b5_slack-04.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy the webhook URL&lt;/strong&gt;: After authorizing, you will be redirected back to the webhook configuration page. Copy the newly generated webhook URL. This URL is what you will use to configure Kpow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded28b3614ecbec3ea85fe_slack-05.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-launch-kpow-with-webhook-environment-variables&quot;&gt;Step 2: Launch Kpow with Webhook Environment Variables&lt;/h3&gt;
&lt;p&gt;Once you have your webhook URL, you can configure Kpow using a few simple environment variables.&lt;/p&gt;
&lt;p&gt;Setting &lt;code&gt;WEBHOOK_VERBOSITY&lt;/code&gt; to &lt;code&gt;MUTATIONS&lt;/code&gt; ensures that Kpow only sends alerts for state-changing actions, preventing notification fatigue from standard data queries.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;WEBHOOK_PROVIDER:&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; slack&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;WEBHOOK_URL:&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;SLACK-WEBHOOK-UR&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;L&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Accepts MUTATIONS, QUERIES, or ALL (default: MUTATIONS)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;WEBHOOK_VERBOSITY:&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; MUTATIONS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-3-verify-the-integration&quot;&gt;Step 3: Verify the Integration&lt;/h3&gt;
&lt;p&gt;To test the integration, perform a state-changing action in Kpow, such as creating and deleting a topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View audit logs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After performing these actions, you can verify they have been logged by navigating to &lt;strong&gt;Settings &amp;gt; Audit log&lt;/strong&gt; in the Kpow UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded2daf2b97a727df70f6a_topic-logs.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the Slack channel, you should see messages detailing the actions. Each message includes information such as the user who performed the action, the type of action (e.g., &lt;code&gt;create-topic&lt;/code&gt;), and the cluster environment name.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69ded2ecc6cc6c768e03cc76_logs-slack.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When platform teams lack visibility into administrative actions, they are forced to rely on restrictive bureaucracy or brittle log parsing. Both options hinder developer productivity and extend incident resolution times.&lt;/p&gt;
&lt;p&gt;By combining granular RBAC with streaming Kpow’s built-in audit log directly into Slack, Microsoft Teams, or custom webhooks, you replace the architectural black box with transparent, automated governance. Every critical mutation is safely restricted and instantly visible to the team, satisfying compliance requirements while enabling developers to work securely and autonomously.&lt;/p&gt;
&lt;p&gt;This concludes our three-part series on driving operational excellence. By combining unified UI diagnostics (Part 1), high-fidelity Prometheus telemetry (Part 2), and transparent governance by real-time webhook integration (Part 3), you have the blueprint to transition your Kafka operations from a reactive guessing game into a proactive, mature observability posture.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Rapid Kafka Diagnostics: A Unified Workflow for Root Cause Analysis</title><link>https://factorhouse.io/articles/rapid-kafka-diagnostics-a-unified-workflow-for-root-cause-analysis/</link><guid isPermaLink="true">https://factorhouse.io/articles/rapid-kafka-diagnostics-a-unified-workflow-for-root-cause-analysis/</guid><description>The Context Gap caused by fragmented tools hinders effective Kafka monitoring and troubleshooting, as it forces engineers to manually piece together logs and metrics. This guide demonstrates how to close that gap using Kpow&apos;s unified workflow to identify the stall, inspect the data, and resolve the incident in a single interface.</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In distributed systems, the biggest barrier to resolving incidents is rarely a lack of data. It is the &lt;strong&gt;Context Gap&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When a data pipeline stalls, operational signals are often fragmented. Infrastructure metrics like broker CPU are isolated in one dashboard, while application performance data like consumer lag resides in another. Engineers are forced to manually correlate these disconnected views, wasting critical minutes switching between terminals and monitoring platforms to piece together a coherent picture of the system.&lt;/p&gt;
&lt;p&gt;Kpow eliminates this friction by providing a unified, real-time interface that bridges the gap between infrastructure, data, and consumers.&lt;/p&gt;
&lt;p&gt;In this guide, we demonstrate exactly how Kpow closes the Context Gap. Using a real-world “Zombie Consumer” scenario, we walk through a streamlined diagnostic workflow that allows you to move from a vague symptom to a definitive root cause and resolution.&lt;/p&gt;
&lt;p&gt;This is Part 1 of the &lt;a href=&quot;https://factorhouse.io/articles/kafka-observability-with-kpow-driving-operational-excellence&quot;&gt;Kafka Observability with Kpow: Driving Operational Excellence&lt;/a&gt; series. You can read the full strategy in the main series article and access the associated posts as they become available.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; Rapid Kafka Diagnostics: A Unified Workflow for Root Cause Analysis &lt;em&gt;(This article)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/beyond-jmx-supercharging-grafana-dashboards-with-high-fidelity-metrics&quot;&gt;Beyond JMX: Supercharging Grafana Dashboards with High-Fidelity Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/articles/operational-transparency-audit-trail-integrated-with-webhooks&quot;&gt;Operational Transparency: Real-Time Audit Trail integrated with Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69012715e1261eed471ea07c_kpow-hero-data%20(2).png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;scenario-silent-stall&quot;&gt;Scenario: Silent Stall&lt;/h2&gt;
&lt;p&gt;Imagine you are managing an &lt;code&gt;orders-fulfillment&lt;/code&gt; service consuming from an &lt;code&gt;orders&lt;/code&gt; topic with three partitions.&lt;/p&gt;
&lt;p&gt;The alert comes in from customer support. Some customers are complaining that their orders haven’t been completed. It is not a total system outage. Most orders are flowing through and generating shipping labels, but a persistent number of requests seem to be vanishing into a black hole.&lt;/p&gt;
&lt;p&gt;You immediately check your primary monitoring dashboard. The Kubernetes pod status is &lt;strong&gt;Running&lt;/strong&gt;. The overall service throughput looks active. There are no critical alerts for broker failures or network outages. According to your infrastructure tools, the system is healthy.&lt;/p&gt;
&lt;p&gt;In reality, a legacy upstream system has injected a “poison pill” message into the topic. Specifically, a JSON record where the &lt;code&gt;amount&lt;/code&gt; field was sent as a string (“ONE THOUSAND DOLLARS”) rather than the expected numeric representation. This schema mismatch has hit &lt;strong&gt;Partition 2&lt;/strong&gt;. It caused that specific consumer thread to crash, catch the error, seek back to the problematic offset, and retry in an infinite loop.&lt;/p&gt;
&lt;p&gt;Because other partitions are processing normally, the aggregated metrics mask the failure. To the Kafka broker, the stuck consumer looks alive because it is still sending heartbeats. To the customers whose orders landed on Partition 2, the service is dead.&lt;/p&gt;
&lt;h2 id=&quot;traditional-investigation-fragmented-tools&quot;&gt;Traditional Investigation: Fragmented Tools&lt;/h2&gt;
&lt;p&gt;Diagnosing this kind of partial failure usually forces you to piece together clues from multiple disconnected sources, wasting valuable time.&lt;/p&gt;
&lt;h3 id=&quot;infrastructure-check&quot;&gt;Infrastructure Check&lt;/h3&gt;
&lt;p&gt;Your first instinct is to check the infrastructure metrics in Datadog or Grafana. You look for CPU spikes or memory leaks, but you find nothing. Because the majority of the consumer instances are running without an issue, the aggregated metrics hide the single stuck instance. The failure is completely invisible at the infrastructure level.&lt;/p&gt;
&lt;h3 id=&quot;tooling-gap-skipping-the-producer&quot;&gt;Tooling Gap (Skipping the Producer)&lt;/h3&gt;
&lt;p&gt;Logically, your next step should be to verify the Producer: &lt;em&gt;is data actually entering the system?&lt;/em&gt; However, native CLI tools like &lt;code&gt;kafka-topics.sh&lt;/code&gt; provide configuration details, not real-time throughput. To check “messages per second” in the terminal requires hacky shell scripts or manual offset math. Because this is slow and painful, &lt;strong&gt;you are forced to skip the producer check entirely&lt;/strong&gt; and jump straight to the consumer, leaving a critical blind spot in your diagnosis.&lt;/p&gt;
&lt;h3 id=&quot;consumer-cli-query&quot;&gt;Consumer CLI Query&lt;/h3&gt;
&lt;p&gt;You switch context to &lt;code&gt;kafka-consumer-groups.sh&lt;/code&gt;. The output shows the group state is &lt;code&gt;Stable&lt;/code&gt;, which is technically true but misleading. However, looking closely at the partition list, you finally spot the anomaly: the &lt;code&gt;LAG&lt;/code&gt; for Partition 2 is skyrocketing while the others remain near zero. You now know &lt;em&gt;where&lt;/em&gt; the blockage is, but because the CLI cannot correlate this with live data, you have no clue &lt;em&gt;what&lt;/em&gt; caused it.&lt;/p&gt;
&lt;h3 id=&quot;log-dive&quot;&gt;Log Dive&lt;/h3&gt;
&lt;p&gt;Finally, you open Splunk or Elasticsearch to hunt for the root cause. You find a stream of &lt;code&gt;Schema Mismatch&lt;/code&gt; exceptions intermingled with thousands of successful processing logs. You can see the error type, but application logs often redact sensitive payloads or fail to capture the specific offset context. You know the consumer is choking on specific data, but you cannot see the message causing the choke. You cannot fix data you cannot see.&lt;/p&gt;
&lt;h2 id=&quot;kpow-workflow-unified-diagnostic-workflow&quot;&gt;Kpow Workflow: Unified Diagnostic Workflow&lt;/h2&gt;
&lt;p&gt;To address this, we replace the fragmented toolset with a unified workflow in Kpow. The following steps demonstrate how to troubleshoot the failure by tracing the path from the cluster down to the specific message. We then resolve the issue immediately, all within a single interface.&lt;/p&gt;
&lt;h3 id=&quot;step-1-broker-metrics---ruling-out-infrastructure&quot;&gt;Step 1: Broker Metrics - Ruling Out Infrastructure&lt;/h3&gt;
&lt;p&gt;Log in to Kpow and navigate to the &lt;strong&gt;Brokers&lt;/strong&gt; view.&lt;/p&gt;
&lt;p&gt;Because only some orders are missing, it is unlikely that the brokers are the root cause. However, it is best practice to perform a quick health check to be certain.&lt;/p&gt;
&lt;p&gt;In the &lt;strong&gt;Overview&lt;/strong&gt; tab, review the summary statistics and metrics graphs. You can see that the cluster throughput is active and there are no indicators of under-replicated partitions. The brokers are running fine.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699391898f67c45e2a598ff7_broker-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-topic-exploration---spotting-deviations&quot;&gt;Step 2: Topic Exploration - Spotting Deviations&lt;/h3&gt;
&lt;p&gt;Navigate to the &lt;strong&gt;Topics&lt;/strong&gt; menu.&lt;/p&gt;
&lt;p&gt;In the &lt;strong&gt;Overview&lt;/strong&gt; tab, select the &lt;code&gt;orders&lt;/code&gt; topic. At first glance, there is no clear evidence of an issue based on the summary statistics and metrics. The aggregate data looks healthy, and it is because the majority of partitions are still processing traffic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699391aafa9a450d3f164490_topic-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Switch to the &lt;strong&gt;Details&lt;/strong&gt; tab to investigate further. Look at the topic partitions table. Here you identify the discrepancy immediately. In &lt;strong&gt;Partition 2&lt;/strong&gt;, messages are being written continuously, but the read rate is zero. Traffic is entering the partition but nothing is leaving.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699391bd91a357a8f3523b90_topic-details.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-3-consumer-analysis---identifying-root-cause&quot;&gt;Step 3: Consumer Analysis - Identifying Root Cause&lt;/h3&gt;
&lt;p&gt;Navigate to the &lt;strong&gt;Consumers&lt;/strong&gt; menu and select the relevant consumer group.&lt;/p&gt;
&lt;p&gt;In the &lt;strong&gt;Overview&lt;/strong&gt; tab, you can spot an issue immediately. The total consumer lag is increasing, even though no idle members are detected and messages continue to be consumed globally.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699391e71935c4cf47e5f1ea_consumer-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Switch to the &lt;strong&gt;Details&lt;/strong&gt; tab for a granular view. You can now see that one specific group member is stuck on a particular offset. It is not reading any new data, and the lag for that specific partition is accumulating rapidly. To confirm what is blocking the pipeline, use the action menu on the right and select &lt;strong&gt;Inspect data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/6993921c642c84e1ebc2b188_consumer-details-inspect.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;This opens the &lt;strong&gt;Data&lt;/strong&gt; inspect view with the affected topic partition pre-selected. You can use &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/kjq/manual&quot;&gt;kJQ&lt;/a&gt; to filter the specific offset under investigation. By clicking the search button, you can confirm that the message value is malformed, causing the application logic to crash on this record.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699392514414f0445d61858e_data-inspect.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-4-resolution---executing-staged-mutations&quot;&gt;Step 4: Resolution - Executing Staged Mutations&lt;/h3&gt;
&lt;p&gt;Now that the poison pill is identified, you need to unblock the partition.&lt;/p&gt;
&lt;p&gt;Go back to the &lt;strong&gt;Consumers&lt;/strong&gt; menu. In the action menu for the stuck group member, select &lt;strong&gt;Skip offset&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69939272c81bbba918a721ca_consumer-details-skip-offset.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;This action initiates a &lt;a href=&quot;https://docs.factorhouse.io/kpow/workflow/staged-mutations&quot;&gt;&lt;strong&gt;Staged Mutation&lt;/strong&gt;&lt;/a&gt;, and its status is marked as &lt;em&gt;Scheduled&lt;/em&gt;. Note that, for Kpow to safely apply this offset change, the consumer group status must be &lt;strong&gt;Empty&lt;/strong&gt; to prevent state conflicts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69939298ec2a4be5acbc3f83_staged-mutations-scheduled.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Stop your consumer application instances. Kpow detects that the group has stopped and automatically applies the staged mutation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699392b39b7bfbe5c9d30a81_staged-mutations-success.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once the mutation status shows as succeeded, restart your consumer application. You can now see the consumers reading from all partitions as usual. The stuck lag drains immediately, and the missing orders begin to process.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/699392d3fb965aed19ba3540_consumer-details-resolved.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In the “Silent Stall” scenario, we saw that relying on fragmented tools creates a critical &lt;strong&gt;context gap&lt;/strong&gt;, forcing engineers to waste time manually correlating logs, metrics, and CLI output.&lt;/p&gt;
&lt;p&gt;Kpow solves this with a &lt;strong&gt;unified workflow&lt;/strong&gt;. By integrating consumer lag, data inspection, and resolution tools into a single interface, we transformed a complex investigation into a linear path. We didn’t just identify the poison pill; we resolved it immediately using &lt;strong&gt;Staged Mutations&lt;/strong&gt; without leaving the platform.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Part 2: Beyond JMX&lt;/strong&gt;, we will show you how to close the &lt;strong&gt;Quality Gap&lt;/strong&gt; by feeding high-fidelity Kafka metrics directly into your Grafana dashboards.&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;Explore Kpow in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you need assistance managing your Kafka environment, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Jaehyeon Kim</author></item><item><title>Integrate Kpow with StreamNative Cloud</title><link>https://factorhouse.io/articles/integrate-kpow-with-streamnative-cloud/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-streamnative-cloud/</guid><description>Integrate Kpow with StreamNative Cloud in minutes. Gain unified visibility and control over your managed Kafka brokers and Schema Registry through our market-leading engineering console.</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://streamnative.io/&quot;&gt;StreamNative&lt;/a&gt; delivers a fully managed, Kafka-native cloud service designed for exceptional elasticity and enterprise-grade resilience. However, as with any high-performance data infrastructure, engineering teams still require an intuitive way to monitor, manage, and explore their live data streams.&lt;/p&gt;
&lt;p&gt;Kpow serves as a versatile engineering toolkit for this modern streaming environment. Fully compatible with StreamNative Kafka out of the box, Kpow connects directly to your managed brokers and Schema Registry using standard Kafka protocols. This provides a unified, single-pane-of-glass experience without the need for proprietary plugins, sidecars, or complex custom configurations.&lt;/p&gt;
&lt;p&gt;Kpow connects natively to a wide range of Kafka vendors and managed service providers. See our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider&quot;&gt;Kafka Providers documentation&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to StreamNative Cloud, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running StreamNative Kafka cluster.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Details:&lt;/strong&gt; Your Kafka Bootstrap Server address and, optionally, Schema Registry URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; A Service Account email and its associated &lt;strong&gt;JWT Token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization:&lt;/strong&gt; The Service Account should be assigned a sufficiently privileged role, such as &lt;em&gt;instance-owner&lt;/em&gt; , to grant Kpow the broad administrative access required for effective management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to StreamNative Cloud is using Docker.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific cluster details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;StreamNative Kafka&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;[BOOTSTRAP_SERVER_ADDRESS]:9093&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;PLAIN&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&apos;org.apache.kafka.common.security.plain.PlainLoginModule required username=&quot;[SERVICE_ACCOUNT_EMAIL]&quot; password=&quot;[JWT_TOKEN]&quot;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;. You will see an overview of your OCI topics, brokers, and consumer groups.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb497ca1df0671600f1c66_kpow-stream-native.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;Connecting to StreamNative Cloud requires understanding a few specific authentication nuances.&lt;/p&gt;
&lt;h3 id=&quot;jwt-authentication-via-saslplain&quot;&gt;JWT Authentication via SASL/PLAIN&lt;/h3&gt;
&lt;p&gt;StreamNative uses JWT tokens for authentication. While standard Kafka often uses &lt;code&gt;OAUTHBEARER&lt;/code&gt; for tokens, StreamNative leverages the SASL/PLAIN mechanism where the “username” is your Service Account email and the “password” is the raw JWT token.&lt;/p&gt;
&lt;h3 id=&quot;access-control&quot;&gt;Access Control&lt;/h3&gt;
&lt;p&gt;As noted in the prerequisites, Kpow connects using the &lt;code&gt;instance-owner&lt;/code&gt; role to fetch metadata and execute management actions against the StreamNative APIs. Once Kpow is connected, you can configure Kpow’s internal &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/overview&quot;&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/a&gt; (via RBAC or Simple Access Control) to restrict exactly what individual engineers or teams are allowed to see and do within the UI.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Integrating Kpow with the StreamNative Schema Registry requires two specific configuration adjustments due to how the proxy is implemented.&lt;/p&gt;
&lt;h3 id=&quot;basic-authentication&quot;&gt;Basic Authentication&lt;/h3&gt;
&lt;p&gt;The StreamNative Schema Registry relies on Basic Authentication (&lt;code&gt;USER_INFO&lt;/code&gt;). However, it only validates the password (which must be your JWT token). The username can be &lt;strong&gt;any string&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;observation-version-requirement&quot;&gt;Observation Version Requirement&lt;/h3&gt;
&lt;p&gt;By default, Kpow uses an highly optimized “Version 2” observation strategy for Confluent-compatible registries. This strategy hits the &lt;code&gt;GET /schemas&lt;/code&gt; endpoint to download all schemas in one efficient bulk request.&lt;/p&gt;
&lt;p&gt;However, StreamNative Cloud’s Schema Registry proxy does not implement this bulk endpoint. It only implements the older &lt;code&gt;/subjects&lt;/code&gt; endpoints. Because the bulk endpoint does not exist, StreamNative will return a &lt;code&gt;404 Not Found&lt;/code&gt; error.&lt;/p&gt;
&lt;p&gt;In order to fix, you must tell Kpow to use the legacy “Version 1” observation strategy. This forces Kpow to query &lt;code&gt;/subjects&lt;/code&gt; first, and then fetch individual schemas one by one. You do this by setting SCHEMA_REGISTRY_OBSERVATION_VERSION=“1”.&lt;/p&gt;
&lt;p&gt;To attach the Schema Registry, append the following environment variables to your &lt;code&gt;docker run&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;StreamNative Schema Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;[SCHEMA_REGISTRY_URL]&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_AUTH=&quot;USER_INFO&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_USER=&quot;any-user&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_PASSWORD=&quot;[JWT_TOKEN]&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_OBSERVATION_VERSION=&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters running alongside your infrastructure, we recommend using our official Helm Charts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your StreamNative Cloud infrastructure. By using standard Kafka protocols and addressing the specific nuances of StreamNative’s proxy endpoints, you can unify your Kafka clusters and Schema Registry environments in minutes.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your StreamNative integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>What the IBM Confluent acquisition means for Kafka users</title><link>https://factorhouse.io/articles/what-the-ibm-confluent-acquisition-means-for-kafka-users/</link><guid isPermaLink="true">https://factorhouse.io/articles/what-the-ibm-confluent-acquisition-means-for-kafka-users/</guid><description>IBM&apos;s $11B Confluent acquisition raises questions for Kafka users. Assess your lock-in risk across Schema Registry, managed connectors, and operational tooling.</description><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On March 17, 2026, IBM completed its $11 billion acquisition of Confluent. The deal was announced in December and closed quickly. Since then, the question for anyone running Kafka in production has been straightforward: what changes, and what should we do about it?&lt;/p&gt;
&lt;h2 id=&quot;early-signals&quot;&gt;Early Signals&lt;/h2&gt;
&lt;p&gt;The early signs are not reassuring. Hundreds of former Confluent employees have appeared on LinkedIn with #opentowork since the acquisition closed. Unofficial reports suggest the number may be as high as 800. There’s been no official statement from IBM or Confluent on the scale of the cuts.&lt;/p&gt;
&lt;p&gt;For engineering and platform teams, the concern isn’t the layoffs themselves but what they imply about product velocity. Confluent’s value has always been driven by deep engineering investment: in the Kafka core, in the connector ecosystem, in Schema Registry, in the cloud platform. Fewer engineers means slower bug fixes, slower feature development, and less community engagement on KIPs. If you depend on Confluent’s managed connectors or cloud-native features, the pace at which those improve is directly relevant to your operational risk.&lt;/p&gt;
&lt;h2 id=&quot;what-history-tells-us&quot;&gt;What History Tells Us&lt;/h2&gt;
&lt;p&gt;Two precedents are worth studying, though neither is a perfect analogy.&lt;/p&gt;
&lt;p&gt;The first is Oracle’s acquisition of Sun Microsystems in 2010, which brought MySQL into Oracle’s hands. Over time the open-source release cadence slowed, and most Linux distributions switched their default database to MariaDB. Oracle never killed MySQL, but it deprioritised it. The key difference: Oracle’s core business (Oracle Database) competed directly with MySQL, giving Oracle an incentive to let it stagnate. IBM has no equivalent competing product, so the incentive structure is different.&lt;/p&gt;
&lt;p&gt;The more instructive precedent is Red Hat’s decision, under IBM ownership, to replace CentOS Linux with CentOS Stream in late 2020. CentOS had been a free, stable, binary-compatible rebuild of RHEL. Thousands of organisations ran production workloads on it precisely because it tracked a stable RHEL release. CentOS Stream changed the model to a rolling preview of the &lt;em&gt;next&lt;/em&gt; RHEL version. The product still existed, but the contract it had with its users (stability equivalent to RHEL) was broken. Organisations that had standardised on CentOS faced a forced migration they hadn’t planned for.&lt;/p&gt;
&lt;p&gt;The pattern in both cases is the same: the open-source project survived, but the terms of engagement changed in ways that imposed real costs on users who had built deep operational dependencies.&lt;/p&gt;
&lt;h2 id=&quot;where-the-risk-actually-sits&quot;&gt;Where the Risk Actually Sits&lt;/h2&gt;
&lt;p&gt;Apache Kafka the open-source project is likely safe. It has multiple companies invested in its development. AWS, Redpanda, Aiven, and others all have commercial interests in the Kafka protocol continuing to thrive. IBM has publicly stated its acquisition rationale depends on Kafka’s openness and adoption.&lt;/p&gt;
&lt;p&gt;The risk is in the commercial and proprietary layer. Specifically:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Confluent-managed connectors.&lt;/strong&gt; If your data pipelines use Confluent’s fully managed or licensed source and sink connectors, you have a hard dependency. There’s no direct equivalent on other platforms. The open-source Kafka Connect framework is portable, but the managed hosting and operational wrapper around it is not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema Registry.&lt;/strong&gt; Confluent Schema Registry is the de facto standard for schema management in Kafka environments, but it’s a Confluent product, not part of Apache Kafka. Alternative implementations exist (Apicurio Registry from Red Hat and AWS Glue Schema Registry being the most mature) but migration involves changing client configurations, compatibility testing, and potentially reworking CI/CD pipelines that integrate with the registry API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Confluent Cloud platform dependencies.&lt;/strong&gt; Features like cluster linking, Stream Governance, the Confluent Terraform provider, and the Confluent CLI tooling don’t have 1:1 replacements in the broader ecosystem. If your operational workflows are built around these, a migration is materially harder than “just point at a different broker.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pricing and licensing.&lt;/strong&gt; This is the quietest risk but potentially the most impactful. IBM has a well-established pattern of monetising enterprise customers through licensing changes. Confluent’s current pricing may not survive contact with IBM’s enterprise sales model.&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-now&quot;&gt;What To Do Now&lt;/h2&gt;
&lt;p&gt;You don’t need to migrate anything today. But you should know your exposure. A few concrete questions worth putting to your platform team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which Confluent-specific features are we actually using, versus standard Kafka APIs?&lt;/li&gt;
&lt;li&gt;If we had to move to MSK, Redpanda, or Aiven, what would break? What would need to be re-implemented?&lt;/li&gt;
&lt;li&gt;How much of our CI/CD and data pipeline tooling is tied to Confluent-specific features, versus standard APIs like Schema Registry or Kafka Admin that other providers also support?&lt;/li&gt;
&lt;li&gt;Are our monitoring and operational workflows coupled to Confluent Control Center?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point is sometimes overlooked. If your engineers’ daily Kafka workflows (topic inspection, consumer group management, ACL configuration, data inspection) run through Confluent tooling, that’s both a technical dependency and a familiarity dependency. It’s one of the things that makes a provider migration feel harder than it technically is.&lt;/p&gt;
&lt;p&gt;This is an area where decoupling is straightforward. Vendor-agnostic Kafka management tools exist specifically to work across providers. Factor House’s &lt;a href=&quot;/products/kpow&quot;&gt;Kpow&lt;/a&gt;, for example, works with &lt;a href=&quot;/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Confluent Cloud&lt;/a&gt;, &lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-aws&quot;&gt;AWS MSK&lt;/a&gt;, &lt;a href=&quot;https://factorhouse.io/how-to/integrate-kpow-with-redpanda&quot;&gt;Redpanda&lt;/a&gt;, Aiven, and self-managed Kafka. Regardless of which tool you choose, the underlying point is the same: building your observability and operational workflows on a tool that is independent of your Kafka provider means one less thing to migrate if your provider changes.&lt;/p&gt;
&lt;h2 id=&quot;what-to-watch&quot;&gt;What To Watch&lt;/h2&gt;
&lt;p&gt;A few signals that will indicate how this acquisition is playing out over the coming months:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka open-source contribution cadence.&lt;/strong&gt; Track commit activity from Confluent-affiliated contributors on the Apache Kafka repo. A sustained decline would suggest engineering resources are being redirected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KIP activity.&lt;/strong&gt; Kafka Improvement Proposals are the mechanism for protocol-level changes. If Confluent-driven KIPs slow down or shift toward IBM enterprise use cases, that tells you something about roadmap priorities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confluent Cloud pricing changes.&lt;/strong&gt; Any movement toward consumption-based pricing with enterprise minimums, or changes to the free tier, would signal IBM’s monetisation strategy taking hold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connector ecosystem investment.&lt;/strong&gt; Watch whether the managed connector catalogue continues to expand, or whether investment shifts toward IBM’s own integration tooling (App Connect, MQ).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is cause for panic. But the cost of understanding your dependencies now is low, and the cost of discovering them under pressure is high.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Chad Harris</author></item><item><title>Factor House expands to Europe</title><link>https://factorhouse.io/articles/factor-house-expands-to-europe/</link><guid isPermaLink="true">https://factorhouse.io/articles/factor-house-expands-to-europe/</guid><description>Discover how Factor House&apos;s expansion into Germany brings enterprise-grade control and monitoring to European teams running Kafka and Flink.</description><pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, Factor House is officially expanding into Europe with our first hire in Germany, our gateway to the broader European market.&lt;/p&gt;
&lt;p&gt;The demand has been building for some time. Over the past twelve months, our European sales have grown 178 percent. More than 23,000 engineers across Europe now log into our platform to manage critical Kafka and Flink infrastructure. Germany has emerged as our strongest European market by usage, making it the natural place to establish our first local presence.&lt;/p&gt;
&lt;p&gt;We’re thrilled to welcome Falko Schwarz as our first European hire. Falko brings nearly a decade of deep streaming industry experience from his previous role at &lt;a href=&quot;/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Confluent&lt;/a&gt; and will be working directly with enterprise teams across the region. He is the first of several planned hires as we build out dedicated European support.&lt;/p&gt;
&lt;h2 id=&quot;why-europe-why-now&quot;&gt;Why Europe, Why Now&lt;/h2&gt;
&lt;p&gt;The timing of this expansion is no accident. IBM’s acquisition of Confluent has sent a clear signal through the data streaming market: consolidation is here. For European enterprises, many of whom operate in heavily regulated industries with strong data sovereignty requirements, this creates real urgency. Organisations that built their streaming infrastructure on a single vendor’s ecosystem are now re-evaluating their tooling dependencies. They want management and governance solutions that work across Kafka and Flink environments regardless of the underlying provider, whether that’s AWS MSK, Confluent, GCP, or self-managed open source.&lt;/p&gt;
&lt;p&gt;At the same time, the operational complexity of streaming infrastructure continues to grow. Teams are managing sprawling multi-cluster environments, enforcing granular access controls, and supporting hundreds of engineering teams who need to inspect and debug their own data streams without waiting on a centralised platform team. Adding to the pressure, organisations are now opening up their streaming data to fuel AI initiatives, but doing so safely means ensuring fast data discovery, controlled access, and protection of sensitive data across an already unwieldy infrastructure. Especially in sectors like financial services and retail logistics, where real-time data is both business-critical and increasingly feeding AI workloads, the need for vendor-independent governance has moved from nice-to-have to essential.&lt;/p&gt;
&lt;h2 id=&quot;what-european-enterprises-are-building-with-us&quot;&gt;What European Enterprises Are Building With Us&lt;/h2&gt;
&lt;p&gt;This isn’t speculative demand. Major European organisations are already using Factor House to solve these exact problems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/case-studies/nord-lb&quot;&gt;&lt;strong&gt;NORD/LB&lt;/strong&gt;&lt;/a&gt;, a leading German bank, needed Kafka management tooling that met the stringent security and compliance standards of a regulated financial institution, without compromising on functionality. Since deploying Kpow, they’ve reduced debugging time by 30 percent, and the platform has become a core part of how their engineering team operates.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Having Factor House establish a presence in Germany strengthens our confidence in the partnership. Since deploying Kpow, we’ve reduced debugging time by 30%, and the platform has become a critical part of how our team operates.”&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;— Erik Schumann, Product Owner Data Quality Platform, NORD/LB&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A &lt;a href=&quot;/solutions/industry/retail&quot;&gt;&lt;strong&gt;global apparel and footwear company&lt;/strong&gt;&lt;/a&gt; managing 400 engineering teams across its Kafka deployment migrated to Kpow to gain the robust governance and access controls needed to keep that many developers moving fast without compromising the stability of their core infrastructure.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;/solutions/industry/logistics&quot;&gt;&lt;strong&gt;major Spanish supermarket chain&lt;/strong&gt;&lt;/a&gt; runs a mixed deployment of Confluent-managed Kafka alongside self-managed open-source Kafka clusters, processing continuous flows of payments, warehouse movements, and production data. With Kpow, they now centrally manage and monitor both environments through a single pane of glass, giving their team unified visibility across their entire streaming infrastructure without being locked into a single vendor’s tooling.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h2&gt;
&lt;p&gt;As Derek Troy-West, CEO and co-founder of Factor House, puts it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“European enterprises are sitting on some of the most sophisticated streaming infrastructure in the world, and the IBM-Confluent acquisition has every architect asking the same question: who else is in my stack, and do I control it? That’s our sweet spot. Our investment in Europe is just the beginning — we’re building a team that can sit across the table from these organisations and help them take back control of their streaming environments.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you’re managing complex Kafka or Flink infrastructure in Europe and want to see what Factor House can do, &lt;a href=&quot;/contact&quot;&gt;get in touch&lt;/a&gt; or &lt;a href=&quot;/products/kpow&quot;&gt;start a free trial&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Company</category><author>Kylie Troy-West</author></item><item><title>Self-Service Kafka Governance: Streamlining Workflows with Kpow and ServiceNow</title><link>https://factorhouse.io/articles/self-service-kafka-governance-with-kpow-and-servicenow/</link><guid isPermaLink="true">https://factorhouse.io/articles/self-service-kafka-governance-with-kpow-and-servicenow/</guid><description>Implement Just-in-Time Kafka access by integrating Kpow with ServiceNow. Automate approvals and temporary policy management to enhance security and developer self-service.</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Managing access to Kafka resources often presents a dilemma: how do you maintain strict security without slowing down engineering teams? Manual Role-Based Access Control (RBAC) updates are often slow, prone to human error, and create significant administrative bottlenecks.&lt;/p&gt;
&lt;p&gt;By integrating &lt;strong&gt;Kpow&lt;/strong&gt; with &lt;a href=&quot;https://www.servicenow.com/&quot;&gt;&lt;strong&gt;ServiceNow&lt;/strong&gt;&lt;/a&gt;, we can implement a “Just-in-Time” (JIT) access model. This approach allows users to request temporary permissions through a self-service portal, ensuring that access is granted only when needed and revoked automatically when a task is complete.&lt;/p&gt;
&lt;h3 id=&quot;key-benefits-of-this-integration&quot;&gt;Key Benefits of this Integration&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Just-in-Time Access:&lt;/strong&gt; Grant permissions only for the specific duration required for a task, reducing the risk of stale or over-privileged accounts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-Service Efficiency:&lt;/strong&gt; Empower engineers to request their own access via the ServiceNow Service Catalog, removing Kafka administrators from the critical path of daily data queries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardized Governance:&lt;/strong&gt; Leverage ServiceNow’s robust approval engine to ensure every access request is authorized by the correct data owner and fully audited.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational Security:&lt;/strong&gt; Scopes access to specific clusters and topics, enforcing the principle of least privilege while maintaining an automated lifecycle from request to revocation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; or grab a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day Trial license&lt;/a&gt; and dive into streaming tech on your laptop with &lt;a href=&quot;https://github.com/factorhouse/factorhouse-local&quot;&gt;Factor House Local&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow this guide, you will need the following components and configurations:&lt;/p&gt;
&lt;h3 id=&quot;servicenow-environment&quot;&gt;ServiceNow Environment&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ServiceNow PDI&lt;/strong&gt; : A Personal Developer Instance available for free at &lt;a href=&quot;https://developer.servicenow.com/&quot;&gt;developer.servicenow.com&lt;/a&gt;. It has been tested on the &lt;em&gt;Zurich&lt;/em&gt; release.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.servicenow.com/docs/r/washingtondc/integrate-applications/integration-hub/ih-plugins.html&quot;&gt;&lt;strong&gt;IntegrationHub Plugin&lt;/strong&gt;&lt;/a&gt;: The Professional or Enterprise version is required to use the &lt;strong&gt;REST and JSON Parser steps&lt;/strong&gt; in Action Designer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connectivity:&lt;/strong&gt; Valid network access from the ServiceNow cloud to your Kpow API. This can be achieved using a &lt;a href=&quot;https://www.servicenow.com/docs/r/servicenow-platform/mid-server/mid-server-landing.html&quot;&gt;MID Server&lt;/a&gt;, a cloud deployment, or a reverse proxy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;kpow-configuration&quot;&gt;Kpow Configuration&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API &amp;amp; Port:&lt;/strong&gt; Enable Kpow API (&lt;code&gt;API_ENABLED=true&lt;/code&gt;) on port &lt;code&gt;4000&lt;/code&gt; for this guide or the port on your configuration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auth &amp;amp; RBAC:&lt;/strong&gt; This guide uses &lt;a href=&quot;https://docs.factorhouse.io/kpow/authentication/file&quot;&gt;Jetty-based File Authentication&lt;/a&gt; with a &lt;code&gt;realm.properties&lt;/code&gt; (Auth) and &lt;code&gt;hash-rbac.yml&lt;/code&gt; (RBAC). Configure JAAS and environment variables (e.g., &lt;code&gt;AUTH_PROVIDER_TYPE=jetty&lt;/code&gt; and &lt;code&gt;API_AUTH_PROVIDER_TYPE=jetty&lt;/code&gt;) to manage credentials and access roles.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;credential-connection-and-alias&quot;&gt;&lt;a href=&quot;https://www.servicenow.com/docs/r/platform-security/connections-and-credentials/credentials-connections-alias.html&quot;&gt;Credential, Connection, and Alias&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Credential:&lt;/strong&gt; Create an HTTP Basic Auth record and enter the username and password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection and Alias:&lt;/strong&gt; Configure these records to link the credentials and the Kpow connection URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specific username, password, and connection URL values used for these records are found in the &lt;strong&gt;Environment Setup&lt;/strong&gt; section.&lt;/p&gt;
&lt;h2 id=&quot;environment-setup&quot;&gt;Environment Setup&lt;/h2&gt;
&lt;p&gt;This guide uses a Kafka cluster and Kpow instance deployed locally via Docker Compose. You can find the complete setup in the &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/features/servicenow&quot;&gt;&lt;code&gt;features/servicenow&lt;/code&gt;&lt;/a&gt; folder of the Factor House examples GitHub repository.&lt;/p&gt;
&lt;h3 id=&quot;servicenow-configuration-values&quot;&gt;ServiceNow Configuration Values&lt;/h3&gt;
&lt;p&gt;During the ServiceNow setup, you will need the following values derived from your Kpow configuration to establish the integration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Username:&lt;/strong&gt; The username defined in your &lt;code&gt;realm.properties&lt;/code&gt; (e.g., &lt;code&gt;admin&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password:&lt;/strong&gt; The password associated with that user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection URL:&lt;/strong&gt; The accessible endpoint of your Kpow API (e.g., &lt;code&gt;https://kpow.your-domain.com&lt;/code&gt; or your tunnel URL).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note on Cloud Connectivity:&lt;/strong&gt; Because the ServiceNow PDI is a cloud service, the &lt;strong&gt;Connection URL&lt;/strong&gt; must be an endpoint that is reachable over the internet or through a ServiceNow MID Server. Ensure that your firewall or proxy settings allow inbound traffic from ServiceNow to the Kpow API port (&lt;code&gt;4000&lt;/code&gt; for this guide or the port on your configuration).&lt;/p&gt;
&lt;h3 id=&quot;authentication-and-roles&quot;&gt;Authentication and Roles&lt;/h3&gt;
&lt;p&gt;We define two users in &lt;code&gt;jaas/hash-realm.properties&lt;/code&gt; with the following credentials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;admin (password: admin):&lt;/strong&gt; Assigned the &lt;code&gt;kafka-admins&lt;/code&gt; role. ServiceNow will use these credentials to create and delete temporary policies. Please ensure the user also has the &lt;em&gt;TOPIC_DATA_QUERY&lt;/em&gt; permission, as it is required to create a temporary policy that includes this action.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;user (password: password):&lt;/strong&gt; Assigned the &lt;code&gt;kafka-users&lt;/code&gt; role. This is our “standard user” who initially lacks permission to query data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;rbac/hash-rbac.yml&lt;/code&gt; file defines what these roles can do:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;admin_roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka-admins&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;authorized_roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka-admins&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka-users&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;policies&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;actions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # TOPIC_INSPECT encapsulates both TOPIC_DATA_QUERY and TOPIC_DATA_DOWNLOAD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;TOPIC_INSPECT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    effect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;Allow&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    resource&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    role&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka-admins&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Note: No &quot;Allow&quot; policies are defined for kafka-users initially.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;baseline-verification&quot;&gt;Baseline Verification&lt;/h3&gt;
&lt;p&gt;Before starting the ServiceNow integration, confirm that the standard user does not have permission to inspect a topic:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start the services from the &lt;a href=&quot;https://github.com/factorhouse/examples/blob/main/features/servicenow&quot;&gt;&lt;code&gt;features/servicenow&lt;/code&gt;&lt;/a&gt; directory:&lt;code&gt;docker compose up -d   &lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the &lt;code&gt;KPOW_LICENSE&lt;/code&gt; environment variable points to your license file before launching. Alternatively, place the license file in &lt;code&gt;./config/license.env&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Log into the Kpow UI (port 3000) as the &lt;strong&gt;user&lt;/strong&gt; (password: &lt;strong&gt;password&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Attempt to &lt;strong&gt;Inspect&lt;/strong&gt; any topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Result:&lt;/strong&gt; The user should be unable to view messages. This confirms our “Access Denied” starting point.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1fc101a81db764de4e7ee_initial-access-denied.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;api-example&quot;&gt;API Example&lt;/h3&gt;
&lt;p&gt;In this example, we configure a ServiceNow workflow to create and delete temporary policies using the Kpow Admin API. For full request/response schemas and additional fields, see the official documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow-api-reference#tag/admin/POST/admin/v1/temporary-policies&quot;&gt;Create a temporary policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow-api-reference#tag/admin/DELETE/admin/v1/temporary-policies/%7Bid%7D&quot;&gt;Delete a temporary policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Authentication is performed using &lt;strong&gt;HTTP Basic Auth&lt;/strong&gt; with admin user credentials. The &lt;code&gt;Authorization&lt;/code&gt; header must be included with every request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a Temporary Policy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;POST /admin/v1/temporary-policies&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Method:&lt;/strong&gt; &lt;code&gt;POST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path parameters:&lt;/strong&gt; None&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request body:&lt;/strong&gt; JSON payload (required)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headers:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Authorization: Basic &amp;lt;base64-encoded-credentials&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The request body must include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;duration_ms&lt;/code&gt;: How long the policy remains active (in milliseconds).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;policy&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;role&lt;/code&gt;: User role to which this temporary policy is assigned.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;actions&lt;/code&gt;: Actions associated with the temporary policy.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effect&lt;/code&gt;: &lt;code&gt;Allow&lt;/code&gt;, &lt;code&gt;Deny&lt;/code&gt; or &lt;code&gt;Stage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resource&lt;/code&gt;: Resource to which the policy applies to.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;API_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;http://localhost:4000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;AUTH_HEADER&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Authorization: Basic $(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;admin:admin&apos; &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; base64&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Create a temporary policy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; $API_URL&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/admin/v1/temporary-policies&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -X&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; POST&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -H&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;Content-Type: application/json&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -H&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$AUTH_HEADER&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -d&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;duration_ms&quot;: 3600000,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  &quot;policy&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;role&quot;: &quot;kafka-users&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;actions&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;      &quot;TOPIC_DATA_QUERY&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;effect&quot;: &quot;Allow&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    &quot;resource&quot;: [&quot;cluster&quot;, &quot;42RglGpZQwy9D5VzTVpCWA&quot;, &quot;topic&quot;, &quot;__oprtr_audit_log&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;}&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;‍&lt;/code&gt;&lt;strong&gt;Example Response&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;&quot;temporary_policy&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;```shell&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;API_URL=http&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//localhost:4000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;AUTH_HEADER=$(echo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;Authorization: Basic $(echo -n &apos;admin:admin&apos; | base64)&quot;&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; ID&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; returned&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;POLICY_ID=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;de&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1-8e1&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;-42e1&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;-a&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;99&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;-132e3&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;580e3&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;f&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; Delete&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; temporary&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; policy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; DELETE&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;  -H&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;$AUTH_HEADER&quot;&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;  $API_URL/admin/v&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#B31D28;font-style:italic&quot;&gt;/temporary-policies/$POLICY_ID&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;id&quot;: &quot;0de3f8b1-8e1b-42e1-a99f-132e3c580e3f&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;created_ts&quot;: 1772169681579,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;duration_ms&quot;: 3600000,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;policy&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;role&quot;: &quot;kafka-users&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;actions&quot;: [&quot;TOPIC_DATA_QUERY&quot;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;resource&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;cluster&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;42RglGpZQwy9D5VzTVpCWA&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;topic&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;__oprtr_audit_log&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;effect&quot;: &quot;Allow&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;},
“metadata”: {
“response_id”: “7751c40a-23b5-4a1f-aae5-86888fc67ced”,
“is_staged”: false,
“tenant_id”: “__kpow_global”
}
}&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;`‍`This request creates a temporary policy that grants the `kafka-users` role permission to perform `TOPIC_DATA_QUERY` on the specified topic for one hour (3,600,000 milliseconds).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;**Delete a Temporary Policy**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;```bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;API_URL=http://localhost:4000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;AUTH_HEADER=$(echo &quot;Authorization: Basic $(echo -n &apos;admin:admin&apos; | base64)&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# ID returned from the create request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;POLICY_ID=0de3f8b1-8e1b-42e1-a99f-132e3c580e3f&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Delete the temporary policy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;curl -X DELETE \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -H &quot;$AUTH_HEADER&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  $API_URL/admin/v1/temporary-policies/$POLICY_ID&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;‍&lt;/code&gt;A successful delete request returns no response body.&lt;/p&gt;
&lt;h3 id=&quot;skip-the-manual-setup-optional&quot;&gt;Skip the Manual Setup (Optional)&lt;/h3&gt;
&lt;p&gt;If you prefer to dive straight into the demonstration, you can skip the manual creation of actions, catalog items, and flows by importing a pre-configured ServiceNow Update Set.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the &lt;a href=&quot;https://github.com/factorhouse/examples/blob/main/features/servicenow/kpow_integration_update_set.xml&quot;&gt;&lt;code&gt;kpow_integration_update_set.xml&lt;/code&gt;&lt;/a&gt; file from the Factor House examples repository.&lt;/li&gt;
&lt;li&gt;In your ServiceNow PDI, navigate to &lt;strong&gt;System Update Sets &amp;gt; Retrieved Update Sets&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Import Update Set from XML&lt;/strong&gt; related link and upload the downloaded file.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Kpow Kafka Governance Integration&lt;/strong&gt; record, click &lt;strong&gt;Preview Update Set&lt;/strong&gt; , and then click &lt;strong&gt;Commit Update Set&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; After importing, you must still manually configure your &lt;strong&gt;Credential&lt;/strong&gt; and &lt;strong&gt;Connection&lt;/strong&gt; records as described in the &lt;strong&gt;Prerequisites&lt;/strong&gt; to point to your specific &lt;strong&gt;Connection URL&lt;/strong&gt; and API credentials.&lt;/p&gt;
&lt;h2 id=&quot;create-servicenow-actions&quot;&gt;Create ServiceNow Actions&lt;/h2&gt;
&lt;p&gt;ServiceNow Actions allow us to encapsulate Kpow API calls into reusable building blocks that can be easily dropped into any Flow. We will create two custom actions using the &lt;strong&gt;Action Designer&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kpow---create-temporary-policy&quot;&gt;Kpow - Create Temporary Policy&lt;/h3&gt;
&lt;p&gt;This action handles the JSON payload construction and parses the response to extract the unique ID of the newly created policy.&lt;/p&gt;
&lt;h4 id=&quot;define-inputs&quot;&gt;Define Inputs&lt;/h4&gt;
&lt;p&gt;We define four inputs that correspond to the parameters required by the Kpow API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Role Name&lt;/strong&gt; : The Kpow role to receive access (e.g., &lt;code&gt;kafka-users&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cluster ID&lt;/strong&gt; : The unique identifier of the Kafka cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic Name&lt;/strong&gt; : The specific topic for which access is requested.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration MS&lt;/strong&gt; : The lifespan of the policy in milliseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f6f6aea35cde0a6da5b9_create-policy-action-01-inputs.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;configure-the-rest-step&quot;&gt;Configure the REST Step&lt;/h4&gt;
&lt;p&gt;In the REST step, we configure the connection to our Kpow API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection Details&lt;/strong&gt; : Select the &lt;strong&gt;Kpow&lt;/strong&gt; Connection Alias created in the prerequisites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build Request&lt;/strong&gt; : Manual.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Path&lt;/strong&gt; : &lt;code&gt;/admin/v1/temporary-policies&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP Method&lt;/strong&gt; : &lt;code&gt;POST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headers&lt;/strong&gt; : Add &lt;code&gt;Content-Type: application/json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request Body&lt;/strong&gt; : We construct the JSON payload here. Use the &lt;strong&gt;Data Pill Picker&lt;/strong&gt; on the right to drag and drop the input variables into the JSON structure.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f7104c73f502353eff87_create-policy-action-02-rest-step.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;parse-the-json-response&quot;&gt;Parse the JSON Response&lt;/h4&gt;
&lt;p&gt;To revoke access later, we must capture the &lt;code&gt;id&lt;/code&gt; returned by Kpow. We use the &lt;strong&gt;JSON Parser&lt;/strong&gt; step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source Data&lt;/strong&gt; : Select the &lt;strong&gt;Response Body&lt;/strong&gt; from the REST step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Root Element&lt;/strong&gt; : Map the schema by pasting a sample response into the source editor and clicking &lt;strong&gt;Generate Target&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target Mapping&lt;/strong&gt; : Locate &lt;code&gt;root &amp;gt; temporary_policy &amp;gt; id&lt;/code&gt; and mark it as mandatory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f73268b803aeafef2b6b_create-policy-action-03-json-parser.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;define-outputs&quot;&gt;Define Outputs&lt;/h4&gt;
&lt;p&gt;Finally, create an action output named &lt;em&gt;Policy ID&lt;/em&gt; (&lt;code&gt;policy_id&lt;/code&gt;) and map it to the &lt;code&gt;id&lt;/code&gt; extracted by the JSON Parser. This makes the ID available for the “Delete” step in our Flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f74c1f348a3e41bfb472_create-policy-action-04-outputs.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;kpow---delete-temporary-policy&quot;&gt;Kpow - Delete Temporary Policy&lt;/h3&gt;
&lt;p&gt;This action is used to revoke access, either manually or at the end of a scheduled workflow.&lt;/p&gt;
&lt;h4 id=&quot;define-inputs-1&quot;&gt;Define Inputs&lt;/h4&gt;
&lt;p&gt;This action requires only a single input:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Policy ID&lt;/strong&gt; : The unique UUID of the policy to be deleted (captured from the Create action).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f76bdd602261fbfdd865_delete-policy-action-01-inputs.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;configure-the-rest-step-1&quot;&gt;Configure the REST Step&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection Details&lt;/strong&gt; : Use the same &lt;strong&gt;Kpow&lt;/strong&gt; Connection Alias.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP Method&lt;/strong&gt; : &lt;code&gt;DELETE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Path&lt;/strong&gt; : &lt;code&gt;/admin/v1/temporary-policies/{policy_id}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path Parameters&lt;/strong&gt; : Map the &lt;code&gt;{policy_id}&lt;/code&gt; variable in the path to the &lt;strong&gt;Policy ID&lt;/strong&gt; input defined in the first step.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f78152959f577d4f22b2_delete-policy-action-02-rest-step.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;When this action is executed, ServiceNow sends a DELETE request to Kpow, which immediately revokes the temporary permissions associated with that ID.&lt;/p&gt;
&lt;h2 id=&quot;create-servicenow-catalog-item&quot;&gt;Create ServiceNow Catalog Item&lt;/h2&gt;
&lt;p&gt;The Service Catalog item is the user-facing portal where team members can request access. This interface collects the specific Kafka details required to generate a temporary policy.&lt;/p&gt;
&lt;h3 id=&quot;configure-the-catalog-item&quot;&gt;Configure the Catalog Item&lt;/h3&gt;
&lt;p&gt;Navigate to &lt;strong&gt;Service Catalog &amp;gt; Catalog Definitions &amp;gt; Maintain Items&lt;/strong&gt; and create a new record with the following settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Request Kpow Topic Query Access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Catalogs:&lt;/strong&gt; Service Catalog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Category:&lt;/strong&gt; Software&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; Global&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Short Description:&lt;/strong&gt; Request temporary Just-in-Time (JIT) query access for a specific Kafka topic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f7a4c7fef88b82f808d7_catalog-item-create-01.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;define-variables&quot;&gt;Define Variables&lt;/h3&gt;
&lt;p&gt;To pass the correct data to the &lt;em&gt;Kpow - Create Temporary Policy&lt;/em&gt; action, we must create four variables. These variables act as the form fields the user fills out during the request.&lt;/p&gt;
&lt;p&gt;For this demonstration, we set all types to &lt;strong&gt;Single Line Text&lt;/strong&gt;. In a production environment, you might use &lt;em&gt;Select Box&lt;/em&gt; or &lt;em&gt;Reference&lt;/em&gt; types to pull cluster IDs or topic names directly from your &lt;em&gt;Configuration Management Database (CMDB)&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cluster ID:&lt;/strong&gt; The unique ID of the target Kafka cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic Name:&lt;/strong&gt; The name of the topic the user needs to inspect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role Name:&lt;/strong&gt; The Kpow role the user currently holds (e.g., &lt;code&gt;kafka-users&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration MS:&lt;/strong&gt; The length of time the access should remain active (e.g., &lt;code&gt;3600000&lt;/code&gt; for one hour).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use the &lt;strong&gt;Order&lt;/strong&gt; field to ensure the variables appear in a logical sequence on the request form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f7bf9b9a39f4350a4461_catalog-item-create-02.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once saved, this item is ready to be linked to our automated workflow in the next step.&lt;/p&gt;
&lt;h2 id=&quot;create-servicenow-flow&quot;&gt;Create ServiceNow Flow&lt;/h2&gt;
&lt;p&gt;With our custom actions and catalog item ready, we use &lt;strong&gt;Flow Designer&lt;/strong&gt; to orchestrate the end-to-end lifecycle. This flow manages the request from the initial request through to the automatic revocation of access.&lt;/p&gt;
&lt;h3 id=&quot;flow-overview&quot;&gt;Flow Overview&lt;/h3&gt;
&lt;p&gt;The flow (&lt;em&gt;Kpow Access Lifecycle&lt;/em&gt;) is triggered by the &lt;strong&gt;Service Catalog&lt;/strong&gt; and consists of six distinct steps designed to handle data extraction, governance, and integration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f7ee631bd79f570928b9_servicenow-flow.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-1-get-catalog-variables&quot;&gt;Step 1: Get Catalog Variables&lt;/h3&gt;
&lt;p&gt;To use the data submitted in the request form, we must first extract the variables.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Get Catalog Variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submitted Request:&lt;/strong&gt; Drag the &lt;strong&gt;Requested Item Record&lt;/strong&gt; pill from the trigger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Catalog Item:&lt;/strong&gt; Select &lt;strong&gt;Request Kpow Topic Query Access&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Variables:&lt;/strong&gt; Move all four variables (Cluster ID, Topic Name, Role Name, Duration MS) to the selected list.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f80b0d9d64393c051467_servicenow-flow-action-1.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-ask-for-approval&quot;&gt;Step 2: Ask For Approval&lt;/h3&gt;
&lt;p&gt;Before any technical changes occur, the request must be authorized.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Ask For Approval&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Record:&lt;/strong&gt; Requested Item Record&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rules:&lt;/strong&gt; Approve or Reject when &lt;strong&gt;Anyone approves or rejects&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approver:&lt;/strong&gt; Map this to &lt;code&gt;Trigger &amp;gt; Requested Item Record &amp;gt; Opened By &amp;gt; Manager&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Note: In a production environment, you would typically route this to Data Owners or Kafka Infrastructure Admins.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8245afe419954110d7f_servicenow-flow-action-2.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-3-kpow---create-temporary-policy&quot;&gt;Step 3: Kpow - Create Temporary Policy&lt;/h3&gt;
&lt;p&gt;Once approved, the flow triggers the Just-in-Time (JIT) access creation via the Kpow API.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Kpow - Create Temporary Policy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inputs:&lt;/strong&gt; Drag the variables extracted in &lt;strong&gt;Step 1&lt;/strong&gt; into the corresponding fields (Role, Cluster, Topic, and Duration).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f83cd3030519c0eabcb2_servicenow-flow-action-3.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-4-update-requested-item-record&quot;&gt;Step 4: Update Requested Item Record&lt;/h3&gt;
&lt;p&gt;We provide immediate feedback to the requester and create an audit trail by updating the ticket.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Update Record&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fields:&lt;/strong&gt; Set the &lt;strong&gt;Work notes&lt;/strong&gt; to include the unique Policy ID returned by Kpow: &lt;code&gt;Kpow Access Granted. Policy ID: {{Step 3.Policy ID}}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8566b94f004ab495aac_servicenow-flow-action-4.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-5-wait-for-condition&quot;&gt;Step 5: Wait For Condition&lt;/h3&gt;
&lt;p&gt;To allow the user time to perform their work, the flow pauses until the ticket is manually closed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Wait For Condition&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Record:&lt;/strong&gt; Requested Item Record&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditions:&lt;/strong&gt; &lt;code&gt;State&lt;/code&gt; &lt;code&gt;is&lt;/code&gt; &lt;code&gt;Closed Complete&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Result: The flow will “sleep” at this step, keeping the Kpow policy active until an admin or user closes the ticket.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f86bec80a813a65808ef_servicenow-flow-action-5.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-6-kpow---delete-temporary-policy&quot;&gt;Step 6: Kpow - Delete Temporary Policy&lt;/h3&gt;
&lt;p&gt;When the ticket is closed, the flow “wakes up” to clean up the temporary permissions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Action:&lt;/strong&gt; Kpow - Delete Temporary Policy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy ID:&lt;/strong&gt; Drag the &lt;strong&gt;Policy ID&lt;/strong&gt; pill from &lt;strong&gt;Step 3&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Result: Access is revoked immediately in Kpow, ensuring no stale permissions remain.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f88402a0f51cf8542a85_servicenow-flow-action-6.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;validating-the-workflow&quot;&gt;Validating the Workflow&lt;/h2&gt;
&lt;p&gt;To test our end-to-end workflow, we begin by clicking the &lt;strong&gt;Try It&lt;/strong&gt; button on our Catalog Item - &lt;em&gt;Request Kpow Topic Query Access&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8a288fd447b95773af7_end-to-end-01-item-create-01.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;We enter the necessary values for our Kafka environment and click &lt;strong&gt;Order Now&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cluster ID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic Name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role Name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration MS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8b6c71a6c2c49d7006d_end-to-end-02-item-create-02.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;We see the request status is immediately marked as &lt;strong&gt;Approved&lt;/strong&gt;. This occurs because our request is made by the System Administrator, who has no manager defined in this environment. In a production configuration, this would stay in a “Requested” state until manual approval is granted. Now, we can check if our temporary policy was created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8caa3b6ee2c266546e5_end-to-end-03-item-create-03.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;We login to Kpow as the &lt;strong&gt;admin&lt;/strong&gt; user (username: admin, password: admin). In &lt;strong&gt;Settings &amp;gt; Temporary policies&lt;/strong&gt;, we can verify that a temporary policy has been successfully created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f8ecdd602261fbfdfa07_end-to-end-04-kpow-admin.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, we login to Kpow as the &lt;strong&gt;standard user&lt;/strong&gt; (username: user, password: password). In &lt;strong&gt;Data &amp;gt; Inspect&lt;/strong&gt;, we can now successfully search the approved topic: &lt;code&gt;__oprtr_audit_log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f90bf03ff3c11359e958_end-to-end-05-kpow-user-01.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;As we expect, when we attempt to search any other topic, the request fails, confirming that our policy is restricted only to the resource we requested.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f925e7133188cfe566de_end-to-end-06-kpow-user-02.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;To test the policy deletion and access revocation, we update the state of the requested item. First, we select the specific &lt;strong&gt;Requested Item (RITM)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f9405fee4ce6e5f1d91c_end-to-end-07-item-close-01.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;We update the state to &lt;strong&gt;Closed Complete&lt;/strong&gt; and hit &lt;strong&gt;Update&lt;/strong&gt;. This triggers the final stage of our ServiceNow flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f9627a7f24b3bb9334be_end-to-end-08-item-close-02.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;When we revisit Kpow as the admin user, the temporary policy no longer appears in the list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1f97e2b4f7a4cd09b224a_end-to-end-09-kpow-admin.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, we can verify that the standard user no longer has permission to query the topic, completing the full access lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69b1fc101a81db764de4e7ee_initial-access-denied.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By integrating Kpow’s Admin API with ServiceNow’s Flow Designer, we have successfully moved from a static, manual security model to a dynamic and governed access framework. This self-service approach empowers engineering teams with the access they need to move fast while providing security teams with the peace of mind that every permission is authorized, scoped, and automatically revoked.&lt;/p&gt;
&lt;p&gt;Whether you are looking to minimize your administrative overhead or strictly enforce zero-trust principles within your data streaming environment, this integration provides a robust template for Kafka governance. We invite you to explore the &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/features/servicenow&quot;&gt;Factor House examples repository&lt;/a&gt; to start building your own Just-in-Time access workflows today.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Beyond Reagent: Migrating to React 19 with HSX and RFX</title><link>https://factorhouse.io/articles/beyond-reagent-migrating-to-react-19-with-hsx-and-rfx/</link><guid isPermaLink="true">https://factorhouse.io/articles/beyond-reagent-migrating-to-react-19-with-hsx-and-rfx/</guid><description>Introducing two new open sources Clojure UI libraries by Factor House. HSX and RFX are drop-replacements for Reagent and Re-Frame, allowing us to migrate to React 19 while maintaining a familiar developer experience with Hiccup and similar data-driven event model.</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reagent-project/reagent&quot;&gt;&lt;strong&gt;Reagent&lt;/strong&gt;&lt;/a&gt; is a popular library that enables ClojureScript developers to write clean and concise React components using simple Clojure data structures and functions. Reagent is commonly used with &lt;a href=&quot;https://github.com/day8/re-frame&quot;&gt;&lt;strong&gt;re-frame&lt;/strong&gt;&lt;/a&gt;, a simlarly popular ClojureScript UI library.&lt;/p&gt;
&lt;p&gt;Both libraries have been fundamental to how we build modern, sophisticated, &lt;a href=&quot;https://factorhouse.io/blog/articles/web-accessibility-at-factor-house/&quot;&gt;&lt;strong&gt;accessible&lt;/strong&gt;&lt;/a&gt; UI/UX at Factor House over the past seven years.&lt;/p&gt;
&lt;p&gt;Now, after 121 product releases, we have replaced Reagent and re-frame in our products with two new libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/hsx&quot;&gt;&lt;strong&gt;HSX&lt;/strong&gt;&lt;/a&gt;: a Hiccup-to-React compiler that lets us write components the way we always have, but produces pure React function components under the hood.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/rfx&quot;&gt;&lt;strong&gt;RFX&lt;/strong&gt;&lt;/a&gt;: a re-frame-inspired subscription and event system built entirely on React hooks and context.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Our front-end tech stack was starting to accumulate technical debt, not from the usual entropy of growing software, but from the slow diversion of Reagent from the underlying Javascript library that it leverages - &lt;a href=&quot;https://github.com/facebook/react&quot;&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In many ways, Reagent was ahead of its time. Simple state primitives (the Ratom), function components, and even batched updates for state changes were innovations Reagent offered well before React itself. It provided a remarkably elegant abstraction that made building UIs in ClojureScript a joy.&lt;/p&gt;
&lt;p&gt;But it’s now 2025. React has caught up and in many areas surpassed those early innovations. Hooks offer state management. Concurrent rendering and built-in batched updates are first-class features. While it took React a decade to reach this point, the landscape has undeniably shifted.&lt;/p&gt;
&lt;p&gt;Unfortunately, Reagent hasn’t kept pace. Its internals are built around class-based components and are increasingly at odds with React’s architecture. Most critically for us, &lt;a href=&quot;https://github.com/reagent-project/reagent/issues/597#issuecomment-1908054952&quot;&gt;&lt;strong&gt;Reagent is fundamentally incompatible with React 19’s new internals&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This incompatibility created serious technical debt for us at Factor House. More and more of our vital front-end dependencies, from libraries for virtualized lists to accessible UI components, are starting to require React 18 or 19. Without a way forward, we risked stagnation.&lt;/p&gt;
&lt;p&gt;However, &lt;strong&gt;we deeply value&lt;/strong&gt; what Reagent and re-frame gave us - a simple, expressive syntax based on &lt;a href=&quot;https://github.com/weavejester/hiccup&quot;&gt;&lt;strong&gt;Hiccup&lt;/strong&gt;&lt;/a&gt;, and a clean event-driven model. We didn’t want to abandon these strengths. Instead, we chose to move forward by building &lt;strong&gt;new libraries&lt;/strong&gt;, ones that preserve the spirit of Reagent and re-frame and modernize their foundations to align with today’s React.&lt;/p&gt;
&lt;p&gt;In this post, we’ll walk you through why we had to move beyond Reagent and re-frame, how we built new libraries to modernize our stack, and the real-world outcomes of embracing React 19’s capabilities.&lt;/p&gt;
&lt;h2 id=&quot;the-migration-challenge&quot;&gt;The Migration Challenge&lt;/h2&gt;
&lt;p&gt;Our goal wasn’t to rewrite our entire front-end stack, but to modernize it. That meant preserving two things that serve us well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to write React components using &lt;strong&gt;Hiccup-style markup&lt;/strong&gt;, which we now call &lt;strong&gt;HSX&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Continued use of &lt;strong&gt;re-frame’s event and subscription model&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the same time, we wanted to align ourselves much more closely with React’s internals. We were ready to fully embrace idiomatic React. That meant we were happy to let go of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ratoms&lt;/strong&gt; — in favor of React’s native &lt;strong&gt;&lt;code&gt;useState&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;useReducer&lt;/code&gt;&lt;/strong&gt; primitives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Class-based components&lt;/strong&gt; — which are no longer relevant in a hooks-first React world.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing to move away from Reagent’s internals, especially Ratoms, was not a loss. To us Ratoms were always an implementation detail. Since we already manage app state through re-frame subscriptions, local component state was minimal.&lt;/p&gt;
&lt;p&gt;So the real migration challenge became this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Could we capture the spirit of Reagent and re-frame — using nothing but React itself?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And if we could, would the resulting behavior and performance match (or exceed) what we had before?&lt;/p&gt;
&lt;p&gt;With these in hand, we were ready to test them where it matters most: against our real-world products. Both &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex for Apache Flink&lt;/strong&gt;&lt;/a&gt; are complex, enterprise-grade applications. Could HSX and RFX support them without regressions? Could we maintain backward compatibility, migrate incrementally, and still unlock the benefits of React 19?&lt;/p&gt;
&lt;p&gt;These were the questions we set out to answer, and as we’ll see, they led to some surprising and exciting results.&lt;/p&gt;
&lt;h2 id=&quot;migrating-kpow-and-flex&quot;&gt;Migrating Kpow and Flex&lt;/h2&gt;
&lt;p&gt;We began by sketching out minimal viable implementations of &lt;strong&gt;HSX&lt;/strong&gt; and &lt;strong&gt;RFX&lt;/strong&gt; — enough to prove the migration path could work.&lt;/p&gt;
&lt;h3 id=&quot;hsx-building-a-modern-hiccup-to-react-layer&quot;&gt;HSX: Building a Modern Hiccup-to-React Layer&lt;/h3&gt;
&lt;p&gt;For HSX, the first goal was essentially to reimplement the behavior of &lt;strong&gt;&lt;code&gt;reagent.core/as-element&lt;/code&gt;&lt;/strong&gt;. We required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The same props and tag SerDes logic as Reagent.&lt;/li&gt;
&lt;li&gt;Special operators like Fragments (&lt;strong&gt;&lt;code&gt;:&amp;lt;&amp;gt;&lt;/code&gt;&lt;/strong&gt;) and direct React component invocation (&lt;strong&gt;&lt;code&gt;:&amp;gt;&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Memoization semantics — essentially replicating Reagent’s implicit &lt;strong&gt;&lt;code&gt;shouldComponentUpdate&lt;/code&gt;&lt;/strong&gt; optimization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This would allow us to preserve the developer experience of writing Hiccup-style components while outputting React function components under the hood.&lt;/p&gt;
&lt;h3 id=&quot;rfx-reimagining-re-frame-on-pure-react-foundations&quot;&gt;RFX: Reimagining re-frame on Pure React Foundations&lt;/h3&gt;
&lt;p&gt;Migrating re-frame was more challenging because of its much larger API surface area. We needed to implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Subscriptions, events, coeffects, effects — the full re-frame public API.&lt;/li&gt;
&lt;li&gt;A global state store compatible with React Hooks.&lt;/li&gt;
&lt;li&gt;A queuing system for efficiently processing dispatched events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing functions like &lt;strong&gt;&lt;code&gt;reg-event-db&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;subscribe&lt;/code&gt;&lt;/strong&gt; was straightforward. The bigger challenge was syncing global state changes into the React UI without relying on Ratoms and ‘reactions’.&lt;/p&gt;
&lt;p&gt;To solve this, we initially deferred a custom solution and instead leaned on a battle-tested JavaScript library: &lt;a href=&quot;https://github.com/pmndrs/zustand&quot;&gt;&lt;strong&gt;Zustand&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For event queuing, we adapted re-frame’s own &lt;a href=&quot;https://github.com/day8/re-frame/blob/master/src/re_frame/router.cljc&quot;&gt;&lt;strong&gt;FIFO router&lt;/strong&gt;&lt;/a&gt;, which was pleasantly decoupled from Reagent internals and easily portable.&lt;/p&gt;
&lt;h3 id=&quot;first-steps-in-production-tweaking-kpow-and-flex&quot;&gt;First Steps in Production: Tweaking Kpow and Flex&lt;/h3&gt;
&lt;p&gt;With early versions of HSX and RFX in hand, we moved quickly to integrate them into our products. The migration required surprisingly few code changes at the application level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replacing &lt;strong&gt;&lt;code&gt;reagent.core/atom&lt;/code&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;code&gt;react/useState&lt;/code&gt;&lt;/strong&gt; where needed (thankfully very few places).&lt;/li&gt;
&lt;li&gt;Replacing &lt;strong&gt;&lt;code&gt;reagent.core/as-element&lt;/code&gt;&lt;/strong&gt; calls with &lt;strong&gt;&lt;code&gt;io.factorhouse.hsx.core/create-element&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Replacing &lt;strong&gt;&lt;code&gt;react/createRef&lt;/code&gt;&lt;/strong&gt; calls with &lt;strong&gt;&lt;code&gt;react/useRef&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Updating the entry points to use the &lt;strong&gt;&lt;code&gt;react-dom/client&lt;/code&gt;&lt;/strong&gt; API (&lt;strong&gt;&lt;code&gt;createRoot&lt;/code&gt;&lt;/strong&gt;) instead of the legacy &lt;strong&gt;&lt;code&gt;render&lt;/code&gt;&lt;/strong&gt; method.&lt;/li&gt;
&lt;li&gt;Introducing a &lt;strong&gt;&lt;code&gt;re-frame.core&lt;/code&gt;&lt;/strong&gt; shim namespace over RFX, mapping 1:1 to the re-frame public API and requiring no migration for event handlers or subscriptions!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these adjustments in place, and some rapid iteration on HSX and RFX, we were able to compile and run Kpow (our larger application at ~60,000 lines of ClojureScript) entirely on top of React 19!&lt;/p&gt;
&lt;p&gt;The first results were rough: performance was poor and some pages failed to render correctly.&lt;/p&gt;
&lt;p&gt;But critically, &lt;strong&gt;the foundation worked&lt;/strong&gt; and these early failures became the catalyst for aggressively refining and productionizing our libraries.&lt;/p&gt;
&lt;h2 id=&quot;optimizing-hsx-learnings-along-the-way&quot;&gt;Optimizing HSX: Learnings Along the Way&lt;/h2&gt;
&lt;p&gt;As we moved toward a pure React model, we found ourselves learning a lot more about React’s internals. Sometimes the hard way.&lt;/p&gt;
&lt;p&gt;The biggest issue we faced stemmed from React’s &lt;strong&gt;reliance on referential equality&lt;/strong&gt;. In React, referential equality (whether two variables point to the same object in memory) underpins how React identifies components across renders and how it optimizes updates, handles memoization, etc.&lt;/p&gt;
&lt;p&gt;This presented a fundamental problem for HSX:&lt;/p&gt;
&lt;p&gt;Just like Reagent, HSX creates React elements &lt;strong&gt;dynamically at runtime&lt;/strong&gt; (when &lt;strong&gt;&lt;code&gt;io.factorhouse.hsx.core/create-element&lt;/code&gt;&lt;/strong&gt; is called).&lt;/p&gt;
&lt;p&gt;Unlike other ClojureScript React templating libraries, we do not rely on Clojure macros to precompile Hiccup into React elements.&lt;/p&gt;
&lt;p&gt;We quickly encountered several major symptoms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Component memoization failed:&lt;/strong&gt; React could not track components properly across renders.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hook rule violations:&lt;/strong&gt; Clicking around the app often triggered &lt;a href=&quot;https://react.dev/reference/rules/rules-of-hooks&quot;&gt;&lt;strong&gt;Hook violations&lt;/strong&gt;&lt;/a&gt;, a sign that React’s internal assumptions were being broken.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal React errors:&lt;/strong&gt; Most concerning was the obscure: &lt;a href=&quot;https://github.com/facebook/react/issues/24391&quot;&gt;&lt;strong&gt;Internal React error: Expected static flag was missing&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To understand why, consider a simple example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(defn my&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;component [props text] &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;[:div props text])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HSX compiles this by creating a proxy function component that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maps React’s &lt;strong&gt;&lt;code&gt;props&lt;/code&gt;&lt;/strong&gt; object to a Reagent-style function signature.&lt;/li&gt;
&lt;li&gt;Compiles the returned Hiccup (&lt;strong&gt;&lt;code&gt;[:div props text]&lt;/code&gt;&lt;/strong&gt;) into a React element via &lt;strong&gt;&lt;code&gt;react/createElement&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem is that a &lt;strong&gt;new intermediate proxy function&lt;/strong&gt; is created between renders, even if the logic is identical.&lt;/p&gt;
&lt;p&gt;React, relying on referential equality, treated each instance as a brand-new component, thus resulting in the above bugs.&lt;/p&gt;
&lt;h3 id=&quot;the-solution-weakmap-based-caching&quot;&gt;The Solution: WeakMap-Based Caching&lt;/h3&gt;
&lt;p&gt;Our solution was a simple but powerful idea: &lt;strong&gt;cache the translated component functions&lt;/strong&gt; using a JavaScript &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap&quot;&gt;&lt;strong&gt;WeakMap&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keys&lt;/strong&gt;: the user-defined HSX components (e.g., &lt;strong&gt;&lt;code&gt;my-div-component&lt;/code&gt;&lt;/strong&gt;), which have stable memory references.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Values&lt;/strong&gt;: the compiled React function components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using a WeakMap was essential, without it the cache could grow unbounded if components created new anonymous functions every render.&lt;/p&gt;
&lt;p&gt;WeakMaps automatically clean up entries when keys (functions) are garbage collected.&lt;/p&gt;
&lt;p&gt;However, this approach revealed a secondary problem: &lt;strong&gt;Higher-Order Components (HOCs)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-hidden-trap-anonymous-functions-and-hocs&quot;&gt;The Hidden Trap: Anonymous Functions and HOCs&lt;/h3&gt;
&lt;p&gt;When users define &lt;strong&gt;anonymous functions inside render methods&lt;/strong&gt;, React treats them as &lt;a href=&quot;https://legacy.reactjs.org/docs/higher-order-components.html&quot;&gt;&lt;strong&gt;Higher-Order Components&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(defn my&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;complex&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;component [props text]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;	(let [inner&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (fn [] [:div props text])]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;		[inner&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;component]))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, &lt;strong&gt;&lt;code&gt;inner-component&lt;/code&gt;&lt;/strong&gt; is redefined every render, breaking referential equality, exactly the problem we had just solved. This exact issue is even highlighted in the &lt;a href=&quot;https://legacy.reactjs.org/docs/higher-order-components.html#dont-use-hocs-inside-the-render-method&quot;&gt;&lt;strong&gt;legacy React docs&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To address this, we added &lt;strong&gt;explicit logging and warnings&lt;/strong&gt; whenever HSX detected HOC-like patterns during compilation.&lt;/p&gt;
&lt;p&gt;This forced us to clean up the codebase by refactoring anonymous components into top-level named components.&lt;/p&gt;
&lt;p&gt;Unexpectedly, this not only improved correctness but &lt;strong&gt;significantly improved performance&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Even when using Reagent previously, anonymous functions inside components had led to unnecessary re-renders, an invisible cost that we were now able to eliminate.&lt;/p&gt;
&lt;h2 id=&quot;optimizing-rfx-learnings-along-the-way&quot;&gt;Optimizing RFX: Learnings Along the Way&lt;/h2&gt;
&lt;p&gt;The challenge with RFX was twofold:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Without Ratoms, how would we &lt;strong&gt;sync application state to the UI&lt;/strong&gt; efficiently and correctly?&lt;/li&gt;
&lt;li&gt;How could we faithfully reimplement &lt;strong&gt;re-frame’s subscription graph&lt;/strong&gt;, ensuring minimal recomputation when parts of the database change?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;signal-graph-re-frames-core-innovation&quot;&gt;Signal Graph: Re-frame’s Core Innovation&lt;/h3&gt;
&lt;p&gt;In re-frame, subscriptions form a DAG called the &lt;a href=&quot;https://github.com/day8/re-frame/blob/master/docs/subscriptions.md#subscriptions&quot;&gt;&lt;strong&gt;signal graph&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Subscriptions can depend on other subscriptions (&lt;strong&gt;materialised views&lt;/strong&gt;), and on each state change, re-frame walks this graph and only recomputes nodes where upstream values have changed.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;;; :foo will update on every app&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;db change&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(reg&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;sub :foo [db _] (:foo db))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;;; :consumes&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;foo will update only when the value &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;of&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; :foo changes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;	(reg&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;sub :consumes&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;foo  :&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [:foo]  (fn [foo _]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;		(str &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Consumed foo&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;:foo&lt;/code&gt;&lt;/strong&gt; listens directly to the app-db and updates on every change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;:consumes-foo&lt;/code&gt;&lt;/strong&gt; listens to &lt;strong&gt;&lt;code&gt;:foo&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;only recomputes&lt;/strong&gt; if &lt;strong&gt;&lt;code&gt;:foo&lt;/code&gt;&lt;/strong&gt;’s output changes, not just because the db changed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This graph-based optimization is a key reason re-frame scales so well even in complex applications.&lt;/p&gt;
&lt;h3 id=&quot;usesyncexternalstore-the-missing-piece&quot;&gt;useSyncExternalStore: The Missing Piece&lt;/h3&gt;
&lt;p&gt;Fortunately, React 18+ provides a new primitive that fits our needs perfectly: &lt;a href=&quot;https://react.dev/reference/react/useSyncExternalStore&quot;&gt;&lt;strong&gt;useSyncExternalStore&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This hook allows external data sources to integrate cleanly with React. We used this to &lt;strong&gt;wrap a regular ClojureScript atom&lt;/strong&gt;, turning it into a fully React-compatible external store.&lt;/p&gt;
&lt;p&gt;On top of this, we layered the store’s signal graph logic: fine-grained &lt;strong&gt;subscription invalidation and recomputation&lt;/strong&gt; based on upstream changes.&lt;/p&gt;
&lt;h2 id=&quot;accounting-for-differences&quot;&gt;Accounting for Differences&lt;/h2&gt;
&lt;p&gt;With HSX and RFX at a production-grade checkpoint, it was time to &lt;strong&gt;audit Kpow’s functionality&lt;/strong&gt; and &lt;strong&gt;identify any performance regressions&lt;/strong&gt; between the old (Reagent-based) and new (React 19-based) implementations.&lt;/p&gt;
&lt;p&gt;As we touched on earlier, the key architectural difference was &lt;strong&gt;relying on React’s&lt;/strong&gt; &lt;a href=&quot;https://dev.to/this-is-learning/automatic-batching-in-react-18-273h&quot;&gt;&lt;strong&gt;batched updates&lt;/strong&gt;&lt;/a&gt; instead of Reagent’s custom batching system.&lt;/p&gt;
&lt;p&gt;Up to this point we had Kpow running on HSX and RFX without any structural changes to our view or data layers. We had effectively &lt;strong&gt;the same application&lt;/strong&gt;, just running on a new foundation.&lt;/p&gt;
&lt;h3 id=&quot;our-only-regression-data-inspect&quot;&gt;Our Only Regression: Data Inspect&lt;/h3&gt;
&lt;p&gt;We noticed only &lt;strong&gt;one major area&lt;/strong&gt; where performance regressed after the migration: our &lt;strong&gt;Data Inspect&lt;/strong&gt; feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data Inspect&lt;/strong&gt; is one of Kpow’s most sophisticated pieces of functionality. It allows users to query Kafka topics and stream results into the browser in real-time. Given that Kafka topics can contain tens of millions of records, this feature has always demanded a high level of performance.&lt;/p&gt;
&lt;p&gt;We observed that when result sets grew beyond &lt;strong&gt;10,000 records&lt;/strong&gt;, front-end performance degraded when a user clicked the &lt;strong&gt;“Continue Consuming”&lt;/strong&gt; button to load more data.&lt;/p&gt;
&lt;h3 id=&quot;root-cause-subscriptions-vs-component-responsibility&quot;&gt;Root Cause: Subscriptions vs Component Responsibility&lt;/h3&gt;
&lt;p&gt;Upon investigation, the root cause was clear: we were performing &lt;strong&gt;sorting operations inside a re-frame subscription&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Because React’s batched update model differs subtly from Reagent’s, this subscription was being &lt;strong&gt;recomputed more frequently&lt;/strong&gt; as individual records streamed in from the backend.&lt;/p&gt;
&lt;p&gt;Each recomputation triggered an expensive sort over an increasingly large dataset. Under the old model (Reagent), our batched updates masked some of this cost. Under React’s model, these inefficiencies became more visible.&lt;/p&gt;
&lt;h3 id=&quot;solution-move-presentation-logic-to-components&quot;&gt;Solution: Move Presentation Logic to Components&lt;/h3&gt;
&lt;p&gt;The fix was simple and logical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sorting and other presentation-specific logic&lt;/strong&gt; were moved &lt;strong&gt;out of the re-frame database layer&lt;/strong&gt; and into the &lt;strong&gt;UI components themselves&lt;/strong&gt; using local state.&lt;/li&gt;
&lt;li&gt;Components could now &lt;strong&gt;locally manage view-specific transforms&lt;/strong&gt; on the shared data stream, without polluting the central app-db or affecting unrelated views.&lt;/li&gt;
&lt;li&gt;This also better modeled reality: multiple queries might view the same underlying result set with different sort preferences or filters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This change not only solved the performance regression, but &lt;strong&gt;improved architectural clarity&lt;/strong&gt;, separating &lt;strong&gt;global application state&lt;/strong&gt; from &lt;strong&gt;local view presentation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Features like data inspect could be better served by React APIs like &lt;a href=&quot;https://react.dev/reference/react/Suspense&quot;&gt;&lt;strong&gt;Suspense&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Figuring out how newer React API features like suspense and transitions fit into HSX+RFX is part of ongoing research at Factor House!&lt;/p&gt;
&lt;h2 id=&quot;the-outcome-better-performance-better-developer-experience&quot;&gt;The Outcome: Better Performance, Better Developer Experience&lt;/h2&gt;
&lt;h3 id=&quot;performance-improvements&quot;&gt;Performance Improvements&lt;/h3&gt;
&lt;p&gt;We saw performance gains across several dimensions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Embracing concurrent rendering&lt;/strong&gt; in React 19 allowed React to interrupt, schedule, and batch rendering more intelligently — especially critical in data-heavy UIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eliminating class-based components&lt;/strong&gt;, which Reagent relied on under the hood, removed unnecessary rendering layers (via &lt;strong&gt;&lt;code&gt;:f&amp;gt;&lt;/code&gt;&lt;/strong&gt;) and improved interop with React libraries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixing long-standing Reagent interop quirks&lt;/strong&gt; such as the well-documented &lt;a href=&quot;https://github.com/reagent-project/reagent/issues/619&quot;&gt;&lt;strong&gt;controlled input hacks&lt;/strong&gt;&lt;/a&gt; gave us more predictable form behavior with fewer workarounds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removing all use of Higher-Order Components&lt;/strong&gt; (HOCs), which had previously introduced subtle performance traps and referential equality issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;profiling-kpow&quot;&gt;Profiling Kpow&lt;/h4&gt;
&lt;p&gt;We benchmarked two versions of Kpow using &lt;a href=&quot;https://react.dev/reference/react/Profiler&quot;&gt;&lt;strong&gt;React’s Profiler&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kpow 94.1&lt;/strong&gt;: Reagent + re-frame + React 17&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kpow 94.2&lt;/strong&gt;: HSX + RFX + React 19&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result: &lt;strong&gt;HSX+React19 led to overall fewer commits&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With both versions of the product observing the same Kafka cluster (thus identical data for each version), we ran a simple headed script in Chrome navigating through Kpow’s user interface.&lt;/p&gt;
&lt;p&gt;We found that HSX resulted in a total of &lt;strong&gt;63 commits&lt;/strong&gt; vs Reagent’s &lt;strong&gt;228 commits&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7b1614c77c83d704b0cb8_Reagent-Profiler.jpeg&quot; alt=&quot;Reagent profiling&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Reagent profiling at 228 commits&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7b1614c77c83d704b0cbb_HSX-Profiler.jpeg&quot; alt=&quot;HSX profiling&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;HSX profiling at 63 commits!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The larger yellow spikes of render duration in both bar charts were roughly identical (around ~160ms)&lt;/li&gt;
&lt;li&gt;These spikes relate to the performance of our product, not Reagent or HSX. This is something we need to improve on!&lt;/li&gt;
&lt;li&gt;This isn’t the most scientific test, but seems to confirm that migrating to React19 has resulted in overall less commits without blowing out the render duration.&lt;/li&gt;
&lt;li&gt;See this &lt;a href=&quot;https://gist.github.com/wavejumper/99d5af5bab2e4f21e4d33b9e83e29b54&quot;&gt;&lt;strong&gt;gist&lt;/strong&gt;&lt;/a&gt; on how you can create a production React profiling build with &lt;a href=&quot;https://github.com/thheller/shadow-cljs&quot;&gt;&lt;strong&gt;shadow-cljs&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;developer-experience&quot;&gt;Developer Experience&lt;/h3&gt;
&lt;p&gt;We also took this opportunity to address long-standing developer pain points in Reagent and re-frame, especially around &lt;strong&gt;testability&lt;/strong&gt; and &lt;strong&gt;component isolation&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;goodbye-global-singletons&quot;&gt;Goodbye Global Singletons&lt;/h4&gt;
&lt;p&gt;Re-frame’s global singleton model, while convenient, made it hard to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Isolate component state in &lt;strong&gt;tests&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;multiple independent environments&lt;/strong&gt; (e.g., previewing components in &lt;a href=&quot;https://storybook.js.org/&quot;&gt;&lt;strong&gt;StorybookJS&lt;/strong&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Compose components dynamically with context-specific state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With RFX, we took a idiomatic React approach by using &lt;a href=&quot;https://react.dev/learn/passing-data-deeply-with-context&quot;&gt;&lt;strong&gt;Context Providers&lt;/strong&gt;&lt;/a&gt; to inject isolated app environments where needed.&lt;/p&gt;
&lt;p&gt;Here, &lt;strong&gt;&lt;code&gt;rfx/init&lt;/code&gt;&lt;/strong&gt; spins up a completely fresh RFX instance, including its own app-db and event queue, scoped just to this story.&lt;/p&gt;
&lt;h4 id=&quot;accessing-subscriptions-outside-a-react-context&quot;&gt;Accessing Subscriptions Outside a React Context&lt;/h4&gt;
&lt;p&gt;One of the limitations we frequently ran into with re-frame was the inability to easily access a subscription &lt;strong&gt;outside of a React component&lt;/strong&gt;. Doing so often required hacks or leaking internal implementation details.&lt;/p&gt;
&lt;p&gt;But in real-world applications, this use case &lt;strong&gt;comes up more than you might expect&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example, we integrate with &lt;a href=&quot;https://codemirror.net/&quot;&gt;&lt;strong&gt;CodeMirror&lt;/strong&gt;&lt;/a&gt;, a JavaScript-based code editor that lives &lt;strong&gt;outside of React’s render cycle&lt;/strong&gt;. Within CodeMirror, we implement rich intellisense for several domain-specific languages we support including kSQL, kJQ, and Clojure.&lt;/p&gt;
&lt;p&gt;These autocomplete features often rely on data stored in &lt;strong&gt;&lt;code&gt;app-db&lt;/code&gt;&lt;/strong&gt;. But much of that data is already computed via &lt;strong&gt;subscriptions&lt;/strong&gt; in other parts of the application (materialized views). Re-computing those values manually would introduce duplication and potential inconsistency.&lt;/p&gt;
&lt;p&gt;Another example: when writing complex event handlers in &lt;strong&gt;&lt;code&gt;reg-event-fx&lt;/code&gt;&lt;/strong&gt;, it’s often useful to pull in a &lt;strong&gt;computed subscription value&lt;/strong&gt; (using &lt;strong&gt;&lt;code&gt;inject-cofx&lt;/code&gt;&lt;/strong&gt;) to use as part of a side-effect or payload.&lt;/p&gt;
&lt;p&gt;With RFX, this problem is solved cleanly via the &lt;strong&gt;&lt;code&gt;snapshot-sub&lt;/code&gt;&lt;/strong&gt; function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(defn codemirror&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;autocomplete&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;suggestions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;	[rfx]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;   	(let [database&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;completions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (rfx&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;snapshot&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;sub rfx [:ksql&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;database&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;completions])]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    	;; Logic to wire up codemirror6 completions based on re&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;frame data goes here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        ...&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives us access to the latest materialized value of a subscription without needing to be inside a React component. No hacks, no coupling, just a clean, synchronous read from the store.&lt;/p&gt;
&lt;p&gt;It’s a small feature, but one that has made a big impact on the architecture of our side-effecting code.&lt;/p&gt;
&lt;h4 id=&quot;developer-tooling&quot;&gt;Developer Tooling&lt;/h4&gt;
&lt;p&gt;As a developer tooling company, it should come as no surprise that we’re also building powerful tools around these new libraries!&lt;/p&gt;
&lt;p&gt;Following from our earlier point about &lt;strong&gt;isolated RFX contexts&lt;/strong&gt;, this architectural shift unlocked an entirely new class of debugging and introspection capabilities, all of which we’re packaging into a developer-focused suite we’re calling &lt;strong&gt;&lt;code&gt;rfx-dev&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what it can do, all plug and play:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Subscription Inspector&lt;/strong&gt;&lt;br&gt;
See which components in the React DOM tree are using which subscriptions, including:
&lt;ul&gt;
&lt;li&gt;How often they render&lt;/li&gt;
&lt;li&gt;When they last rendered&lt;/li&gt;
&lt;li&gt;Which signals triggered them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registry Explorer&lt;/strong&gt;&lt;br&gt;
A dynamic view of:
&lt;ul&gt;
&lt;li&gt;Subscriptions&lt;/li&gt;
&lt;li&gt;Registered events&lt;/li&gt;
&lt;li&gt;Their current inputs and handlers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Profiling Metrics&lt;/strong&gt;&lt;br&gt;
Measure performance across the entire data loop:
&lt;ul&gt;
&lt;li&gt;Event dispatch durations&lt;/li&gt;
&lt;li&gt;Subscription realization and recomputation timings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event Log&lt;/strong&gt; A chronological record of dispatched events - useful for debugging complex flows or reproducing state issues.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive REPL&lt;/strong&gt;&lt;br&gt;
Dispatch events, subscribe to signals, and inspect current app-db state in real-time - all from the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time Travel Debugging&lt;/strong&gt;&lt;br&gt;
Snapshot, restore, and export app-db state - perfect for debugging regressions or sharing minimal reproduction cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Signal Graph&lt;/strong&gt;&lt;br&gt;
A visual, interactive graph of your subscriptions dependency tree.&lt;br&gt;
See how subscriptions depend on one another and trace data flows across your app in real-time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7b1614c77c83d704b0cbe_rfx-dev.png&quot; alt=&quot;rfx-dev preview&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;rfx-dev&lt;/code&gt;&lt;/strong&gt; is still a work-in-progress but we’re excited about where it’s heading. We hope to open-source it soon. Stay tuned! 🚀&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;What began as a necessary migration became an opportunity to radically improve our front-end stack.&lt;/p&gt;
&lt;p&gt;We didn’t just swap out dependencies, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preserved what we loved from Reagent and re-frame: Hiccup and a data-oriented event and subscription model.&lt;/li&gt;
&lt;li&gt;Dropped what was holding us back: class-based internals, ratoms, global singletons.&lt;/li&gt;
&lt;li&gt;Aligned ourselves with idiomatic React: hooks, context, and newer API features.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HSX and RFX are more than just drop-in replacements, they’re the result of over a decades experience working in ClojureScript UIs - rethought for React’s present and future.&lt;/p&gt;
&lt;p&gt;After adopting these libraries we find our UI snappier and our code easier to test and reason about. Our team is better equipped to work with the broader React ecosystem, no compromises or awkward interop. Our intent is to continue to hold close to React as the underlying library evolves further in the future.&lt;/p&gt;
&lt;p&gt;For years, the Reagent + re-frame stack was the gold standard for building reactive UIs in ClojureScript and many companies (like ours) adopted it with great success. We know we’re not alone in experiencing the issue of migrating to React 19 and beyond, if you find yourself in the same boat let us know if these libraries help you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/factorhouse/hsx&quot;&gt;&lt;strong&gt;HSX&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/factorhouse/rfx&quot;&gt;&lt;strong&gt;RFX&lt;/strong&gt;&lt;/a&gt; are open-source and Apache 2.0 licensed, we’re hopeful they contribute some value back to the Clojure community.&lt;/p&gt;
</content:encoded><category>Product</category><author>Thomas Crowley</author></item><item><title>Improvements to Data Inspect in Kpow 94.3</title><link>https://factorhouse.io/articles/data-inspect-improvements-94-3/</link><guid isPermaLink="true">https://factorhouse.io/articles/data-inspect-improvements-94-3/</guid><description>Kpow&apos;s 94.3 release is here, transforming how you work with Kafka. Instantly query topics using plain English with our new AI-powered filtering, automatically decode any message format without manual setup, and leverage powerful new enhancements to our kJQ language. This update makes inspecting Kafka data more intuitive and powerful than ever before.</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Kpow’s Data Inspect feature has always been a cornerstone for developers working with Apache Kafka, offering a powerful way to query and understand topic data, as introduced in our earlier guide on &lt;a href=&quot;https://factorhouse.io/blog/how-to/query-a-kafka-topic/&quot;&gt;&lt;strong&gt;how to query a Kafka topic&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://factorhouse.io/blog/releases/94-3/&quot;&gt;&lt;strong&gt;94.3 release&lt;/strong&gt;&lt;/a&gt; dramatically enhances this experience by introducing a suite of intelligent and user-friendly upgrades. This release focuses on making data inspection more accessible for all users while adding even more power for advanced use cases. The key highlights include AI-powered message filtering, which allows you to query Kafka using plain English; automatic deserialization, which removes the guesswork when dealing with unknown data formats; and significant enhancements to the kJQ language itself, providing more flexible and powerful filtering capabilities.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;ai-powered-message-filtering&quot;&gt;AI-Powered Message Filtering&lt;/h2&gt;
&lt;p&gt;Kpow now supports the integration of external AI models to enhance its capabilities, most notably through its “bring your own” (BYO) AI model functionality. This allows you to connect Kpow with various AI providers to power features within the platform.&lt;/p&gt;
&lt;h3 id=&quot;ai-model-configuration&quot;&gt;AI Model Configuration&lt;/h3&gt;
&lt;p&gt;You have the flexibility to configure one or more AI model providers. Within your Kpow user preferences, you can then set a default model for all AI-assisted tasks. Configuration is managed through environment variables and is supported for the following providers:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Environment Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;‘OPENAI_API_KEY’&lt;/td&gt;
&lt;td&gt;Your OpenAI API key&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(required)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;‘XXXX’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;‘OPENAI_MODEL’&lt;/td&gt;
&lt;td&gt;Model ID to use&lt;/td&gt;
&lt;td&gt;‘gpt-4o-mini’&lt;/td&gt;
&lt;td&gt;‘o3-mini’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;‘ANTHROPIC_API_KEY’&lt;/td&gt;
&lt;td&gt;Your Anthropic API key&lt;/td&gt;
&lt;td&gt;(required)&lt;/td&gt;
&lt;td&gt;‘XXXX’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;‘ANTHROPIC_MODEL’&lt;/td&gt;
&lt;td&gt;Model ID to use&lt;/td&gt;
&lt;td&gt;‘claude-3-7-sonnet-20250219’&lt;/td&gt;
&lt;td&gt;‘claude-opus-4-20250514’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;‘OLLAMA_MODEL’&lt;/td&gt;
&lt;td&gt;Model ID to use (must support tools)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;‘llama3.1:8b’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;OLLAMA_URL&lt;/td&gt;
&lt;td&gt;URL of the Ollama model server&lt;/td&gt;
&lt;td&gt;‘&lt;a href=&quot;http://localhost:11434&quot;&gt;http://localhost:11434&lt;/a&gt;’&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://prod.ollama.mycorp.io&quot;&gt;https://prod.ollama.mycorp.io&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you need support for a different AI provider, you can contact the &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;&lt;strong&gt;Factor House support team&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;enhanced-ai-features&quot;&gt;Enhanced AI Features&lt;/h3&gt;
&lt;p&gt;The primary AI-driven feature is the &lt;strong&gt;kJQ filter generation&lt;/strong&gt;. This powerful tool enables you to query Kafka topics using natural language. Instead of writing complex &lt;em&gt;kJQ&lt;/em&gt; expressions, you can simply describe the data you’re looking for in plain English.&lt;/p&gt;
&lt;p&gt;Here’s how it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Natural Language Processing:&lt;/strong&gt; The system converts your conversational prompts (e.g., “show me all orders over $100 from the last hour”) into precise kJQ filter expressions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema-Awareness:&lt;/strong&gt; To improve accuracy, the AI can optionally use the schemas of your Kafka topics to understand field names, data types, and the overall structure of your data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in Validation:&lt;/strong&gt; Every filter generated by the AI is automatically checked against Kpow’s kJQ engine to ensure it is syntactically correct before you run it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This feature is accessible from the Data Inspect view for any topic. After the AI generates a filter, you have the option to execute it immediately, modify it for more specific needs, or save it for later use. For best results, it is recommended to provide specific and actionable descriptions in your natural language queries.&lt;/p&gt;
&lt;p&gt;It is important to be mindful that AI-generated filters are probabilistic and may not always be perfect. Additionally, when using cloud-based AI providers, your data will be processed by them, so for sensitive information, using local models via Ollama or enterprise-grade AI services with strong privacy guarantees is recommended.&lt;/p&gt;
&lt;p&gt;For more details, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/features/ai-models/&quot;&gt;&lt;strong&gt;AI Models documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7ce967dc533868c7515f0_ai-model-demo.gif&quot; alt=&quot;kJQ AI filter in action&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;automatic-deserialization&quot;&gt;Automatic Deserialization&lt;/h2&gt;
&lt;p&gt;Kpow simplifies data inspection with its “Auto SerDes” feature. In the Data Inspect view, you can select “Auto” as the deserializer, and Kpow will analyze the raw data to determine its format (like JSON, Avro, etc.) and decode it for you. This is especially useful in several scenarios, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you are exploring unfamiliar topics for the first time.&lt;/li&gt;
&lt;li&gt;While working with topics that may contain mixed or inconsistent data formats.&lt;/li&gt;
&lt;li&gt;When debugging serialization problems across different environments.&lt;/li&gt;
&lt;li&gt;For onboarding new team members who need to get up to speed on topic data quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To make these findings permanent, you can enable the &lt;strong&gt;Topic SerDes Observation&lt;/strong&gt; job by setting &lt;strong&gt;&lt;code&gt;INFER_TOPIC_SERDES=true&lt;/code&gt;&lt;/strong&gt;. When active, this job saves the automatically detected deserializer settings and any associated schema IDs, making them visible and persistent in the Kpow UI for future reference.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7ce967dc533868c7515f6_auto-deserializer.gif&quot; alt=&quot;kJQ AI filter in action&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kjq-language-enhancements&quot;&gt;kJQ Language Enhancements&lt;/h2&gt;
&lt;p&gt;In response to our customers’ evolving filtering needs, we’ve significantly improved the &lt;strong&gt;kJQ&lt;/strong&gt; language to make Kafka record filtering more powerful and flexible. Check out the updated &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/features/data-inspect/kjq-filters/&quot;&gt;&lt;strong&gt;kJQ filters documentation&lt;/strong&gt;&lt;/a&gt; for full details.&lt;/p&gt;
&lt;p&gt;Below are some highlights of the improvements:&lt;/p&gt;
&lt;h3 id=&quot;chained-alternatives&quot;&gt;Chained alternatives&lt;/h3&gt;
&lt;p&gt;Selects the first non-null email address and checks if it ends with “.com”:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.value.primary_email &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// .value.secondary_email // .value.contact_email | endswith(&quot;.com&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;stringarray-slices&quot;&gt;String/Array slices&lt;/h3&gt;
&lt;p&gt;Matches where the first 3 characters of &lt;strong&gt;&lt;code&gt;transaction_id&lt;/code&gt;&lt;/strong&gt; equal &lt;strong&gt;&lt;code&gt;TXN&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.value.transaction_id[&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;TXN&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, &lt;strong&gt;&lt;code&gt;{ &quot;transaction_id&quot;: &quot;TXN12345&quot; }&lt;/code&gt;&lt;/strong&gt; matches, while &lt;strong&gt;&lt;code&gt;{ &quot;transaction_id&quot;: &quot;ORD12345&quot; }&lt;/code&gt;&lt;/strong&gt; does not&lt;/p&gt;
&lt;h3 id=&quot;uuid-type-support&quot;&gt;UUID type support&lt;/h3&gt;
&lt;p&gt;kJQ supports UUID types out of the box, including the &lt;strong&gt;&lt;code&gt;UUID&lt;/code&gt;&lt;/strong&gt; deserializer, &lt;strong&gt;&lt;code&gt;AVRO&lt;/code&gt;&lt;/strong&gt; + logical types, or &lt;strong&gt;&lt;code&gt;Transit / JSON&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;EDN&lt;/code&gt;&lt;/strong&gt; deserializers that have richer data types.&lt;/p&gt;
&lt;p&gt;To compare against literal UUID strings, prefix them with &lt;strong&gt;&lt;code&gt;#uuid&lt;/code&gt;&lt;/strong&gt; to coerce into a UUID:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.key &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; #uuid &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;fc1ba6a8-6d77-46a0-b9cf-277b6d355fa6&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7ce967dc533868c7515f3_slice-demo.gif&quot; alt=&quot;kJQ AI filter in action&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The 94.3 release marks a significant leap forward for data exploration in Kpow. By integrating AI for natural language queries, automating the complexities of deserialization, and enriching the kJQ language with advanced functions, Kpow now caters to an even broader range of users. These updates streamline workflows for everyone, from new team members who can now inspect topics without prior knowledge of data formats, to seasoned engineers who can craft more sophisticated and precise queries than ever before. This release reaffirms our commitment to simplifying the complexities of Apache Kafka and empowering teams to unlock the full potential of their data with ease and efficiency.&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Introducing Factor House Docs</title><link>https://factorhouse.io/articles/intro-factor-house-docs/</link><guid isPermaLink="true">https://factorhouse.io/articles/intro-factor-house-docs/</guid><description>We&apos;re excited to launch the new Factor House Docs, a unified hub for all our product documentation. Discover key improvements like a completely new task-based structure, interactive kJQ examples, and powerful search, all designed to help you find the information you need, faster than ever. Explore the new home for all things Kpow, Flex, Factor Platform and more.</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;We are excited to announce the launch of our new, unified documentation site: &lt;a href=&quot;https://docs.factorhouse.io/&quot;&gt;&lt;strong&gt;Factor House Docs&lt;/strong&gt;&lt;/a&gt;. This new hub brings all our product documentation together into a single, streamlined resource, making it easier than ever to find the information you need.&lt;/p&gt;
&lt;p&gt;Previously, our documentation was split between community and enterprise editions. Now, with Factor House Docs, all of that content is in one place with a simplified structure. This move is designed to provide a more cohesive and user-friendly experience for everyone.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e0d9350d4fe92d24b51b_new-docs.png&quot; alt=&quot;Factor House Docs&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-hightlights&quot;&gt;Key hightlights&lt;/h2&gt;
&lt;h3 id=&quot;unified-product-content&quot;&gt;Unified product content&lt;/h3&gt;
&lt;p&gt;No more switching between different sites for community and enterprise documentation. All documentation for Kpow, Flex, and the upcoming Factor Platform is now in one centralized location. This consolidation simplifies the user experience by providing a single source of truth for all product-related information, regardless of your edition.&lt;/p&gt;
&lt;h3 id=&quot;clear-feature-availability&quot;&gt;Clear feature availability&lt;/h3&gt;
&lt;p&gt;To help you quickly identify which features are available in your edition, we’ve introduced clear &lt;strong&gt;&lt;code&gt;COMMUNITY&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;TEAM&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;ENTERPRISE&lt;/code&gt;&lt;/strong&gt; badges throughout the documentation. This makes it easy to understand the capabilities of your current plan and see what additional features are available if you choose to upgrade.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e0d9350d4fe92d24b52f_badges.png&quot; alt=&quot;Factor House Docs&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;improved-organization&quot;&gt;Improved organization&lt;/h3&gt;
&lt;p&gt;One of the most impactful changes in the new Factor House Docs is the move from a dense, reference-style list to a clear, task-oriented structure. This isn’t just a redesign; it’s a fundamental improvement in how you find and use our documentation.&lt;/p&gt;
&lt;p&gt;A good example of this is how we’ve consolidated administrative features.&lt;/p&gt;
&lt;p&gt;In the previous documentation, getting a complete view of your administrative capabilities required you to hunt through completely different sections. Core admin controls like &lt;strong&gt;Staged mutations&lt;/strong&gt; and &lt;strong&gt;Temporary policies&lt;/strong&gt; were buried under the &lt;strong&gt;&lt;code&gt;User authorisation&lt;/code&gt;&lt;/strong&gt; section. Meanwhile, other powerful administrative tools like &lt;strong&gt;Bulk actions&lt;/strong&gt; and &lt;strong&gt;Data governance&lt;/strong&gt; were located in the general &lt;strong&gt;&lt;code&gt;Features&lt;/code&gt;&lt;/strong&gt; list, mixed in with dozens of other unrelated items. To perform your duties, you had to already know which features we considered part of “authorization” and which were just “features.”&lt;/p&gt;
&lt;p&gt;The new documentation eliminates this guesswork by introducing a single, dedicated &lt;strong&gt;Administrative workflows&lt;/strong&gt; section.&lt;/p&gt;
&lt;p&gt;Content is now organized to reflect a natural user journey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A clear onboarding path:&lt;/strong&gt; Instead of a jumble of topics, you are now greeted with a clear, sequential path: &lt;strong&gt;Getting started&lt;/strong&gt;, followed by dedicated &lt;strong&gt;Installation&lt;/strong&gt; and &lt;strong&gt;Configuration&lt;/strong&gt; sections. This guides you logically from initial setup to a fully running instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic-based grouping:&lt;/strong&gt; The monolithic “Features” section is gone. Related capabilities are now grouped into intuitive, high-level categories. For instance, powerful data tools are now consolidated under the &lt;strong&gt;Query languages &amp;amp; Data management&lt;/strong&gt; section. This creates a focused hub for anyone responsible for the data itself, bringing together everything from querying with kJQ to inspecting topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced discoverability:&lt;/strong&gt; This new structure makes it much easier to discover the full range of Kpow’s capabilities. By browsing top-level sections like &lt;strong&gt;Integration &amp;amp; Kafka management&lt;/strong&gt;, you can quickly get a sense of the available tools without having to read through a long, unstructured list.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This streamlined layout is designed to get you to your answers faster, reducing search time and making the entire platform more accessible and easier to master.&lt;/p&gt;
&lt;h3 id=&quot;powerful-search-instant-answers&quot;&gt;Powerful search, instant answers&lt;/h3&gt;
&lt;p&gt;Finding the exact information you need is now faster and easier than ever. Using &lt;strong&gt;Algolia DocSearch&lt;/strong&gt;, our documentation site delivers instant, relevant results across all sections. No more navigating multiple pages to locate a specific configuration property or function.&lt;/p&gt;
&lt;p&gt;Whether you’re looking for an environment variable, a kJQ example, or details on Role-Based Access Control, the global search guides you directly to the content you need. This improvement ensures you spend less time searching and more time building.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e0d9350d4fe92d24b51e_doc-search.png&quot; alt=&quot;Document Search&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;interactive-kjq-examples&quot;&gt;Interactive kJQ examples&lt;/h3&gt;
&lt;p&gt;Our new documentation includes a dedicated section for fully interactive kJQ examples. You can now modify both the kJQ filter and the JSON data in real-time to see how different filters work and experiment with the syntax. This hands-on approach allows you to test everything from basic filters and function calls to complex, nested logic right in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e0d9350d4fe92d24b532_interactive-examples.png&quot; alt=&quot;Interactive kJQ Examples&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;hands-on-playground&quot;&gt;Hands-on playground&lt;/h3&gt;
&lt;p&gt;To further enhance your learning experience, we’ve added a new “Playground” section. This area features a collection of labs and projects designed to help you explore the full range of product capabilities in a hands-on environment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e0d9350d4fe92d24b518_playground.png&quot; alt=&quot;Interactive kJQ Examples&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ready-for-the-future&quot;&gt;Ready for the future&lt;/h3&gt;
&lt;p&gt;The new Factor House Docs site is built to be a long-lasting resource. The documentation for the new Factor Platform will be added and expanded upon its release, ensuring that this hub will remain the most up-to-date and comprehensive resource for all product information now and in the future.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We are confident that the new Factor House Docs will be a valuable resource for our users. We encourage you to explore the new site and update your bookmarks. The previous documentation site will be retired.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>Company</category><author>Factor House</author></item><item><title>Data Inspect Enhancements in Kpow 94.5</title><link>https://factorhouse.io/articles/data-inspect-enhancements-94-5/</link><guid isPermaLink="true">https://factorhouse.io/articles/data-inspect-enhancements-94-5/</guid><description>Kpow 94.5 enhances data inspection with comma-separated kJQ Projection expressions, in-browser search, and flexible deserialization options. This release also adds high-performance streaming for large datasets and expands kJQ with new transforms and functions—testable on our new interactive examples page. These updates provide deeper insights and more granular control over your Kafka data streams.</description><pubDate>Tue, 17 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Following our &lt;a href=&quot;https://factorhouse.io/blog/articles/data-inspect-improvements-94-3&quot;&gt;&lt;strong&gt;recent improvements in Kpow release 94.3&lt;/strong&gt;&lt;/a&gt;, which introduced AI-powered filtering and automatic deserialization, release 94.5 continues to enhance the data inspect experience. This latest version introduces several key additions, including comma-separated kJQ Projection expressions, in-browser search, multiple deserialization options, and attribute sorting. It also features a significant optimization with high-performance streaming and an enhancement to our query language with expanded kJQ capabilities. You can try out the new transforms and functions on the new interactive examples page on our Factor House docs. These updates provide more granular control and deeper insights into your Kafka topics, empowering developers to navigate and analyze their data with greater efficiency and precision.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;targeted-data-views-with-comma-separated-kjq-projection-expressions&quot;&gt;Targeted data views with comma-separated kJQ Projection expressions&lt;/h2&gt;
&lt;p&gt;Kpow 94.5 introduces support for comma-separated projection expressions in kJQ, such as &lt;strong&gt;&lt;code&gt;.value.base, .value.rates&lt;/code&gt;&lt;/strong&gt;. This powerful feature allows you to extract multiple fields from Kafka records in a single query. Now you can create targeted data views without unnecessary information, streamlining your workflow and keeping your output clean. This functionality is available for both key and value sub-paths, offering greater flexibility in how you inspect your data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e43276eee28679f65fa3_projection-expressions.gif&quot; alt=&quot;Data Inspect - Projection expressions&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;faster-navigation-with-in-browser-search&quot;&gt;Faster navigation with in-browser search&lt;/h2&gt;
&lt;p&gt;You can now use your browser’s native search functionality (Ctrl + F) to quickly find records by JSON path or value when using kJQ filters. This eliminates the need to re-run queries, saving you time and effort. The results component is fully keyboard-friendly, adhering to the &lt;a href=&quot;https://factorhouse.io/blog/articles/data-inspect-enhancements-94-5/(https://www.w3.org/WAI/ARIA/apg/patterns/listbox/)&quot;&gt;&lt;strong&gt;Listbox pattern&lt;/strong&gt;&lt;/a&gt; for improved accessibility. This ensures a smoother and more predictable navigation experience for all users, including those who rely on screen readers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e43276eee28679f65f98_search.gif&quot; alt=&quot;Data Inspect - In-Browser Search&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;deeper-insights-into-schemas-and-deserialization&quot;&gt;Deeper insights into schemas and deserialization&lt;/h2&gt;
&lt;p&gt;Data Inspect now offers detailed schema metadata for each message, including schema IDs and deserializer types. This makes it easier to identify misaligned schemas and poison messages. To handle these problematic records, Kpow 94.5 provides several deserialization options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Drop record (default):&lt;/strong&gt; This option ignores erroneous records, displaying only the well-formatted ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retain record:&lt;/strong&gt; This includes both well-formatted and erroneous records. Problematic records are flagged with a ‘Deserialization exception’ message instead of displaying the raw, poisonous value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poison only:&lt;/strong&gt; This option displays only the erroneous records, with the value recorded as ‘Deserialization exception’.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e43276eee28679f65fa8_deser-opts.gif&quot; alt=&quot;Data Inspect - Deserialization Options&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;improved-readability-with-attribute-sorting&quot;&gt;Improved readability with attribute sorting&lt;/h2&gt;
&lt;p&gt;When you select the ‘Pretty printed (sorted)’ display option, the attributes of the key or value are now sorted alphabetically by name. This improves the readability and consistency of your data during inspection, making it easier to compare records and identify specific fields.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7e43276eee28679f65fab_attr-sort.gif&quot; alt=&quot;Data Inspect - Attribute Sorting&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;high-performance-streaming-for-large-datasets&quot;&gt;High-performance streaming for large datasets&lt;/h2&gt;
&lt;p&gt;Kpow 94.5 is optimized for performance, allowing you to stream over 500,000 records without any UI lag. This enables the efficient analysis of large datasets, so you can work with high-volume topics without compromising on speed or responsiveness.&lt;/p&gt;
&lt;h2 id=&quot;expanded-kjq-capabilities-with-new-transforms-and-functions&quot;&gt;Expanded kJQ capabilities with new transforms and functions&lt;/h2&gt;
&lt;p&gt;The kJQ language has been significantly expanded with a host of new transforms, including &lt;strong&gt;&lt;code&gt;parse-json&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;floor&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;ceil&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;upper-case&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;lower-case&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;trim&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;ltrim&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;rtrim&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;reverse&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;unique&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;first&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;last&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;keys&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;values&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;is-empty&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;flatten&lt;/code&gt;&lt;/strong&gt;. Additionally, new functions such as &lt;strong&gt;&lt;code&gt;within&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;split&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;join&lt;/code&gt;&lt;/strong&gt; have been added to enable more complex data manipulation directly within your kJQ queries.&lt;/p&gt;
&lt;p&gt;For more details on these new features, please refer to the updated &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/kjq/manual&quot;&gt;&lt;strong&gt;kJQ manual&lt;/strong&gt;&lt;/a&gt;. Also, be sure to visit the new &lt;a href=&quot;https://docs.factorhouse.io/kpow/language/kjq/examples/&quot;&gt;&lt;strong&gt;interactive examples&lt;/strong&gt;&lt;/a&gt; page on our new &lt;strong&gt;Factor House docs&lt;/strong&gt; site—it’s a great way to quickly verify your kJQ queries.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow 94.5 builds upon the foundation of previous releases to deliver a more powerful and user-friendly data inspection experience. The latest additions—including kJQ Projection expressions, in-browser search, and flexible deserialization options—provide more granular control over your data. Together with high-performance streaming and expanded kJQ capabilities, these enhancements solidify Kpow as an essential tool for any developer working with Apache Kafka. Be sure to test out the new kJQ features on the interactive examples page on our Factor House docs. Ultimately, this release is designed to streamline your workflows, reduce debugging time, and empower you to unlock the full potential of your data.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Releasing Software at Factor House: Our Java Compatibility and Evolution Strategy</title><link>https://factorhouse.io/articles/java-compatibility-and-evolution-strategy/</link><guid isPermaLink="true">https://factorhouse.io/articles/java-compatibility-and-evolution-strategy/</guid><description>At Factor House, delivering reliable software is at the heart of everything we do. A key aspect of this commitment lies in our approach to managing Java compatibility. This blog post outlines our current release process and future plans for evolving Java support, including our approach to deprecating older versions in a way that respects the needs of diverse customer bases.</description><pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;releasing-software-at-factor-house-our-java-compatibility-and-evolution-strategy&quot;&gt;Releasing Software at Factor House: Our Java Compatibility and Evolution Strategy&lt;/h2&gt;
&lt;p&gt;At Factor House, delivering reliable software is at the heart of everything we do. A key aspect of this commitment lies in our approach to managing Java compatibility.&lt;/p&gt;
&lt;p&gt;Our suite of products works seamlessly across a range of JVM versions—from Java 8 to Java 17 and beyond. We balance supporting large enterprises still demanding Java 8 releases while staying ahead with JVM advancements, such as catering to customers requiring Graviton builds for deployments targetting ARM.&lt;/p&gt;
&lt;p&gt;This blog post outlines our current release process and future plans for evolving Java support, including our approach to deprecating older versions in a way that respects the needs of diverse customer bases.&lt;/p&gt;
&lt;h3 id=&quot;our-current-java-release-strategy&quot;&gt;Our Current Java Release Strategy&lt;/h3&gt;
&lt;p&gt;We release our software in two primary formats: as JAR files and through Docker containers. Here’s an overview of our compatibility and deployment practices:&lt;/p&gt;
&lt;h4 id=&quot;jar-releases&quot;&gt;JAR Releases&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java 8:&lt;/strong&gt; Supported for customers who rely on legacy environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java 11:&lt;/strong&gt; A modern, stable release offering long-term support (LTS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java 17:&lt;/strong&gt; Our recommended LTS version for customers, ensuring compatibility with newer environments and features.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;docker-releases&quot;&gt;Docker Releases&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;We use &lt;a href=&quot;https://aws.amazon.com/corretto/?filtered-posts.sort-by=item.additionalFields.createdDate&amp;amp;filtered-posts.sort-order=desc&quot;&gt;&lt;strong&gt;Amazon Corretto 17&lt;/strong&gt;&lt;/a&gt; as the base image for our Dockerfiles.&lt;/li&gt;
&lt;li&gt;Our Docker images include the Java 17 JAR by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;compatibility-matrix&quot;&gt;Compatibility Matrix&lt;/h3&gt;
&lt;h4 id=&quot;java-jars&quot;&gt;Java JARs&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JAR Version&lt;/th&gt;
&lt;th&gt;Supported Java Versions&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java 8&lt;/td&gt;
&lt;td&gt;Java 8&lt;/td&gt;
&lt;td&gt;Legacy support, phased-out over time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 11&lt;/td&gt;
&lt;td&gt;Java 11, Java 17&lt;/td&gt;
&lt;td&gt;Suitable for many modern deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Java 17+&lt;/td&gt;
&lt;td&gt;Recommended for most customers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;docker-releases-1&quot;&gt;Docker Releases&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment Type&lt;/th&gt;
&lt;th&gt;Base Image&lt;/th&gt;
&lt;th&gt;JAR Version&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;Amazon Corretto 17&lt;/td&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Future-proof, stable LTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Helm Charts&lt;/td&gt;
&lt;td&gt;Amazon Corretto 17&lt;/td&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Future-proof, stable LTS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;our-commitment-to-compatibility&quot;&gt;Our Commitment to Compatibility&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Backward Compatibility:&lt;/strong&gt; We understand that some customers operate in environments requiring older Java versions. That’s why we’ve maintained Java 8 and Java 11 compatibility in addition to Java 17.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future-Ready:&lt;/strong&gt; We’re committed to adopting more recent LTS versions of Java as they become available, ensuring our software leverages the latest performance and security improvements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transition Planning:&lt;/strong&gt; While we currently support Java 8, we recognize its end-of-life status in many contexts. As part of our roadmap, we plan to phase out Java 8 support gradually, allowing customers ample time to transition to newer versions.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;moving-closer-to-javas-lts-release-cycle&quot;&gt;Moving Closer to Java’s LTS Release Cycle&lt;/h3&gt;
&lt;p&gt;Moving forward, Factor House will align more closely with Java’s LTS release cycle. For instance, LTS GA support for Java 25 commences in September 2025, and we plan to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transition our JAR compilation targets and base Docker images to the new LTS versions as they are released.&lt;/li&gt;
&lt;li&gt;Phase out older versions in alignment with Java’s premier support timelines.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This strategy will enable us to provide customers with timely access to the latest Java features and optimizations while maintaining a predictable and transparent deprecation schedule.&lt;/p&gt;
&lt;h3 id=&quot;looking-ahead&quot;&gt;Looking Ahead&lt;/h3&gt;
&lt;h4 id=&quot;docker-image-evolution&quot;&gt;Docker Image Evolution&lt;/h4&gt;
&lt;p&gt;Over time, our base Docker image will evolve to reflect newer LTS Java versions. For instance, as future LTS releases like Java 21, Java 25, or beyond become widely adopted, we’ll:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transition to the new LTS version as our default base image.&lt;/li&gt;
&lt;li&gt;Update our JAR compilation targets to align with the latest versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;phasing-out-java-8&quot;&gt;Phasing Out Java 8&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;We will work closely with customers still using Java 8 to support their migration efforts.&lt;/li&gt;
&lt;li&gt;A detailed timeline for deprecating Java 8 will be communicated well in advance to ensure a smooth transition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;Adopting this strategy ensures that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our software is secure, leveraging the latest Java features and updates.&lt;/li&gt;
&lt;li&gt;Customers have flexibility, whether they’re operating legacy systems or embracing modern environments.&lt;/li&gt;
&lt;li&gt;Factor House remains future-focused, delivering cutting-edge solutions without compromising reliability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;At Factor House, we’re committed to balancing innovation with stability. By supporting multiple Java versions and planning for future transitions, we ensure that our customers can deploy our software confidently, no matter their infrastructure. Stay tuned for updates as we continue to evolve our Java release strategy and roadmap.&lt;/p&gt;
&lt;p&gt;Have questions or need guidance on transitioning to a newer Java version? Reach out to our support team—we’re here to help!&lt;/p&gt;
</content:encoded><category>Industry</category><author>Factor House</author></item><item><title>How to Integrate Kpow with OCI Streaming with Apache Kafka</title><link>https://factorhouse.io/articles/integrate-kpow-with-oci-streaming/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-oci-streaming/</guid><description>Integrate Kpow with Oracle Cloud Infrastructure Streaming with Apache Kafka in minutes. Gain unified visibility and control over your OCI brokers and ecosystem components through our market-leading engineering toolkit.</description><pubDate>Mon, 08 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Kpow is the all-in-one engineering toolkit for Apache Kafka, Redpanda, and compatible streaming platforms. It provides engineers with a unified interface to monitor, manage, and explore their streaming resources.&lt;/p&gt;
&lt;p&gt;Kpow is fully compatible with &lt;a href=&quot;https://www.oracle.com/cloud/apache-kafka/&quot;&gt;&lt;strong&gt;Oracle Cloud Infrastructure (OCI) Streaming with Apache Kafka&lt;/strong&gt;&lt;/a&gt; (Oracle’s dedicated managed service) out of the box. Because Kpow uses standard Kafka protocols, it integrates seamlessly with your OCI cluster without requiring proprietary plugins, sidecars, or complex custom configurations.&lt;/p&gt;
&lt;p&gt;OCI provides two Kafka-compatible services. This guide focuses on the dedicated &lt;strong&gt;OCI Streaming with Apache Kafka&lt;/strong&gt; service which supports the full Kafka API, though the configuration remains similar for the serverless &lt;a href=&quot;https://www.oracle.com/cloud/streaming/&quot;&gt;&lt;strong&gt;OCI Streaming&lt;/strong&gt;&lt;/a&gt; offering as well. Check &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/oci-streaming#oci-streaming&quot;&gt;this page&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to OCI, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running OCI Kafka cluster:&lt;/strong&gt; A dedicated “OCI Streaming with Apache Kafka” instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network reachability:&lt;/strong&gt; Ensure your Security Lists or Network Security Groups allow inbound traffic on port 9092 (or 9093) from the host running Kpow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Details:&lt;/strong&gt; Your Bootstrap Server address. Note that while this guide uses SASL/SCRAM, &lt;a href=&quot;https://docs.oracle.com/en-us/iaas/Content/kafka/security.htm&quot;&gt;Mutual TLS (mTLS)&lt;/a&gt; is also supported.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; The username and password defined in your OCI Vault Secret for SASL/SCRAM authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to OCI is using our standard Enterprise Docker image.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific OCI connection details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;[OCI_BOOTSTRAP_ADDRESS]:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;SCRAM-SHA-512&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&apos;org.apache.kafka.common.security.scram.ScramLoginModule required username=&quot;[VAULT_USERNAME]&quot; password=&quot;[VAULT_PASSWORD]&quot;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;. You will see an overview of your OCI topics, brokers, and consumer groups.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c5cefdb324c47d338e271c_kpow-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;This screenshot displays a cluster with three Kafka brokers and one instance each of Kafka Connect and Schema Registry. Your specific view will vary depending on your environment configuration.&lt;/p&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;OCI Dedicated Kafka uses SASL/SCRAM-SHA-512 for authentication, which is backed by the OCI Vault service. The credentials used in the SASL_JAAS_CONFIG are the keys and values you defined inside your OCI Vault Secret JSON. These are distinct from your Oracle Cloud console login or IAM credentials.&lt;/p&gt;
&lt;p&gt;Kpow also supports OCI clusters configured with &lt;strong&gt;mTLS&lt;/strong&gt;. For a comprehensive list of configuration options, including mTLS setup and advanced security configurations, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/oci-streaming&quot;&gt;Kpow OCI Provider Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Since OCI does not provide managed Kafka Connect or Schema Registry services, you may be running self-hosted instances on OCI Compute or OKE. You can integrate these into Kpow by adding the following environment variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka Connect:&lt;/strong&gt; &lt;code&gt;--env CONNECT_REST_URL=&quot;http://connect-host:8083&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema Registry:&lt;/strong&gt; &lt;code&gt;--env SCHEMA_REGISTRY_URL=&quot;http://schema-registry-host:8081&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once configured, Kpow provides full visibility into your connectors and schemas alongside your OCI brokers. For further details, refer to the documentation for &lt;a href=&quot;https://docs.factorhouse.io/kpow/configuration/kafka-connect&quot;&gt;Kafka Connect&lt;/a&gt; and &lt;a href=&quot;https://docs.factorhouse.io/kpow/configuration/schema-registry&quot;&gt;Schema Registry&lt;/a&gt; configuration.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;Kpow runs seamlessly on &lt;strong&gt;OKE (Oracle Kubernetes Engine)&lt;/strong&gt;. We recommend following our official &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;Helm Installation Guide&lt;/a&gt; for deployment.&lt;/p&gt;
&lt;h3 id=&quot;vm--bare-metal&quot;&gt;VM / Bare Metal&lt;/h3&gt;
&lt;p&gt;For running on OCI Compute instances, you can also use the &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;Kpow JAR artifact&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a single pane of glass for your OCI Streaming with Apache Kafka infrastructure, making it easy to monitor and manage your data streams in real-time.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your OCI integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;related-content&quot;&gt;Related Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-aws&quot;&gt;Set Up Kpow with Amazon Managed Streaming for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Set Up Kpow with Confluent Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-gcp&quot;&gt;Set Up Kpow with Google Cloud Managed Service for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-instaclustr&quot;&gt;Set Up Kpow with NetApp Instaclustr Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Unified community license for Kpow and Flex</title><link>https://factorhouse.io/articles/unified-community-license/</link><guid isPermaLink="true">https://factorhouse.io/articles/unified-community-license/</guid><description>The unified Factor House Community License works with both Kpow Community Edition and Flex Community Edition, meaning one license will unlock both products. This makes it even simpler to explore modern data streaming tools, create proof-of-concepts, and evaluate our products.</description><pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The new unified Factor House Community License works with both Kpow Community Edition and Flex Community Edition, so you only need one license to unlock both products. This makes it even simpler to explore modern data streaming tools, create proof-of-concepts, and evaluate our products.&lt;/p&gt;
&lt;h2 id=&quot;whats-changing&quot;&gt;What’s changing&lt;/h2&gt;
&lt;p&gt;Previously, we issued separate community licenses for Kpow and Flex, with different tiers for individuals and organisations. Now, there’s just one single Community License that unlocks both products.&lt;/p&gt;
&lt;p&gt;What’s new:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One license for both products&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three environments for everyone&lt;/strong&gt; - whether you’re an individual developer or part of a team, you get three non-production installations per product&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplified management&lt;/strong&gt; - access and renew your licenses through our new self-service portal at &lt;a href=&quot;http://account.factorhouse.io/&quot;&gt;account.factorhouse.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;our-commitment-to-the-engineering-community&quot;&gt;Our commitment to the engineering community&lt;/h2&gt;
&lt;p&gt;Since first launching Kpow CE at Current ’22, thousands of engineers have used our community licenses to learn Kafka and Flink without jumping through enterprise procurement hoops. This unified license keeps that same philosophy: high-quality tools that are free for non-production use.&lt;/p&gt;
&lt;p&gt;The Factor House Community License is free for individuals and organizations to use in non-production environments. It’s perfect for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Learning and experimenting&lt;/strong&gt; with Kafka and Flink&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building prototypes&lt;/strong&gt; and proof-of-concepts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing integrations&lt;/strong&gt; before production deployment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exploring sample projects&lt;/strong&gt; like &lt;a href=&quot;https://github.com/factorhouse/factorhouse-local&quot;&gt;Factor House Local&lt;/a&gt;, &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/projects/mobile-game-top-k-analytics&quot;&gt;Top-K Game Leaderboard&lt;/a&gt;, and &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/projects/thelook-ecomm-cdc&quot;&gt;theLook eCommerce dashboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;New users:&lt;/strong&gt; Head to &lt;a href=&quot;http://account.factorhouse.io/&quot;&gt;account.factorhouse.io&lt;/a&gt; to grab your free Community license. You’ll receive instant access via magic link authentication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Existing users:&lt;/strong&gt; Your legacy Kpow and Flex Community licenses will continue to work and are now visible in the portal. When your license renews (after 12 months), consider switching to the unified model for easier management.&lt;/p&gt;
&lt;h2 id=&quot;whats-included&quot;&gt;What’s included&lt;/h2&gt;
&lt;p&gt;Both Kpow CE and Flex CE include most enterprise features, optimized for learning and testing. Includes Kafka and Flink monitoring and management, fast multi-topic search, and Schema registry and Kafka Connect support.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License duration:&lt;/strong&gt; 12 months, renewable annually&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Installations:&lt;/strong&gt; Up to 3 per product (Kpow CE: 1 Kafka cluster + 1 Schema Registry + 1 Connect cluster per installation; Flex CE: 1 Flink cluster per installation)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support:&lt;/strong&gt; Self-service via Factor House Community Slack, documentation, and release notes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment:&lt;/strong&gt; Docker, Docker Compose or Kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ready for production? Start a &lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;30-day free trial of our Enterprise editions&lt;/a&gt; directly from the portal to unlock RBAC, Kafka Streams monitoring, custom SerDes, and dedicated support.&lt;/p&gt;
&lt;h2 id=&quot;what-about-legacy-licenses&quot;&gt;What about legacy licenses?&lt;/h2&gt;
&lt;p&gt;If you’re currently using a Kpow Individual, Kpow Organization, or Flex Community license, nothing changes immediately. Your existing licenses will continue to work with their respective products and are now accessible in the portal. When your license expires at the end of its 12-month term, you can easily switch to the new unified license for simpler management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get your free Community license at&lt;/strong&gt; &lt;a href=&quot;http://account.factorhouse.io/&quot;&gt;&lt;strong&gt;account.factorhouse.io&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Questions? Reach out to &lt;a href=&quot;mailto:hello@factorhouse.io&quot;&gt;hello@factorhouse.io&lt;/a&gt; or join us in the &lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;Factor House Community Slack.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>Product</category><author>Sarah Brown</author></item><item><title>Kpow Custom Serdes and Protobuf v4.31.1</title><link>https://factorhouse.io/articles/kpow-custom-serdes-protobuf-4-31-1/</link><guid isPermaLink="true">https://factorhouse.io/articles/kpow-custom-serdes-protobuf-4-31-1/</guid><description>This post explains an update in the version of protobuf libraries used by Kpow, and a possible compatibility impact this update may cause to user defined Custom Serdes.</description><pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;kpow-custom-serdes-and-protobuf-v4311&quot;&gt;Kpow Custom Serdes and Protobuf v4.31.1&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The potential compatibility issues described in this post only impacts users who have implemented Custom Serdes that contain generated protobuf classes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; If you encounter these compatibility issues, resolve them by re-generating any generated protobuf classes with &lt;strong&gt;&lt;code&gt;protoc v31.1&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In the upcoming v94.6 release of Kpow, we’re updating all Confluent Serdes dependencies to the latest major version &lt;strong&gt;&lt;code&gt;8.0.1&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;&lt;code&gt;io.confluent/kafka-protobuf-serializer:8.0.1&lt;/code&gt;&lt;/strong&gt; the protobuf version is advanced from &lt;strong&gt;&lt;code&gt;3.25.5&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;4.31.1&lt;/code&gt;&lt;/strong&gt;, and so the version of protobuf used by Kpow changes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Confluent protobuf upgrade PR: &lt;a href=&quot;https://github.com/confluentinc/schema-registry/pull/3569&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/confluentinc/schema-registry/pull/3569&quot;&gt;https://github.com/confluentinc/schema-registry/pull/3569&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Related Github issue: &lt;a href=&quot;https://github.com/confluentinc/schema-registry/issues/3047&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/confluentinc/schema-registry/issues/3047&quot;&gt;https://github.com/confluentinc/schema-registry/issues/3047&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a major upgrade of the underlying protobuf libraries, and there are some breaking changes related to generated code.&lt;/p&gt;
&lt;p&gt;Protobuf &lt;strong&gt;&lt;code&gt;3.26.6&lt;/code&gt;&lt;/strong&gt; introduces a breaking change that fails at runtime (deliberately) if the &lt;strong&gt;&lt;code&gt;makeExtensionsImmutable&lt;/code&gt;&lt;/strong&gt; method is called as part of generated protobuf code.&lt;/p&gt;
&lt;p&gt;The decision to break at runtime was taken because earlier versions of protobuf were found to be vulnerable to the &lt;a href=&quot;https://github.com/protocolbuffers/protobuf/security/advisories/GHSA-h4h5-3hr4-j3g2&quot;&gt;&lt;strong&gt;footmitten CVE&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protobuf footmitten CVE and breaking change announcement: &lt;a href=&quot;https://protobuf.dev/news/2025-01-23/&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://protobuf.dev/news/2025-01-23/&quot;&gt;https://protobuf.dev/news/2025-01-23/&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apache protobuf discussion thread: &lt;a href=&quot;https://lists.apache.org/thread/87osjw051xnx5l5v50dt3t81yfjxygwr&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://lists.apache.org/thread/87osjw051xnx5l5v50dt3t81yfjxygwr&quot;&gt;https://lists.apache.org/thread/87osjw051xnx5l5v50dt3t81yfjxygwr&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Comment on a Schema Registry ticket: &lt;a href=&quot;https://github.com/confluentinc/schema-registry/issues/3360&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/confluentinc/schema-registry/issues/3360&quot;&gt;https://github.com/confluentinc/schema-registry/issues/3360&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We found that when we advanced to the 8.0.1 version of the libraries; we encountered issues with some test classes generated by 3.x protobuf libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compilation issues:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Compiling &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;14&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; source files to &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;runner&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;work&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kpow&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;enterprise&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;classes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;runner&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;work&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;modules&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kpow&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;dev&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;factorhouse&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;serdes&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;MyRecordOuterClass.java:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;129&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: cannot find symbol&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;        makeExtensionsImmutable&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  symbol&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:   method &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;makeExtensionsImmutable&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  location&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; MyRecord&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Runtime issues:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Bad &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; operand stack&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Exception Details:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Location:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;io/confluent/kafka/schemaregistry/protobuf/ProtobufSchema.toMessage(Lcom/google/protobuf/DescriptorProtos$FileDescriptorProto;Lcom&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;google&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;protobuf&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;DescriptorProtos$DescriptorProto;)Lcom&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;squareup&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;wire&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;internal&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;parser&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;MessageElement; : invokestatic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Reason&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Type &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/protobuf/DescriptorProtos$MessageOptions&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (current frame, stack[&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]) is not assignable to &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/protobuf/GeneratedMessage$ExtendableMessage&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Current &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Frame&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;bci&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;flags&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: { }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;locals&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/protobuf/DescriptorProtos$FileDescriptorProto&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/protobuf/DescriptorProtos$DescriptorProto&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;java/lang/String&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;java/util/LinkedHashMap&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;java/util/LinkedHashMap&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;java/util/List&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;stack&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/common/collect/ImmutableList$Builder&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&apos;com/google/protobuf/DescriptorProtos$MessageOptions&apos;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Bytecode&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0000000&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: 2bb6 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0334&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; 4db2 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0072&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1303&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; 352c b903 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3703&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0000010&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: 00b8 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0159&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; 4eb8 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0159&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; 3a04 b801 593a 05b8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0000020&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0159&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; 3a06 bb02 &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;8959&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; b702 8b3a 07bb &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0289&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you encounter these compatibility issues, resolve them by re-generating any generated protobuf classes with &lt;strong&gt;&lt;code&gt;protoc v31.1&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
</content:encoded><category>Product</category><author>Derek Troy-West</author></item><item><title>A final goodbye to OperatrIO</title><link>https://factorhouse.io/articles/final-goodbye-operatr-io/</link><guid isPermaLink="true">https://factorhouse.io/articles/final-goodbye-operatr-io/</guid><description>2025 is a pivotal moment at Factor House (formally Operatr.IO). We&apos;ve announced our fundraise and have much more to announce about our roadmap this year. This is why we think that now is the perfect time to do a bit of spring cleaning and retire the io.operatr artifacts for good.</description><pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;a-final-goodbye-to-iooperatr&quot;&gt;A final goodbye to io.operatr&lt;/h2&gt;
&lt;p&gt;2025 is a pivotal moment at Factor House (formally Operatr.IO). We’ve announced our fundraise and have much more to announce about our roadmap this year. This is why we think that now is the perfect time to do a bit of spring cleaning and retire the &lt;strong&gt;&lt;code&gt;io.operatr&lt;/code&gt;&lt;/strong&gt; artifacts for good.&lt;/p&gt;
&lt;p&gt;One of the hallmarks of Factor House has always been our unwavering commitment to &lt;strong&gt;backwards compatibility&lt;/strong&gt;, ensuring that our customers can seamlessly transition between versions without disruptions to their configurations, deployments, or workflows. While this has been a source of pride for us, sometimes we take this mantra to the extreme. While our product has been named Kpow and our company Factor House for some years now, we were still publishing our Docker images to the old &lt;strong&gt;&lt;code&gt;operatr/kpow&lt;/code&gt;&lt;/strong&gt; DockerHub repository.&lt;/p&gt;
&lt;p&gt;This blog post outlines our plan to retire the io.operatr artifacts and provides repository details on where to find your new Factor House goodies!&lt;/p&gt;
&lt;h3 id=&quot;documenting-the-changes-a-transparent-approach&quot;&gt;Documenting the Changes: A Transparent Approach&lt;/h3&gt;
&lt;p&gt;To make this transition smooth for everyone involved, we want to be as transparent as possible about the changes we’re making. Here’s a detailed breakdown of the updates across our key repositories:&lt;/p&gt;
&lt;h4 id=&quot;dockerhub&quot;&gt;DockerHub&lt;/h4&gt;
&lt;p&gt;As mentioned earlier, any new updates to Kpow will only be published to the &lt;strong&gt;&lt;code&gt;factorhouse/kpow&lt;/code&gt;&lt;/strong&gt; repo:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Previous image location&lt;/th&gt;
&lt;th&gt;New image location&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow&quot;&gt;Kpow&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;operatr/operatr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;factorhouse/kpow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Has always mirrored &lt;code&gt;factorhouse/kpow&lt;/code&gt;. Starting with 94.1 we will stop mirroring to &lt;code&gt;operatr/operatr&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow&quot;&gt;Kpow&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;operatr/kpow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;factorhouse/kpow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Has always mirrored &lt;code&gt;factorhouse/kpow&lt;/code&gt;. Starting with 94.1 we will stop mirroring to &lt;code&gt;operatr/operatr&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To read more about our container changes please see &lt;a href=&quot;https://factorhouse.io/blog/articles/updates-to-container-specifics/&quot;&gt;&lt;strong&gt;this blog post&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;helm-charts&quot;&gt;Helm Charts&lt;/h4&gt;
&lt;p&gt;Our Helm Charts are now multi-product! New releases will be pushed to the &lt;strong&gt;&lt;code&gt;https://charts.factorhouse.io&lt;/code&gt;&lt;/strong&gt; repository or the &lt;a href=&quot;https://artifacthub.io/packages/search?org=factorhouse&quot;&gt;&lt;strong&gt;factorhouse&lt;/strong&gt;&lt;/a&gt; ArtifactHub repo.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Previous chart location&lt;/th&gt;
&lt;th&gt;New chart location&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow&quot;&gt;Kpow&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kpow/kpow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;factorhouse/kpow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Visit our &lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;helm-charts repo&lt;/a&gt; for more details.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To read more about our container changes please see &lt;a href=&quot;https://factorhouse.io/blog/articles/updates-to-container-specifics/&quot;&gt;&lt;strong&gt;this blog post&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;maven&quot;&gt;Maven&lt;/h4&gt;
&lt;p&gt;We are updating all Maven projects to reflect the Factor House name and branding. This includes updating POM files and repository URLs to ensure compatibility with our latest releases.&lt;/p&gt;
&lt;p&gt;That means all Factor House open source will be deployed to the &lt;a href=&quot;https://central.sonatype.com/namespace/io.factorhouse&quot;&gt;&lt;strong&gt;io.factorhouse&lt;/strong&gt;&lt;/a&gt; Maven central namespace.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Previous deployment&lt;/th&gt;
&lt;th&gt;New deployment&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/factorhouse/kpow-streams-agent&quot;&gt;kpow-streams-agent&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.operatr/kpow-streams-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.factorhouse/kpow-streams-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;As part of 94.1, we have moved the streams agent code to &lt;code&gt;io.factorhouse&lt;/code&gt;. We have also pushed significant improvements to the library!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;clojars&quot;&gt;Clojars&lt;/h4&gt;
&lt;p&gt;Our Clojure libraries will be deprecated under the &lt;strong&gt;&lt;code&gt;io.operatr&lt;/code&gt;&lt;/strong&gt; namespace and replaced with new packages under the updated namespace:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Previous deployment&lt;/th&gt;
&lt;th&gt;New deployment&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/factorhouse/kpow-secure&quot;&gt;shroud&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.operatr/kpow-secure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.factorhouse/shroud&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previously named &lt;code&gt;kpow-secure&lt;/code&gt;. New name reflects its general cross-product utility.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;looking-ahead-a-bright-future-for-factor-house&quot;&gt;Looking Ahead: A Bright Future for Factor House&lt;/h3&gt;
&lt;p&gt;This is not just about retiring old artifacts — it’s about celebrating a new chapter in our journey. As we grow and evolve, we’re committed to maintaining the level of excellence that has made us a trusted partner for businesses around the world.&lt;/p&gt;
&lt;p&gt;The decision to retire &lt;strong&gt;&lt;code&gt;io.operatr&lt;/code&gt;&lt;/strong&gt; isn’t a goodbye to our past but rather a hello to a future filled with endless possibilities. We’re excited to continue building innovative solutions under the Factor House banner, delivering the same reliability and forward-thinking approach our customers have come to expect.&lt;/p&gt;
&lt;p&gt;As we move forward, we’ll be sharing more updates about our roadmap and new offerings. Stay tuned for an even brighter year ahead!&lt;/p&gt;
</content:encoded><category>Company</category><author>Factor House</author></item><item><title>Ensuring Your Data Streaming Stack Is Ready for the EU Data Act</title><link>https://factorhouse.io/articles/ensuring-your-data-streaming-stack-is-ready-for-the-eu-data-act/</link><guid isPermaLink="true">https://factorhouse.io/articles/ensuring-your-data-streaming-stack-is-ready-for-the-eu-data-act/</guid><description>The EU Data Act takes effect in September 2025, introducing major implications for teams running Kafka. This article explores what the Act means for data streaming engineers, and how Kpow can help ensure compliance — from user data access to audit logging and secure interoperability.</description><pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction-whats-changing&quot;&gt;Introduction: What’s Changing?&lt;/h2&gt;
&lt;p&gt;The EU Data Act, effective as of September 12, 2025, introduces new requirements for data access, sharing, and interoperability across the European Union’s single market. For engineers managing data streaming platforms like Apache Kafka, this means re-evaluating how your stack handles user data, security, provider switching, and compliance with both the Data Act and GDPR.&lt;/p&gt;
&lt;h2 id=&quot;what-does-the-eu-data-act-mean-for-data-streaming&quot;&gt;What Does the EU Data Act Mean for Data Streaming?&lt;/h2&gt;
&lt;p&gt;At its core, the Data Act requires that users of connected products and services have access to their raw usage data and metadata. This means that streaming platforms processing such data need to enable seamless and secure access and sharing capabilities. Additionally, the Act outlines rules for business-to-business and business-to-government data sharing, while safeguarding trade secrets and ensuring the security of data.&lt;/p&gt;
&lt;h3 id=&quot;key-highlights-of-the-act&quot;&gt;Key highlights of the Act&lt;/h3&gt;
&lt;h2 id=&quot;why-these-changes-matter-for-kafka-engineers&quot;&gt;Why These Changes Matter for Kafka Engineers&lt;/h2&gt;
&lt;p&gt;Kafka is at the heart of many modern data streaming architectures, making it critical to align Kafka operations with these new legal requirements. Engineers must ensure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data streams can be accessed and shared securely and transparently.&lt;/li&gt;
&lt;li&gt;Systems support interoperability to facilitate provider switching without data loss or downtime.&lt;/li&gt;
&lt;li&gt;Access controls and auditing are in place to protect sensitive data and demonstrate compliance.&lt;/li&gt;
&lt;li&gt;Data subject rights under GDPR, such as access, correction, and deletion, can be fulfilled efficiently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-kpow-supports-compliance-and-operational-excellence&quot;&gt;How Kpow Supports Compliance and Operational Excellence&lt;/h2&gt;
&lt;p&gt;At Factor House, we designed Kpow with these challenges in mind. As an enterprise-native company, our flagship solution Kpow is a comprehensive Kafka management and monitoring platform that empowers engineers to meet the demands of the new EU data landscape. Right out of the box, Kpow enables engineers to meet stringent EU Data Act requirements with ease.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;EU Data Act&lt;/th&gt;
&lt;th&gt;Kpow’s fulfilment out-of-the-box&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Access and Portability&lt;/strong&gt;: Data processing services must provide users and authorized third parties access to product and service data in accessible, interoperable formats and to support seamless switching between providers.&lt;/td&gt;
&lt;td&gt;Kpow connects directly to Kafka clusters using standard client configurations, enabling engineers to expose and manage streaming data effectively, supporting data access requests and portability without vendor lock-in or switching charges.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transparency and Jurisdictional Information:&lt;/strong&gt; Mandated transparency about the jurisdiction of ICT infrastructure and the technical, organizational, and contractual safeguards against unlawful international governmental access to non-personal data.&lt;/td&gt;
&lt;td&gt;Kpow stores all monitoring data locally within Kafka clusters, minimizing data exposure and supporting data sovereignty.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security and Access Controls:&lt;/strong&gt; Protect trade secrets and personal data, and comply with GDPR when personal data is involved.&lt;/td&gt;
&lt;td&gt;Kpow integrates with enterprise-grade authentication providers (OAuth2, OpenID, SAML, LDAP) and implements configurable Role-Based Access Control (RBAC), ensuring that only authorized users can access sensitive data streams. Kpow allows configurable redaction of data inspection results through its data policies, providing enhanced protection against the exposure of sensitive information.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auditability and Monitoring:&lt;/strong&gt; Data sharing and security require an auditable trail of who accessed what data and when.&lt;/td&gt;
&lt;td&gt;Kpow provides rich telemetry, consumer group insights, and audit logging capabilities, enabling organizations to monitor data access and usage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Switching and Interoperability&lt;/strong&gt;: Ensure customers can migrate data streaming workloads smoothly without disruption or additional costs.&lt;/td&gt;
&lt;td&gt;Kpow enables multi-cluster management through standard Kafka client configurations, allowing seamless connection, monitoring, and migration across multiple Kafka clusters and environments without vendor lock-in or proprietary dependencies.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal Procedures and Legal Compliance Support:&lt;/strong&gt; Protect trade secrets and other sensitive data while enabling lawful data sharing without unnecessary obstruction.&lt;/td&gt;
&lt;td&gt;By providing detailed visibility and control over Kafka data streams, Kpow helps organizations implement internal procedures to respond promptly to data access requests, identify trade secrets, and apply necessary protective measures.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;practical-steps-for-engineers&quot;&gt;Practical Steps for Engineers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Review your current Kafka stack: Ensure configurations support data access, portability, and interoperability.&lt;/li&gt;
&lt;li&gt;Implement robust authentication and RBAC: Protect sensitive streams and support GDPR compliance.&lt;/li&gt;
&lt;li&gt;Enable detailed audit logging: Prepare for regulatory audits and internal monitoring.&lt;/li&gt;
&lt;li&gt;Test provider switching: Validate that you can migrate workloads without disruption or extra costs.&lt;/li&gt;
&lt;li&gt;Stay updated: Monitor regulatory updates and best practices for ongoing compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;access-turnkey-compliance-with-kpow&quot;&gt;Access “Turnkey” Compliance with Kpow&lt;/h2&gt;
&lt;p&gt;Kpow’s secure, transparent, and flexible Kafka management capabilities align with the EU Data Act’s requirements, enabling controlled data access, robust security, local data storage, auditability, and interoperability. This makes it an effective tool for data streaming engineers and organizations aiming to comply with the EU’s new data sharing and protection rules starting September 2025.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7c00b04edd080bdcc1622_gdpr-demo.gif&quot; alt=&quot;Kpow GDPR compliance demonstration&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;future-proof-your-kafka-streaming&quot;&gt;Future-Proof Your Kafka Streaming&lt;/h2&gt;
&lt;p&gt;The EU Data Act is reshaping how data streaming services operate in Europe. Ensuring your Kafka infrastructure is compliant and resilient is no longer optional—it’s essential.&lt;/p&gt;
&lt;p&gt;To help you navigate this transition, Factor House offers a free 30-day fully-featured trial license of Kpow.&lt;/p&gt;
&lt;p&gt;Experience firsthand how Kpow’s secure, transparent, and flexible Kafka management capabilities can simplify compliance and enhance your streaming operations. &lt;a href=&quot;https://factorhouse.io/kpow/get-started/&quot;&gt;&lt;strong&gt;Start your free trial of Kpow today&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;sources-and-further-information&quot;&gt;Sources (and further information)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://digital-strategy.ec.europa.eu/en/factpages/data-act-explained&quot;&gt;&lt;strong&gt;Data Act explained, European Commission, accessed 12th June 2025&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.deloitte.com/lu/en/Industries/technology/perspectives/the-eu-data-act-what-does-it-mean-for-you.html&quot;&gt;&lt;strong&gt;The EU Data Act: What does it mean for you?, Deloitte, accessed 12th June 2025&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wsgrdataadvisor.com/2025/03/eu-data-act-imposes-new-data-sharing-obligations/&quot;&gt;&lt;strong&gt;EU Data Act Imposes New Data Sharing Obligations, The WSGR Data Advisor, accessed 12th June 2025&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scaler.com/topics/kafka-tutorial/kafka-compliance/&quot;&gt;&lt;strong&gt;Kafka Compliance, Scalar Topics, accessed 12th June 2025&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded><category>Guides</category><author>Factor House</author></item><item><title>Deploy Kpow on EKS via AWS Marketplace using Helm</title><link>https://factorhouse.io/articles/deploy-kpow-on-eks-via-aws-marketplace/</link><guid isPermaLink="true">https://factorhouse.io/articles/deploy-kpow-on-eks-via-aws-marketplace/</guid><description>Streamline your Kpow deployment on Amazon EKS with our guide, fully integrated with the AWS Marketplace. We use eksctl to automate IAM Roles for Service Accounts (IRSA), providing a secure integration for Kpow&apos;s licensing and metering. This allows your instance to handle license validation via AWS License Manager and report usage for hourly subscriptions, enabling a production-ready deployment with minimal configuration.</description><pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;This guide provides a comprehensive walkthrough for deploying Kpow, a powerful toolkit for Apache Kafka, onto an Amazon EKS (Elastic Kubernetes Service) cluster. We will cover the entire process from start to finish, including provisioning the necessary AWS infrastructure, deploying a Kafka cluster using the Strimzi operator, and finally, installing Kpow using a subscription from the AWS Marketplace.&lt;/p&gt;
&lt;p&gt;The guide demonstrates how to set up both &lt;strong&gt;Kpow Annual&lt;/strong&gt; and &lt;strong&gt;Kpow Hourly&lt;/strong&gt; products, highlighting the specific integration points with AWS services like IAM for service accounts, ECR for container images, and the AWS License Manager for the annual subscription. By the end of this tutorial, you will have a fully functional environment running Kpow on EKS, ready to monitor and manage your Kafka cluster.&lt;/p&gt;
&lt;p&gt;The source code and configuration files used in this guide can be found in the &lt;strong&gt;&lt;code&gt;features/eks-deployment&lt;/code&gt;&lt;/strong&gt; folder of this &lt;a href=&quot;https://github.com/factorhouse/examples&quot;&gt;&lt;strong&gt;GitHub repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;/products/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df945123_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow along the guide, you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CLI Tools:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Recent versions of the following command-line interface (CLI) tools must be installed:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/eksctl/what-is-eksctl.html&quot;&gt;&lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;: For creating and managing Amazon EKS clusters.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/&quot;&gt;&lt;strong&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt;: For interacting with Kubernetes resources.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://helm.sh/&quot;&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/a&gt;: The package manager for Kubernetes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Infrastructure:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VPC&lt;/strong&gt; : A Virtual Private Cloud (VPC) that has both public and private subnets is required.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Permissions&lt;/strong&gt; : A user with the necessary IAM permissions to create an EKS cluster with a service account.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kpow Subscription:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A subscription to a &lt;a href=&quot;https://aws.amazon.com/marketplace/seller-profile?id=ab356f1d-3394-4523-b5d4-b339e3cca9e0&quot;&gt;&lt;strong&gt;Kpow product through the AWS Marketplace&lt;/strong&gt;&lt;/a&gt; is required. After subscribing, you will receive access to the necessary components and deployment instructions.&lt;/li&gt;
&lt;li&gt;The specifics of accessing the container images and Helm chart depend on the chosen Kpow product:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kpow Annual product&lt;/strong&gt; :
&lt;ul&gt;
&lt;li&gt;Subscribing to the annual product provides access to the ECR (Elastic Container Registry) image and the corresponding Helm chart.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kpow Hourly product&lt;/strong&gt; :
&lt;ul&gt;
&lt;li&gt;For the hourly product, access to the ECR image will be provided and deployment utilizes the public Factor House Helm repository for installation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;deploy-an-eks-cluster&quot;&gt;Deploy an EKS cluster&lt;/h2&gt;
&lt;p&gt;We will use &lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt; to provision an Amazon EKS cluster. The configuration for the cluster is defined in the &lt;strong&gt;&lt;code&gt;manifests/eks/cluster.eksctl.yaml&lt;/code&gt;&lt;/strong&gt; file within the repository.&lt;/p&gt;
&lt;p&gt;Before creating the cluster, you must open this file and replace the placeholder values for &lt;strong&gt;&lt;code&gt;&amp;lt;VPC-ID&amp;gt;&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;&amp;lt;PRIVATE-SUBNET-ID-* &amp;gt;&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;&amp;lt;PUBLIC-SUBNET-ID-* &amp;gt;&lt;/code&gt;&lt;/strong&gt; with your actual VPC and subnet IDs.&lt;strong&gt;&lt;em&gt;‍&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The provided configuration assumes the EKS cluster will be deployed in the &lt;code&gt;us-east-1&lt;/code&gt; region. If you intend to use a different region, you must update the &lt;code&gt;metadata.region&lt;/code&gt; field and ensure the availability zone keys under &lt;code&gt;vpc.subnets&lt;/code&gt; (e.g., &lt;code&gt;us-east-1a&lt;/code&gt;, &lt;code&gt;us-east-1b&lt;/code&gt;) match the availability zones of the subnets in your chosen region.&lt;/p&gt;
&lt;p&gt;Here is the content of the &lt;strong&gt;&lt;code&gt;cluster.eksctl.yaml&lt;/code&gt;&lt;/strong&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;apiVersion&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;eksctl.io/v1alpha5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ClusterConfig&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;fh-eks-cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  region&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;us-east-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;vpc&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;VPC-ID&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  subnets&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      us-east-1a&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;PRIVATE-SUBNET-ID-1&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      us-east-1b&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;PRIVATE-SUBNET-ID-2&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      us-east-1a&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;PUBLIC-SUBNET-ID-1&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      us-east-1b&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;PUBLIC-SUBNET-ID-2&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;iam&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  withOIDC&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  serviceAccounts&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-annual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        namespace&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      attachPolicyARNs&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;arn:aws:iam::aws:policy/service-role/AWSLicenseManagerConsumptionPolicy&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-hourly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        namespace&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      attachPolicyARNs&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;arn:aws:iam::aws:policy/AWSMarketplaceMeteringRegisterUsage&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;nodeGroups&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ng-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    instanceType&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;t3.medium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    desiredCapacity&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    minSize&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    maxSize&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    privateNetworking&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration sets up the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cluster Metadata&lt;/strong&gt; : A cluster named &lt;strong&gt;&lt;code&gt;fh-eks-cluster&lt;/code&gt;&lt;/strong&gt; in the &lt;strong&gt;&lt;code&gt;us-east-1&lt;/code&gt;&lt;/strong&gt; region.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VPC&lt;/strong&gt; : Specifies an existing VPC and its public/private subnets where the cluster resources will be deployed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM with OIDC&lt;/strong&gt; : Enables the IAM OIDC provider, which allows Kubernetes service accounts to be associated with IAM roles. This is crucial for granting AWS permissions to your pods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Accounts&lt;/strong&gt; :
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;kpow-annual&lt;/code&gt;&lt;/strong&gt; : Creates a service account for the Kpow Annual product. It attaches the &lt;strong&gt;&lt;code&gt;AWSLicenseManagerConsumptionPolicy&lt;/code&gt;&lt;/strong&gt; , allowing Kpow to validate its license with the AWS License Manager service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;kpow-hourly&lt;/code&gt;&lt;/strong&gt; : Creates a service account for the Kpow Hourly product. It attaches the &lt;strong&gt;&lt;code&gt;AWSMarketplaceMeteringRegisterUsage&lt;/code&gt;&lt;/strong&gt; policy, which is required for reporting usage metrics to the AWS Marketplace.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node Group&lt;/strong&gt; : Defines a managed node group named &lt;strong&gt;&lt;code&gt;ng-dev&lt;/code&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;code&gt;t3.medium&lt;/code&gt;&lt;/strong&gt; instances. The worker nodes will be placed in the private subnets (&lt;strong&gt;&lt;code&gt;privateNetworking: true&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you have updated the YAML file with your networking details, run the following command to create the cluster. This process can take 15-20 minutes to complete.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;eksctl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; cluster&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; cluster.eksctl.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the cluster is created, &lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt; automatically updates your &lt;strong&gt;&lt;code&gt;kubeconfig&lt;/code&gt;&lt;/strong&gt; file (usually located at &lt;strong&gt;&lt;code&gt;~/.kube/config&lt;/code&gt;&lt;/strong&gt;) with the new cluster’s connection details. This allows you to start interacting with your cluster immediately using &lt;strong&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; nodes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                              STATUS   ROLES    AGE     VERSION&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ip-192-168-...-21.ec2.internal   Ready    &amp;#x3C;none&gt;   2m15s    v1.32.9-eks-113cf36&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;launch-a-kafka-cluster&quot;&gt;Launch a Kafka cluster&lt;/h2&gt;
&lt;p&gt;With the EKS cluster running, we will now launch an Apache Kafka cluster into it. We will use the &lt;a href=&quot;https://strimzi.io/&quot;&gt;&lt;strong&gt;Strimzi Kafka operator&lt;/strong&gt;&lt;/a&gt;, which simplifies the process of running Kafka on Kubernetes.&lt;/p&gt;
&lt;h3 id=&quot;install-the-strimzi-operator&quot;&gt;Install the Strimzi operator&lt;/h3&gt;
&lt;p&gt;First, create a dedicated namespace for the Kafka cluster.&lt;/p&gt;
&lt;p&gt;kubectl create namespace kafka&lt;/p&gt;
&lt;p&gt;Next, download the Strimzi operator installation YAML. The repository already contains the file &lt;strong&gt;&lt;code&gt;manifests/kafka/strimzi-cluster-operator-0.45.1.yaml&lt;/code&gt;&lt;/strong&gt; , but the following commands show how it was downloaded and modified for this guide.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## Define the Strimzi version and download URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;0.45.1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;DOWNLOAD_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;https://github.com/strimzi/strimzi-kafka-operator/releases/download/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/strimzi-cluster-operator-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## Download the operator manifest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -L&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/strimzi-cluster-operator-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.yaml&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ${DOWNLOAD_URL}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## Modify the manifest to install the operator in the &apos;kafka&apos; namespace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &apos;s/namespace: .*/namespace: kafka/&apos;&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/strimzi-cluster-operator-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, apply the manifest to install the Strimzi operator in your EKS cluster.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; apply&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/strimzi-cluster-operator-0.45.1.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deploy-a-kafka-cluster&quot;&gt;Deploy a Kafka cluster&lt;/h3&gt;
&lt;p&gt;The configuration for our Kafka cluster is defined in &lt;strong&gt;&lt;code&gt;manifests/kafka/kafka-cluster.yaml&lt;/code&gt;&lt;/strong&gt;. It describes a simple, single-node cluster suitable for development, using ephemeral storage, meaning data will be lost if the pods restart.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;apiVersion&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka.strimzi.io/v1beta2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;Kafka&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;fh-k8s-cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;spec&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  kafka&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    version&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3.9.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    replicas&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    listeners&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;plain&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        port&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9092&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;internal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;        tls&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ... (content truncated for brevity)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deploy the Kafka cluster with the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/kafka-cluster.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;verify-the-deployment&quot;&gt;Verify the deployment&lt;/h3&gt;
&lt;p&gt;After a few minutes, all the necessary pods and services for Kafka will be running. You can verify this by listing all resources in the &lt;strong&gt;&lt;code&gt;kafka&lt;/code&gt;&lt;/strong&gt; namespace.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should look similar to this, showing the pods for Strimzi, Kafka, Zookeeper, and the associated services. The most important service for connecting applications is the Kafka bootstrap service.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pod/fh-k8s-cluster-entity-operator-...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pod/fh-k8s-cluster-kafka-0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# service/fh-k8s-cluster-kafka-bootstrap &amp;#x3C;-- Kafka bootstrap service&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;deploy-kpow&quot;&gt;Deploy Kpow&lt;/h2&gt;
&lt;p&gt;Now that the EKS and Kafka clusters are running, we can deploy Kpow. This guide covers the deployment of both Kpow Annual and Kpow Hourly products. Both deployments will use a common set of configurations for connecting to Kafka and setting up authentication/authorization.&lt;/p&gt;
&lt;p&gt;First, ensure you have a namespace for Kpow. The &lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt; command we ran earlier already created the service accounts in the &lt;strong&gt;&lt;code&gt;factorhouse&lt;/code&gt;&lt;/strong&gt; namespace, so we will use that. If you hadn’t created it, you would run &lt;strong&gt;&lt;code&gt;kubectl create namespace factorhouse&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;create-configmaps&quot;&gt;Create ConfigMaps&lt;/h3&gt;
&lt;p&gt;We will use two Kubernetes ConfigMaps to manage Kpow’s configuration. This approach separates the core configuration from the Helm deployment values.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;kpow-config-files&lt;/code&gt;&lt;/strong&gt; : This ConfigMap holds file-based configurations, including RBAC policies, JAAS configuration, and user properties for authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;kpow-config&lt;/code&gt;&lt;/strong&gt; : This ConfigMap provides environment variables to the Kpow container, such as the Kafka bootstrap address and settings to enable our authentication provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The contents of these files can be found in the repository at &lt;strong&gt;&lt;code&gt;manifests/kpow/config-files.yaml&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;manifests/kpow/config.yaml&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;manifests/kpow/config-files.yaml&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;apiVersion&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ConfigMap&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config-files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  namespace&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  hash-rbac.yml&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # RBAC policies defining user roles and permissions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    admin_roles:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;      - &quot;kafka-admins&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # ... (content truncated for brevity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  hash-jaas.conf&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # JAAS login module configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    kpow {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;      org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;      file=&quot;/etc/kpow/jaas/hash-realm.properties&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # ... (content truncated for brevity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  hash-realm.properties&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # User credentials (username: password, roles)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # admin/admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    admin: CRYPT:adpexzg3FUZAk,server-administrators,content-administrators,kafka-admins&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # user/password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    user: password,kafka-users&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;    # ... (content truncated for brevity)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;manifests/kpow/config.yaml&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;apiVersion&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;v1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;kind&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ConfigMap&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  namespace&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Environment Configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  BOOTSTRAP&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;fh-k8s-cluster-kafka-bootstrap.kafka.svc.cluster.local:9092&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  REPLICATION_FACTOR&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # AuthN + AuthZ&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;-Djava.awt.headless=true -Djava.security.auth.login.config=/etc/kpow/jaas/hash-jaas.conf&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  AUTH_PROVIDER_TYPE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;jetty&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  RBAC_CONFIGURATION_FILE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;/etc/kpow/rbac/hash-rbac.yml&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply these manifests to create the ConfigMaps in the &lt;strong&gt;&lt;code&gt;factorhouse&lt;/code&gt;&lt;/strong&gt; namespace.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; apply&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kpow/config-files.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kpow/config.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify their creation by running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; configmap&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                DATA   AGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# kpow-config         5      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# kpow-config-files   3      ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deploy-kpow-annual&quot;&gt;Deploy Kpow Annual&lt;/h3&gt;
&lt;h4 id=&quot;download-the-helm-chart&quot;&gt;Download the Helm chart&lt;/h4&gt;
&lt;p&gt;The Helm chart for Kpow Annual is in a private Amazon ECR repository. First, authenticate your Helm client.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Enable Helm&apos;s experimental support for OCI registries&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; HELM_EXPERIMENTAL_OCI&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Log in to the AWS Marketplace ECR registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ecr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get-login-password&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --region&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; us-east-1&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; registry&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; login&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --username&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; AWS&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --password-stdin&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 709825985650.dkr.ecr.us-east-1.amazonaws.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, pull and extract the chart.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Create a directory, pull the chart, and extract it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; awsmp-chart&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; awsmp-chart&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Pull the latest version of the Helm chart from ECR (add --version &amp;#x3C;x.x.x&gt; to specify a version)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; pull&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/factor-house/kpow-aws-annual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;tar&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; xf&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; $(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;pwd&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; $(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;pwd&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;-maxdepth&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -type&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -delete&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ..&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;launch-kpow-annual&quot;&gt;Launch Kpow Annual&lt;/h4&gt;
&lt;p&gt;Now, install Kpow using Helm. We will reference the service account &lt;strong&gt;&lt;code&gt;kpow-annual&lt;/code&gt;&lt;/strong&gt; that was created during the EKS cluster setup, which has the required IAM policy for license management.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow-annual&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ./awsmp-chart/kpow-aws-annual/&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --set&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; serviceAccount.create=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --set&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; serviceAccount.name=kpow-annual&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --values&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ./values/eks-annual.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Helm values for this deployment are in &lt;strong&gt;&lt;code&gt;values/eks-annual.yaml&lt;/code&gt;&lt;/strong&gt;. It mounts the configuration files from our ConfigMaps and sets resource limits.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# values/eks-annual.yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  ENVIRONMENT_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kafka from Kpow Annual&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;envFromConfigMap&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kpow-config&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;volumeMounts&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config-volumes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    mountPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/etc/kpow/rbac/hash-rbac.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    subPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;hash-rbac.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config-volumes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    mountPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/etc/kpow/jaas/hash-jaas.conf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    subPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;hash-jaas.conf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config-volumes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    mountPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;/etc/kpow/jaas/hash-realm.properties&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    subPath&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;hash-realm.properties&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;volumes&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-config-volumes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    configMap&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kpow-config-files&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  limits&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    cpu&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    memory&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;0.5Gi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  requests&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    cpu&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    memory&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;0.5Gi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: The CPU and memory values are intentionally set low for this guide. For production environments, check the &lt;a href=&quot;https://docs.factorhouse.io/kpow/faq/system-requirements#memory-and-cpu&quot;&gt;official documentation&lt;/a&gt; for recommended capacity.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;verify-and-access-kpow-annual&quot;&gt;Verify and access Kpow Annual&lt;/h4&gt;
&lt;p&gt;Check that the Kpow pod is running successfully.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -l&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; app.kubernetes.io/instance=kpow-annual&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                                              READY   STATUS    RESTARTS   AGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pod/kpow-annual-kpow-aws-annual-c6bc849fb-zw5ww   0/1     Running   0          46s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# service/kpow-annual-kpow-aws-annual   ClusterIP   10.100.220.114   &amp;#x3C;none&gt;        3000/TCP   47s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To access the UI, forward the service port to your local machine.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; port-forward&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; service/kpow-annual-kpow-aws-annual&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access Kpow by navigating to &lt;strong&gt;&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/strong&gt; in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df94511b_kpow-annual.png&quot; alt=&quot;Kpow Annual Overview&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;deploy-kpow-hourly&quot;&gt;Deploy Kpow Hourly&lt;/h3&gt;
&lt;h4 id=&quot;configure-the-kpow-helm-repository&quot;&gt;Configure the Kpow Helm repository&lt;/h4&gt;
&lt;p&gt;The Helm chart for Kpow Hourly is available in the &lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Factor House Helm repository&lt;/strong&gt;&lt;/a&gt;. First, add the Helm repository.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; repo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; https://charts.factorhouse.io&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, update Helm repositories to ensure you install the latest version of Kpow.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; repo&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;launch-kpow-hourly&quot;&gt;Launch Kpow Hourly&lt;/h4&gt;
&lt;p&gt;Install Kpow using Helm, referencing the &lt;strong&gt;&lt;code&gt;kpow-hourly&lt;/code&gt;&lt;/strong&gt; service account which has the IAM policy for marketplace metering.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow-hourly&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse/kpow-aws-hourly&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --set&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; serviceAccount.create=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --set&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; serviceAccount.name=kpow-hourly&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --values&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ./values/eks-hourly.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Helm values are defined in &lt;strong&gt;&lt;code&gt;values/eks-hourly.yaml&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# values/eks-hourly.yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  ENVIRONMENT_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kafka from Kpow Hourly&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;envFromConfigMap&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kpow-config&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;volumeMounts&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ... (volume configuration is the same as annual)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;volumes&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;verify-and-access-kpow-hourly&quot;&gt;Verify and access Kpow Hourly&lt;/h4&gt;
&lt;p&gt;Check that the Kpow pod is running.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -l&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; app.kubernetes.io/instance=kpow-hourly&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                                               READY   STATUS    RESTARTS   AGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pod/kpow-hourly-kpow-aws-hourly-68869b6cb9-x9prf   0/1     Running   0          83s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# NAME                                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# service/kpow-hourly-kpow-aws-hourly   ClusterIP   10.100.221.36   &amp;#x3C;none&gt;        3000/TCP   85s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To access the UI, forward the service port to a different local port (e.g., 3001) to avoid conflicts.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; port-forward&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; service/kpow-hourly-kpow-aws-hourly&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3001:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access Kpow by navigating to &lt;strong&gt;&lt;code&gt;http://localhost:3001&lt;/code&gt;&lt;/strong&gt; in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df94511e_kpow-hourly.png&quot; alt=&quot;Kpow Hourly Overview&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;delete-resources&quot;&gt;Delete resources&lt;/h2&gt;
&lt;p&gt;To avoid ongoing AWS charges, clean up all created resources in reverse order.&lt;/p&gt;
&lt;h3 id=&quot;delete-kpow-and-configmaps&quot;&gt;Delete Kpow and ConfigMaps&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;helm&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; uninstall&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow-annual&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow-hourly&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; delete&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kpow/config-files.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kpow/config.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;delete-the-kafka-cluster-and-strimzi-operator&quot;&gt;Delete the Kafka cluster and Strimzi operator&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;0.45.1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; delete&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/kafka-cluster.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;kubectl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; delete&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/kafka/strimzi-cluster-operator-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$STRIMZI_VERSION&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.yaml&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;delete-the-eks-cluster&quot;&gt;Delete the EKS cluster&lt;/h3&gt;
&lt;p&gt;This command will remove the cluster and all associated resources.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;eksctl&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; delete&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; cluster&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; manifests/eks/cluster.eksctl.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this guide, we have successfully deployed a complete, production-ready environment for monitoring Apache Kafka on AWS. By leveraging &lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt; , we provisioned a robust EKS cluster with correctly configured IAM roles for service accounts, a critical step for secure integration with AWS services. We then deployed a Kafka cluster using the Strimzi operator, demonstrating the power of Kubernetes operators in simplifying complex stateful applications.&lt;/p&gt;
&lt;p&gt;Finally, we walked through the deployment of both Kpow Annual and Kpow Hourly from the AWS Marketplace. This showcased the flexibility of Kpow’s subscription models and their seamless integration with AWS for licensing and metering. You are now equipped with the knowledge to set up, configure, and manage Kpow on EKS, unlocking powerful insights and operational control over your Kafka ecosystem.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Beyond Kafka: Sharp Signals from Current London 2025</title><link>https://factorhouse.io/articles/beyond-kafka-sharp-signals-from-current-london-2025/</link><guid isPermaLink="true">https://factorhouse.io/articles/beyond-kafka-sharp-signals-from-current-london-2025/</guid><description>The real-time ecosystem has outgrown Kafka alone. At Current London 2025, the transition from Kafka Summit was more than a name change — it marked a shift toward streaming-first AI, system-level control, and production-ready Flink. Here&apos;s what Factor House saw and learned on the ground.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The transition from Kafka Summit to Current is now complete, with this year’s London conference now rebranded to match Confluent’s US and India events and providing a strong indicator that the real-time ecosystem now extends far beyond Apache Kafka. With more than 2,200 attendees, in-depth technical presentations, and countless exhibitor hall discussions, it is evident that real-time streaming is here to stay and the ecosystem is evolving quickly, branching out into AI-native systems, multi-technology stacks, and production-grade stream processing.&lt;/p&gt;
&lt;h2 id=&quot;what-we-saw&quot;&gt;What We Saw&lt;/h2&gt;
&lt;p&gt;For the third consecutive year, Factor House was on the ground in London as a Silver Sponsor, running product demos, meeting clients, and, more importantly, learning about the needs of engineers managing complex deployments, platform teams integrating Flink and Kafka, and architects exploring AI built on live data.&lt;/p&gt;
&lt;p&gt;While the Kafka Summit name has been replaced, Kafka remains a foundational technology. However, attention is shifting to system-level control, end-to-end observability, and tools that reduce operational friction without sacrificing power. We’re focused on that space with Kpow for Kafka, Flex for Flink, and, soon, the Factor Platform.&lt;/p&gt;
&lt;h2 id=&quot;key-signals&quot;&gt;Key Signals&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AI Is Going Event-Driven - But Engineers Remain Cautious&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Streaming-first AI was a recurring theme at Current. Sessions like “Flink Jobs as Agents” (Airy Inc.) explored how AI agents can interact with a live state, reacting in real time rather than relying on stale snapshots.&lt;/li&gt;
&lt;li&gt;But several engineers we spoke to flagged concerns.&lt;/li&gt;
&lt;li&gt;While Kafka and Flink provide the backbone, durable, deterministic, and observable, the idea of introducing autonomous agents into critical pipelines raised eyebrows. There’s excitement, yes, but also scepticism around operational safety, debuggability, and unintended consequences. As one engineer put it:&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;“If an LLM is making decisions in my pipeline, I want to know what it saw, why it acted, and how to stop it fast.”&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Visibility and control are not optional; they’re the line between innovation and outage. AI might be event-driven, but it’s still infrastructure. And infrastructure needs guardrails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production Resilience &amp;gt; Architectural Purity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Sessions from OpenAI, AWS, and Daimler all emphasized pragmatism. OpenAI’s Changing Engines Mid-Flight offered real lessons on handling Kafka migrations under load. Elegant designs are great, but shipping reliable systems matters most.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flink Is Now a First-Class Citizen&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Flink moved from curiosity to cornerstone. Teams from ShareChat, Wix, and Pinterest shared how they reduced latency and costs while simplifying their pipelines. However, Flink remains operationally raw; hence, Flex, our UI and API, is designed to make Flink observable and manageable in real production environments.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;noteworthy-tools&quot;&gt;Noteworthy Tools&lt;/h2&gt;
&lt;p&gt;We saw an uptick in focused tools solving specific friction points, some standouts for us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ShadowTraffic – Safe, controlled Kafka traffic simulation.&lt;/li&gt;
&lt;li&gt;RisingWave – Real-time SQL queries over Kafka streams.&lt;/li&gt;
&lt;li&gt;Gravitee – Fine-grained Kafka API access control.&lt;/li&gt;
&lt;li&gt;Imply – Sub-second dashboards on live data.&lt;/li&gt;
&lt;li&gt;Snowplow – Clean, structured enrichment pipelines for streaming events.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;where-factor-house-fits&quot;&gt;Where Factor House Fits&lt;/h2&gt;
&lt;p&gt;As complexity grows and streaming intersects with AI, teams need visibility, safety, and efficiency, not more abstraction. Our upcoming Factor Platform unifies Kpow and Flex into a single control plane for data in motion, enabling teams to manage Kafka and Flink with confidence across clusters, clouds, and regions, and providing a layer of clarity across an organizations complete streaming ecosystem.&lt;/p&gt;
&lt;p&gt;If you’d like to learn more about Factor House products &lt;a href=&quot;https://factorhouse.io/contact&quot;&gt;&lt;strong&gt;book a demo&lt;/strong&gt;&lt;/a&gt; today.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Factor House</author></item><item><title>Amazon Corretto 11 Memory Issues</title><link>https://factorhouse.io/articles/corretto-memory-issues/</link><guid isPermaLink="true">https://factorhouse.io/articles/corretto-memory-issues/</guid><description>A recent move to v2 cgroups by a number of Linux distributions (including Amazon Linux 2022 and Red Hat Enterprise Linux 9) highlights an issue in Amazon Corretto 11 where the JVM process can cause a Docker container to exit with OOMKilled errors.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This post explores memory issues that can be encountered when running a JVM process in a Corretto container.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; These changes have been backported to OpenJDK11 and will be released in Corretto-11 as part of the &lt;a href=&quot;https://github.com/corretto/corretto-11/issues/228#issuecomment-1128074567&quot;&gt;&lt;strong&gt;Q3 updates on July 19th&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A recent move to v2 cgroups by a number of Linux distributions (including Amazon Linux 2022 and Red Hat Enterprise Linux 9) highlights &lt;a href=&quot;https://github.com/corretto/corretto-11/issues/228&quot;&gt;&lt;strong&gt;an issue in Amazon Corretto 11&lt;/strong&gt;&lt;/a&gt; where the JVM process can cause a Docker container to exit with &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/&quot;&gt;&lt;strong&gt;OOMKilled errors&lt;/strong&gt;&lt;/a&gt;. This issue applies to any Corretto 11 JVM process and is not specific to Kpow.&lt;/p&gt;
&lt;p&gt;Corretto is the base image of our &lt;a href=&quot;https://github.com/factorhouse/kpow/blob/main/dockerfile/kpow/Dockerfile&quot;&gt;&lt;strong&gt;main Docker container&lt;/strong&gt;&lt;/a&gt;. We expect the impact of this issue to be limited, and for it to be resolved in Corretto shortly.&lt;/p&gt;
&lt;p&gt;This post provides an explanation of the issue and a temporary workaround to constrain the heap usage of Kpow when using our docker image.&lt;/p&gt;
&lt;h2 id=&quot;what-is-kpow&quot;&gt;What is Kpow?&lt;/h2&gt;
&lt;p&gt;Kpow provides enterprise-grade monitoring, management, and control of Apache Kafka Clusters, Schema Registries, ksqlDB, and Connect installations.&lt;/p&gt;
&lt;p&gt;Packaged as a single JAR file or Docker container, our users deploy Kpow in numerous ways - on-premises, in private or public cloud, or a mixture of both.&lt;/p&gt;
&lt;h4 id=&quot;kpow-data-inspect-ui&quot;&gt;Kpow Data Inspect UI&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;jvm-memory-management&quot;&gt;JVM Memory Management&lt;/h3&gt;
&lt;p&gt;Kpow is built in &lt;a href=&quot;https://clojure.org/&quot;&gt;&lt;strong&gt;Clojure&lt;/strong&gt;&lt;/a&gt;, a language that runs on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_virtual_machine&quot;&gt;&lt;strong&gt;JVM&lt;/strong&gt;&lt;/a&gt; and in the browser.&lt;/p&gt;
&lt;p&gt;The base deliverable for each Kpow release is a Java JAR. When using the JAR directly we &lt;a href=&quot;https://docs.kpow.io/installation/quick-start#java-commands&quot;&gt;&lt;strong&gt;start the JVM with memory constraints&lt;/strong&gt;&lt;/a&gt;, e.g.&lt;/p&gt;
&lt;p&gt;In the example above Kpow is started with an exact allocation of 2GiB heap memory. If the JVM ran out of memory we would expect to see OutOfMemoryErrors written to the Kpow application logs as the system exited. This basically never happens.&lt;/p&gt;
&lt;h3 id=&quot;docker-memory-management&quot;&gt;Docker Memory Management&lt;/h3&gt;
&lt;p&gt;Every Kpow release is &lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow-ee&quot;&gt;&lt;strong&gt;published to Docker Hub&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Common practice is to set resource limits on Docker containers as they are deployed. Support for detecting linux container resource limits was &lt;a href=&quot;https://bugs.openjdk.java.net/browse/JDK-8146115&quot;&gt;&lt;strong&gt;introduced in OpenJDK10&lt;/strong&gt;&lt;/a&gt; and later backported to OpenJDK8u191. Prior to that change the JVM assumed that CPU and memory available on the host machine was the same as that available to the container itself. Amazon Corretto 11 supports constraining Kpow’s memory usage to 80% of the total memory available to the docker container with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-XX:InitialRAMPercentage=80&lt;/li&gt;
&lt;li&gt;-XX:MaxRAMPercentage=80&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These flags are specified as overridable defaults in the Kpow Dockerfile:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; JVM_OPTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;-server -Dclojure.core.async.pool-size=$CORE_ASYNC_POOL_SIZE -XX:MaxInlineLevel=15 -Djava.awt.headless=true -XX:InitialRAMPercentage=80 -XX:MaxRAMPercentage=80&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice this means the Kpow JVM process and Docker container limits are aligned, and we rarely receive reports of memory issues.&lt;/p&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;The original implementation of container limit detection by the JVM was based on v1 cgroups, which at the time was the only variety available in Linux. Support for v2 cgroups container limit detection was &lt;a href=&quot;https://bugs.openjdk.java.net/browse/JDK-8230305&quot;&gt;&lt;strong&gt;introduced in OpenJDK15&lt;/strong&gt;&lt;/a&gt;, has recently been &lt;a href=&quot;https://mail.openjdk.java.net/pipermail/jdk-updates-dev/2022-February/012398.html&quot;&gt;&lt;strong&gt;backported to OpenJDK11u&lt;/strong&gt;&lt;/a&gt;, and will available in the Q3 11.0.16 GA release.&lt;/p&gt;
&lt;p&gt;Amazon Corretto is a downstream distribution of OpenJDK and doesn’t yet support v2 cgroups container limit detection. Instead, behaviour reverts to the same as prior to JDK10 / JDK8u191 where the resources of the host machine are taken in place of container limits.&lt;/p&gt;
&lt;p&gt;Deploying Kpow to Kubernetes where the nodes use v2 cgroups will result in the JVM assigning a maximum heap of 80% of the node itself, rather than any limit applied to the container. As Kpow runs, the JVM may decide to allocate memory greater than the container limit. At that point the Kubernetes scheduler will kill the Kpow container with an OOMKilled error.&lt;/p&gt;
&lt;p&gt;This has only become an issue as major linux distributions move to v2 cgroups by default. Amazon Corretto 11 will pick up the backported cgroups v2 implementation in time, at which point this will cease to be an issue for Kpow deployment regardless of host machine Linux distribution.&lt;/p&gt;
&lt;h3 id=&quot;a-quick-workaround&quot;&gt;A Quick Workaround&lt;/h3&gt;
&lt;p&gt;The Kpow Docker image &lt;a href=&quot;https://github.com/factorhouse/kpow/blob/main/dockerfile/kpow/Dockerfile#L17&quot;&gt;&lt;strong&gt;accepts &lt;code&gt;JVM_OPTS&lt;/code&gt; as an environment variable&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can override the default &lt;strong&gt;&lt;code&gt;JVM_OPTS&lt;/code&gt;&lt;/strong&gt; and set explicit JVM memory constraints for Kpow like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;JVM_OPTS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;server &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Dclojure.core.async.pool&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;XX&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:MaxInlineLevel&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Djava.awt.headless&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Xms1638M &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Xmx1638M &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ENVIRONMENT_NAME&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Trade &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Book&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (Staging) &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;BOOTSTRAP&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;19092&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;19093&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;19094&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;SECURITY_PROTOCOL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;SASL_PLAINTEXT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This has the downside of requiring you to configure both container limits and an environment variable with an explicit Xmx/Xms lesser than those limits, but is a practical solution to OOMKilled errors while we await the backporting of v2 cgroup detection to Amazon Corretto 11.&lt;/p&gt;
&lt;h3 id=&quot;long-term-resolution&quot;&gt;Long-Term Resolution&lt;/h3&gt;
&lt;p&gt;Support for v2 cgroup limit detection will be merged into Amazon Corretto 11 in the Q3 release on July 19th.&lt;/p&gt;
&lt;p&gt;This issue prompted us to prioritize adoption of Amazon Corretto 17 as the base of our Docker image. OpenJDK17 supports v2 cgroups already, and we hope to avoid similar issues resolved in later version of the JDK awaiting backporting to earlier versions. JDK17 is the current LTS release of the JVM and as such we will provide a tagged release of that base shortly and move to it as the base image once we have tested thoroughly.&lt;/p&gt;
&lt;h3 id=&quot;issue-confirmation&quot;&gt;Issue Confirmation&lt;/h3&gt;
&lt;p&gt;We can inspect behaviour of the JVM process within Amazon Corretto and MaxRAMPercentage application like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;docker run &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;m 1&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;GB&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; amazoncorretto&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;11.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; java \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;XX&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:MaxRAMPercentage&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;XshowSettings&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:vm \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; VM&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; settings&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;     Max. Heap &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Size&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (Estimated): 4.64&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;G&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;     Using &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;VM&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: OpenJDK &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Bit Server &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;VM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; openjdk version &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;11.0.15&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 2022&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;04&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;19&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; LTS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; OpenJDK Runtime Environment Corretto&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;11.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15.9&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (build &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;11.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; OpenJDK &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Bit Server &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;VM&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Corretto&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;11.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15.9&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (build &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;11.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;15&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, mixed mode)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using Amazon Corretto 11 with a 1GB memory constraint on the Docker image and a MaxRAMPercentage of 80% we find the JVM has a 4.64GB max heap size.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;docker run &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;m 1&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;GB&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; amazoncorretto&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;17.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; java \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;XX&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:MaxRAMPercentage&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;XshowSettings&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:vm \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; VM&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; settings&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;     Max. Heap &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;Size&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (Estimated): 792.69&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;M&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;     Using &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;VM&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: OpenJDK &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Bit Server &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;VM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; openjdk version &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;17.0.3&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 2022&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;04&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;19&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; LTS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; OpenJDK Runtime Environment Corretto&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;17.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3.6&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (build &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;17.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt; OpenJDK &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Bit Server &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;VM&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Corretto&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;17.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3.6&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; (build &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;17.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, mixed mode, sharing)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating the image in use to Amazon Corretto 17 demonstrates correct detection of the container resource limits, with a max heap size of ~800MB.&lt;/p&gt;
&lt;p&gt;If enterprise-grade Apache Kafka tooling with a focus on performance and reliability interests you, sign up for a &lt;a href=&quot;https://factorhouse.io/kpow/get-started&quot;&gt;&lt;strong&gt;free 30-day trial today&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Derek Troy-West</author></item><item><title>Introducing Factor House 2.0 🚀</title><link>https://factorhouse.io/articles/factor-house-flow/</link><guid isPermaLink="true">https://factorhouse.io/articles/factor-house-flow/</guid><description>Today we introduce Flex for Apache Flink, and announce the Factor Platform, the future of distributed systems engineering.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Today we introduce&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex for Apache Flink&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;and announce the&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;, the future of distributed systems engineering.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Starting now, individuals can use Flex CE for free, even at work. Organisations can install Flex CE in up to three non-production environments.&lt;/p&gt;
&lt;p&gt;Each installation of Flex CE can manage one Flink Cluster. See our &lt;a href=&quot;https://factorhouse.io/flex/features&quot;&gt;&lt;strong&gt;feature matrix&lt;/strong&gt;&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visiting&lt;/strong&gt; &lt;a href=&quot;https://www.confluent.io/events/current/&quot;&gt;&lt;strong&gt;Current ’23?&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;Stop past the Factor House booth for a full demo and a chat through&lt;/strong&gt; &lt;a href=&quot;https://factorhouse.io/roadmap&quot;&gt;&lt;strong&gt;our product roadmap&lt;/strong&gt;&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;flex-for-apache-flink-is-now-ga-&quot;&gt;Flex for Apache Flink is now GA 🚀&lt;/h2&gt;
&lt;p&gt;With the release of 92.1, we are excited to introduce the general availability of &lt;strong&gt;Flex for Apache Flink&lt;/strong&gt;. &lt;a href=&quot;https://factorhouse.io/flex/get-started&quot;&gt;&lt;strong&gt;Commercial and community&lt;/strong&gt;&lt;/a&gt; editions available today for developers to enjoy.&lt;/p&gt;
&lt;p&gt;Flex brings the power of Factor House’s core technology to the Flink tooling space, with the web application sharing Kpow’s sophisticated, intuitive UX, considered data management features, and advanced security capabilities including User Authentication, RBAC, Multi-Tenancy, Enterprise Integrations, and Audit Log alongside all the Flink job and task management and tracking capabilities you’d expect.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f783d9cfc5b46a0af9c056_flink-ui.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;This enterprise-grade solution unlocks deep insights and provides unparalleled management capabilities across all environments, from local development to production, with users benefiting from cluster-level insights and individual job details, including comprehensive metrics, aggregated consumption, and production data.&lt;/p&gt;
&lt;p&gt;A single Flex Standard or Enterprise Edition instance can manage up to 12 Flink clusters, with organizations able to configure Flex with RBAC and multi-tenancy to provide fine-grained control and access for their users.&lt;/p&gt;
&lt;p&gt;The free community edition of Flex offers a suite of engineer-focused features designed to accelerate the Flink development process. Individual developers can use Flex to manage one Flink cluster, and Organizations can use Flex Community in up to three non-production environments.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f783d9cfc5b46a0af9c050_flink-task-ui.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our mission is to delight and empower distributed systems engineers, and in talking with our community of expert users, it became apparent that the need for enterprise-grade Flink tooling was the next step.&lt;/p&gt;
&lt;p&gt;Flex Community Edition only takes 5 minutes to sign up for, download, and install, so get on board and tell us what you think! Toot toot! 🚂&lt;/p&gt;
&lt;h2 id=&quot;the-factor-platform-the-future-of-distributed-systems-engineering&quot;&gt;The Factor Platform: the Future of Distributed Systems Engineering&lt;/h2&gt;
&lt;p&gt;We are excited to announce the development of the Factor Platform, an integrated, multi-tech platform combining Kpow and Flex with full, secure API access providing complete control of distributed data systems. With Factor Platform organizations can manage, monitor, secure, and explore Apache Kafka and Apache Flink from a single Web-UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Unlock automation with a &lt;strong&gt;secure REST API for GitOPS control of the Factor Platform and connected Kafka and Flink resources&lt;/strong&gt; - driving efficiency gains in enterprise engineering by providing unrivaled integration with distributed systems.&lt;/p&gt;
&lt;p&gt;The plaform combines the market-leading features of Kpow and Flex, including the ability to deploy air-gapped and secure with production-tested integrations for Okta, OAuth, LDAP, SAML, RBAC, Multi-Tenancy, and audit capabilities for data governance.&lt;/p&gt;
&lt;p&gt;The platform has a unique composable architecture. Our ultimate goal for the platform is to cover the entire surface area of distributed systems at both a UI and API level in a single consolidated UI for organisations who have several hundred instances of each resource to manage.&lt;/p&gt;
&lt;h2 id=&quot;factor-house-20&quot;&gt;Factor House 2.0&lt;/h2&gt;
&lt;p&gt;Now that we’re a three-trick pony, a few things are changing, and a few aren’t.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt; is still &lt;strong&gt;our flagship solution&lt;/strong&gt; - and we’ll ensure it remains the market-leading Kafka UI (and soon-to-be API) available.&lt;/p&gt;
&lt;p&gt;We’ll also approach the ongoing improvement and development of Flex and Factor Platform with the same speed and zest as we have with Kpow. We’ve already started on our SQL gateway integration for Flex and can’t wait for the user feedback and feature requests to begin flowing in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kpow.io/&quot;&gt;&lt;strong&gt;Kpow.io&lt;/strong&gt;&lt;/a&gt; is being brought into the mothership and will live at &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;factorhouse.io/kpow&lt;/strong&gt;&lt;/a&gt; alongside &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt;, with the Factor House website getting a well-deserved makeover!&lt;/p&gt;
&lt;h3 id=&quot;automation-automation-automation&quot;&gt;Automation Automation Automation&lt;/h3&gt;
&lt;p&gt;Factor House products share a mature, production-tested core IP and as such some features of the Factor Platform platform will naturally flow into our tooling products.&lt;/p&gt;
&lt;p&gt;Building The Factor Platform allows us to venture into the exciting space between distributed systems, adding value where the engineer lives. The secure API provided by Factor Platform is an important progression product capability, allowing engineers to automate not only the control of Apache Flink and Apache Kafka resources but also to control the platform itself, modifing RBAC rules, adding and removing resources, and more.&lt;/p&gt;
&lt;p&gt;We are pleased to confirm that Kpow and Flex will receive a capability boost in Quarter 1 / 2024 when the &lt;strong&gt;resource management API&lt;/strong&gt; is integrated in those products respectively.&lt;/p&gt;
&lt;h3 id=&quot;factor-house-product-roadmap-and-changelogs&quot;&gt;Factor House Product Roadmap and Changelogs&lt;/h3&gt;
&lt;p&gt;As part of the evolution and expansion of the Factor House toolset, we’ve published a complete &lt;a href=&quot;https://factorhouse.io/roadmap&quot;&gt;&lt;strong&gt;product roadmap outlining the path ahead&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Similarly each product now has a details changelog, so you can reference previous iterations at a glance and measure our cadence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kpow for Apache Kafka &lt;a href=&quot;https://factorhouse.io/kpow/changelog&quot;&gt;&lt;strong&gt;Changelog&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Flex for Apache Flink &lt;a href=&quot;https://factorhouse.io/flex/changelog&quot;&gt;&lt;strong&gt;Changelog&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h3&gt;
&lt;p&gt;Factor House specializes in developing market-leading tools for distributed systems. We believe that Kafka and Flink are transformative technologies, and every engineer should have access to tooling that makes working with distributed systems a joy.&lt;/p&gt;
&lt;p&gt;Our solutions are built on the foundation of our extensive experience delivering data platforms and leverage our learnings from a decade of building systems with Apache Kafka, Apache Flink, Apache Cassandra, and Apache Storm.&lt;/p&gt;
&lt;p&gt;As distributed systems engineers, we developed the tools we needed, which we consider the most powerful and user-friendly solutions in the market.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/about&quot;&gt;&lt;strong&gt;Read more&lt;/strong&gt;&lt;/a&gt; about our team and experience.&lt;/p&gt;
&lt;p&gt;We hope you find Flex Community Edition useful. If you encounter any problems or techincal questions just raise an issue on the &lt;a href=&quot;https://github.com/factorhouse/flex/issues&quot;&gt;&lt;strong&gt;Flex Github repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Interested in a commercial license? Reach out to &lt;a href=&quot;mailto:sales@factorhouse.io&quot;&gt;&lt;strong&gt;&lt;a href=&quot;mailto:sales@factorhouse.io&quot;&gt;sales@factorhouse.io&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt; any time to discuss requirements and start a POC.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kylie Troy-West is a Co-Founder and COO of Factor House.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/about&quot;&gt;&lt;strong&gt;Factor House&lt;/strong&gt;&lt;/a&gt; build essential tools for modern engineers.&lt;/p&gt;
</content:encoded><category>Company</category><author>Factor House</author></item><item><title>Factor House Product VPAT</title><link>https://factorhouse.io/articles/factor-house-product-vpat/</link><guid isPermaLink="true">https://factorhouse.io/articles/factor-house-product-vpat/</guid><description>We first published a VPAT in the release notes of Kpow for Apache Kafka v92.4, with a VPAT available to download in every release of Kpow since. Today, we are pleased to announce that we are extending that commitment to all future Factor House product releases - including Flex for Apache Flink and the Factor Platform.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;our-commitment-to-product-accessibility&quot;&gt;Our Commitment to Product Accessibility&lt;/h2&gt;
&lt;p&gt;Our mission at Factor House is to empower engineers with developer tools that are not only powerful and efficient but also inclusive and accessible.&lt;/p&gt;
&lt;p&gt;We first published a VPAT in the release notes of &lt;a href=&quot;https://factorhouse.io/blog/releases/92-4/#vpat&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka v92.4&lt;/strong&gt;&lt;/a&gt; and you can find a VPAT available to download in &lt;a href=&quot;https://factorhouse.io/blog/releases/93-4/#vpat&quot;&gt;&lt;strong&gt;every release of Kpow since&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today, we are pleased to announce that we are extending that commitment to all future releases of &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex for Apache Flink&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt; (currently in beta).&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-vpat-and-why-does-it-matter&quot;&gt;What is a VPAT and Why Does It Matter?&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Voluntary_Product_Accessibility_Template&quot;&gt;&lt;strong&gt;Voluntary Product Accessibility Template (VPAT)&lt;/strong&gt;&lt;/a&gt; is a comprehensive document that evaluates how accessible a product is according to established accessibility standards.&lt;/p&gt;
&lt;p&gt;For organisations evaluating products built by Factor House, a VPAT provides crucial information on whether a product can be used by people with accessibility needs and what we need to change so that it is.&lt;/p&gt;
&lt;p&gt;Publishing a VPAT for each of our products demonstrates our commitment to ensuring that our tools can be effectively used by all developers. It also solidifies our position as a leader in developer tools, offering our customers the confidence that &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex&lt;/strong&gt;&lt;/a&gt;, and &lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt; meet the highest standards of accessibility.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f785753cd0e57115f61f33_kpow-accessibility.png&quot; alt=&quot;Kpow For Apache Kafka Topic UI Accessibility Report&quot;&gt;&lt;/p&gt;
&lt;p&gt;Some concrete improvements to both product and process at Factor House through our commitment to web accessibility:&lt;/p&gt;
&lt;h4 id=&quot;screen-reader-compatibility&quot;&gt;Screen Reader Compatibility&lt;/h4&gt;
&lt;p&gt;Factor House products are fully compatible with screen readers, allowing users with visual impairments to navigate and interact with our software effectively.&lt;/p&gt;
&lt;h4 id=&quot;keyboard-shortcuts&quot;&gt;Keyboard Shortcuts&lt;/h4&gt;
&lt;p&gt;We implement &lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/&quot;&gt;&lt;strong&gt;ARIA Authoring Practices Guide (APG)&lt;/strong&gt;&lt;/a&gt; patterns to deliver accessible elements. All Factor House UI widgets (such as buttons, menu buttons and dropdowns etc) implement the ARIA spec for keyboard interactions, roles, states and properties.&lt;/p&gt;
&lt;h4 id=&quot;wcag-21-aa-compliance&quot;&gt;WCAG 2.1 AA Compliance&lt;/h4&gt;
&lt;p&gt;We’ve aligned our products with the &lt;a href=&quot;https://www.w3.org/TR/WCAG21/&quot;&gt;&lt;strong&gt;Web Content Accessibility Guidelines (WCAG)&lt;/strong&gt;&lt;/a&gt; 2.1 AA standards, ensuring that we meet the highest industry benchmarks for accessibility.&lt;/p&gt;
&lt;h4 id=&quot;external-audits&quot;&gt;External Audits&lt;/h4&gt;
&lt;p&gt;Our commitment to accessibility was validated through an external audit conducted by AccessibilityOZ, a leading expert in accessibility assessments. Their thorough evaluation helped us identify areas for improvement and guided our efforts to enhance Kpow’s accessibility features.&lt;/p&gt;
&lt;h4 id=&quot;published-vpat&quot;&gt;Published VPAT&lt;/h4&gt;
&lt;p&gt;We publish our compliance to accessibility guidelines in a publicly available VPAT available in every product release. This document provides a detailed analysis of how our products meet various accessibility standards, giving our customers transparency and confidence in our product’s accessibility capabilities.&lt;/p&gt;
&lt;h2 id=&quot;the-journey-to-vpat-certification&quot;&gt;The Journey to VPAT Certification&lt;/h2&gt;
&lt;p&gt;The process of publishing a VPAT was rigorous and required significant enhancements to our products. There were four major steps that we had to undertake:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our team had to become experts in WAI-ARIA standards to get each product to the point where we could submit to a VPAT audit.&lt;/li&gt;
&lt;li&gt;We had to retrofit a large, complex, production-grade Web UI to one that achieved compliance with WCAG accessibility guidelines. It was hard, it was worth it, and if we are honest with ourselves it should have been something we had included from the start.&lt;/li&gt;
&lt;li&gt;We adopted frontend libraries built with accessibility in mind, such as HeadlessUI and Echarts. Having such core components of our product ship with great accessibility standards built into their libraries certainly helps our team, and is a testament to the broader developer community.&lt;/li&gt;
&lt;li&gt;We engaged an external consultant and auditor, AccessibilityOZ, who iterated with us to create the best, most accessible, outcome.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-comes-next&quot;&gt;What Comes Next?&lt;/h2&gt;
&lt;p&gt;A commitment to publish a VPAT for each product release is a significant milestone, but it is by no means the end of the journey.&lt;/p&gt;
&lt;p&gt;Accessibility is a constantly evolving field, and we are committed to staying at the forefront of best practices and emerging standards. We will continue to refine and improve our products, ensuring that they remain accessible to all users.&lt;/p&gt;
&lt;p&gt;At Factor House, empowerment is a core value that drives our company and product development. The successful completion of our VPAT certification is a testament to our dedication to this value, and we are determined to continue in our role as a leader in developer tooling.&lt;/p&gt;
&lt;p&gt;We would like to extend our sincere thanks to our customers, partners, and team members who have supported us on this journey. Together, we are making technology more inclusive and empowering every developer to achieve their full potential with Kpow.&lt;/p&gt;
</content:encoded><category>Company</category><author>Factor House</author></item><item><title>From Bootstrap to Blackbird: The Future of Factor House</title><link>https://factorhouse.io/articles/from-bootstrap-to-blackbird/</link><guid isPermaLink="true">https://factorhouse.io/articles/from-bootstrap-to-blackbird/</guid><description>We are thrilled to announce that Factor House has closed a $5M seed round to accelerate the commercial release of our new product, the Factor Platform. Led by Blackbird Ventures, with OIF Ventures, Flying Fox Ventures, and LaunchVic’s Alice Anderson Fund as partners, this round brings our five-year bootstrapping journey to a happy conclusion and points to a bright future ahead!</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;announcing-our-5m-seed-round-led-by-blackbird&quot;&gt;Announcing our $5M Seed Round, led by &lt;a href=&quot;https://www.blackbird.vc/&quot;&gt;&lt;strong&gt;Blackbird&lt;/strong&gt;&lt;/a&gt;.&lt;/h1&gt;
&lt;p&gt;We are thrilled to announce that &lt;a href=&quot;https://www.afr.com/street-talk/couple-s-start-up-clinches-25m-valuation-with-blackbird-backing-20250212-p5lbi4&quot;&gt;&lt;strong&gt;Factor House has closed a $5M seed round&lt;/strong&gt;&lt;/a&gt; to accelerate the commercial release of our new product, the &lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Led by &lt;a href=&quot;https://www.blackbird.vc/&quot;&gt;&lt;strong&gt;Blackbird Ventures&lt;/strong&gt;&lt;/a&gt;, with &lt;a href=&quot;https://www.oifventures.com.au/&quot;&gt;&lt;strong&gt;OIF Ventures&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.flyingfox.vc/&quot;&gt;&lt;strong&gt;Flying Fox Ventures&lt;/strong&gt;&lt;/a&gt;, LaunchVic’s &lt;a href=&quot;https://launchvic.org/investment/the-alice-anderson-fund/&quot;&gt;&lt;strong&gt;Alice Anderson Fund&lt;/strong&gt;&lt;/a&gt;, and Steve and Michelle Holmes as investment partners, this round brings our five-year journey as a bootstrapped startup to a happy conclusion and points to a bright future ahead!&lt;/p&gt;
&lt;h2 id=&quot;pull-yourself-up-by-your-bootstraps&quot;&gt;Pull yourself up by your bootstraps&lt;/h2&gt;
&lt;p&gt;We founded Factor House with a simple yet ambitious goal: &lt;strong&gt;to empower engineers with the tools they need to build real-time systems with confidence&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Many startups chase funding to find product-market fit, we took a different path - bootstrapping, building, listening to engineers, iterating, and delivering products that have our users at heart. That approach allowed us to grow organically and cement our place as a trusted name in real-time data management.&lt;/p&gt;
&lt;p&gt;We have been fortunate through five years of bootstrapping to pass fellow travellers who could share support, advice, or a shoulder to cry on. It was Ben Slater, &lt;a href=&quot;https://www.instaclustr.com/&quot;&gt;&lt;strong&gt;Instaclustr’s&lt;/strong&gt;&lt;/a&gt; then Chief Product Officer, who told us how hard it is to find your first customers, and then pointed us in the right direction. Just as importantly, the engineering teams at &lt;a href=&quot;https://block.xyz/&quot;&gt;&lt;strong&gt;Block&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.airwallex.com/&quot;&gt;&lt;strong&gt;Airwallex&lt;/strong&gt;&lt;/a&gt;, and &lt;a href=&quot;https://pepperstone.com/&quot;&gt;&lt;strong&gt;Pepperstone&lt;/strong&gt;&lt;/a&gt; pushed us to refine early versions of Kpow, ensuring it met the needs of world-class teams operating at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So why change tactic and close a funding round?&lt;/strong&gt; Learnings from our users show that the opportunity in front of us is huge, and we’re determined to build a balanced business that can not only ship great products, but speak clearly and authoritatively about the future of real-time engineering.&lt;/p&gt;
&lt;h2 id=&quot;real-time-data-is-business-critical&quot;&gt;Real-time data is business critical&lt;/h2&gt;
&lt;p&gt;From FinTech and eCommerce to logistics and cybersecurity, industries everywhere are waking up to the reality that real-time data isn’t a luxury - it’s a necessity. Customers expect instant transactions, predictive analytics, and seamless digital experiences. Businesses that fail to embrace real-time processing will inevitably fall behind those that move faster and make smarter decisions.&lt;/p&gt;
&lt;p&gt;Factor House has been at the forefront of this shift, providing engineers with intuitive tools that make real-time data management effortless. &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow&lt;/strong&gt;&lt;/a&gt;, our flagship toolkit for Apache Kafka, has become an essential part of the stack for enterprises managing complex data flows. But as demand grows, so does the need to innovate.&lt;/p&gt;
&lt;h2 id=&quot;whats-next-for-factor-house&quot;&gt;What’s Next for Factor House?&lt;/h2&gt;
&lt;p&gt;With this investment, we’re focused on three key areas:&lt;/p&gt;
&lt;h3 id=&quot;expanding-product-capabilities&quot;&gt;Expanding Product Capabilities&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;We will invest in Kpow and Flex, our flagship products.&lt;/strong&gt; Engineers always need more; they need more profound insights, intelligent automation, and ever more fine-grained control of underlying systems. We’re investing in our existing products to continue to bring clarity and confidence to engineers working with real-time data.&lt;/p&gt;
&lt;h3 id=&quot;growing-our-global-reach&quot;&gt;Growing our Global Reach&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Our products are used by engineers in over a hundred countries&lt;/strong&gt;. Growing our team and expanding our ability to communicate as well as ship products will ensure more companies can access enterprise-ready solutions that scale with their needs.&lt;/p&gt;
&lt;h3 id=&quot;building-the-future-with-factor-platform&quot;&gt;Building the Future with Factor Platform&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/platform&quot;&gt;&lt;strong&gt;Factor Platform&lt;/strong&gt;&lt;/a&gt; combines features of each of our tools with extended functionality to provide clarity, control, and governance of real-time data at enterprise scale.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f787ea880d926f0e8ca19b_factor-platform-hero.png&quot; alt=&quot;Factor Platform for Streaming Data&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our composable system architecture provides &lt;strong&gt;centralized management of every Kafka and Flink cluster in an organization from a single Web UI&lt;/strong&gt;, and allows service integration via a secure OpenAPI 3.1 REST API.&lt;/p&gt;
&lt;p&gt;We can’t wait to open early access to existing customers and start iterating on their feedback.&lt;/p&gt;
&lt;h3 id=&quot;a-future-without-fud-where-engineers-lead-the-way&quot;&gt;A Future Without FUD, Where Engineers Lead the Way&lt;/h3&gt;
&lt;p&gt;Factor House has always been, and will always be, built for engineers first. For our existing customers, &lt;a href=&quot;https://factorhouse.io/blog/articles/our-commitment-to-engineers/&quot;&gt;&lt;strong&gt;nothing changes (read more about our commitment to engineers)&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While many enterprise software companies focus on selling to executives or satisfying aggressive growth targets at the expense of their customers, we stay true to the practitioners - those working directly with data daily - ensuring they have the &lt;a href=&quot;https://factorhouse.io/&quot;&gt;&lt;strong&gt;best tools available&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As real-time data becomes the foundation of modern business, the need for intuitive, scalable, high-performance tooling will only grow. Factor House isn’t just responding to that trend; &lt;strong&gt;we’re helping shape the future of how real-time data is managed, understood, and leveraged.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The journey from bootstrap to industry leader has been a remarkable one, and lots of fun, but the most exciting chapters are still ahead.&lt;/p&gt;
&lt;p&gt;Stay tuned; &lt;strong&gt;the best is yet to come!&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>Company</category><author>Derek Troy-West</author></item><item><title>Introducing Kpow&apos;s new API</title><link>https://factorhouse.io/articles/introducing-kpows-new-api/</link><guid isPermaLink="true">https://factorhouse.io/articles/introducing-kpows-new-api/</guid><description>With our new API, you can now leverage Kpow&apos;s capabilities directly from your own tools and platforms, opening up a whole new range of possibilities for integrating Kpow into your existing workflows. Whether you&apos;re managing topics, consumer groups, or monitoring Kafka clusters, our API provides a seamless experience that mirrors the functionality of our user interface.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Factor House, we’re dedicated to providing the best possible developer tooling for data-in-motion. Our products continue to evolve to meet the ever-growing demands of our customers, who rely on Kpow as the central repository for their organization’s Kafka clusters and associated resources. For many teams, Kpow has become an integral part of their day-to-day Kafka workflow, offering a comprehensive suite of features to simplify and streamline Kafka management.&lt;/p&gt;
&lt;p&gt;That’s why we’re thrilled to announce the release of Kpow’s new API, available in version 93.1 onwards! This new API opens up a world of possibilities, allowing you to seamlessly integrate Kpow’s capabilities into your own organization’s tools, platforms, and operations.&lt;/p&gt;
&lt;p&gt;In this blog post, we’ll take a closer look at what our new API has to offer, how it can benefit your organization, and how you can get started with it. Let’s dive in!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f784e1ffae4a1bd15221b5_api-data.png&quot; alt=&quot;Kpow API data example&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;api-overview&quot;&gt;API overview&lt;/h2&gt;
&lt;p&gt;We’re excited to introduce the latest addition to the Kpow family: our new API, now available in version 93.1 onwards. This API represents a significant milestone in our commitment to providing powerful, flexible, and easy-to-use tools for Kafka.&lt;/p&gt;
&lt;p&gt;With our new API, you can now leverage Kpow’s capabilities directly from your own tools and platforms, opening up a whole new range of possibilities for integrating Kpow into your existing workflows. Whether you’re managing topics, consumer groups, or monitoring Kafka clusters, our API provides a seamless experience that mirrors the functionality of our user interface.&lt;/p&gt;
&lt;p&gt;Our API is backed by all of Kpow’s enterprise features, including role-based access control, multi-tenancy, and the audit log for data governance. This means you can securely manage access to your Kafka resources and isolate workloads as needed, all through a centralized interface.&lt;/p&gt;
&lt;p&gt;In the sections below, we’ll explore the different modules of Kpow’s API, highlighting key features and benefits of each. Let’s dive in and see how Kpow’s API can empower your Kafka management!&lt;/p&gt;
&lt;h3 id=&quot;kafka-api&quot;&gt;Kafka API&lt;/h3&gt;
&lt;p&gt;Kpow’s Kafka API allows you to perform a wide range of operations directly from your own tools and platforms. Here are some common Kafka operations you can perform with the API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Managing Topics:&lt;/strong&gt; Create, delete, and modify Kafka topics, including configuring topic properties such as replication factor and partition count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing Consumer Groups:&lt;/strong&gt; Create, delete, and manage consumer groups, including resetting consumer group offsets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring Kafka Clusters:&lt;/strong&gt; Retrieve metrics and monitoring data for Kafka clusters, brokers, topics, and partitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing ACLs (Access Control Lists):&lt;/strong&gt; Configure access control for Kafka resources, including topics and consumer groups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing Quotas:&lt;/strong&gt; Set and manage quotas for producer and consumer traffic, controlling the rate at which clients can produce or consume messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing Transactional Producers:&lt;/strong&gt; Implement transactional operations such as fencing and aborting transactions, ensuring data integrity in transactional systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kpow’s Kafka API supports the full surface of the AdminClient API, allowing you to leverage all the capabilities of Kafka within your own workflows.&lt;/p&gt;
&lt;p&gt;Kpow’s Kafka API is vendor-specific, meaning it supports any technology that speaks the Kafka protocol. Whether you’re using RedPanda, MSK serverless, Confluent Cloud, or any other Kafka-compatible technology, you can integrate it seamlessly with Kpow.&lt;/p&gt;
&lt;p&gt;For more details on the Kafka API, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;schema-registry-api&quot;&gt;Schema Registry API&lt;/h3&gt;
&lt;p&gt;Kpow’s Schema Registry API supports both AWS Glue and Confluent’s schema registry. With this API you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create, edit and delete schemas&lt;/li&gt;
&lt;li&gt;Update schema compatibility&lt;/li&gt;
&lt;li&gt;Permanently delete soft-deleted schemas (saving money on your Confluent bill!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details on the Schema Registry API, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kafka-connect-api&quot;&gt;Kafka Connect API&lt;/h3&gt;
&lt;p&gt;Right now the Kafka Connect API endpoints only support Apache Kafka Connect and Confluent Cloud/Platform. We will be looking to support MSK Connect very shortly! With this API you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create, edit and delete connectors&lt;/li&gt;
&lt;li&gt;Restart/pause/stop connectors + tasks&lt;/li&gt;
&lt;li&gt;View connector task details like stacktraces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details on the Kafka Connect API, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kpow-user-management-api&quot;&gt;Kpow user management API&lt;/h3&gt;
&lt;p&gt;Kpow’s user management API allows customers to manage user actions through an API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View user details (such as assigned roles)&lt;/li&gt;
&lt;li&gt;View and manage any temporary policies assigned to your user&lt;/li&gt;
&lt;li&gt;View and manage any scheduled mutations invoked by your user&lt;/li&gt;
&lt;li&gt;List all mutations performed by your user&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;benefits-for-users&quot;&gt;Benefits for users&lt;/h2&gt;
&lt;p&gt;Kpow’s API offers a centralized interface for managing all your Kafka clusters and related resources, regardless of the vendor or technology you’re using. Whether you’re operating in a multi-cloud environment, using multiple Kafka technologies, or managing on-premise clusters, Kpow provides a unified platform for managing all your Kafka infrastructure.&lt;/p&gt;
&lt;p&gt;One of the key benefits of Kpow’s API is its ability to simplify management and reduce complexity. By providing a single point of control for all your Kafka infrastructure, Kpow streamlines your Kafka workflows and makes it easier to manage your resources efficiently.&lt;/p&gt;
&lt;p&gt;Additionally, Kpow’s API is backed by robust role-based access control (RBAC) and multi-tenancy features. This ensures that you can securely manage access to your Kafka resources and isolate workloads as needed, providing the flexibility and security required for modern data management.&lt;/p&gt;
&lt;p&gt;Already, many of our customers are leveraging Kpow’s API and integrating it into their GitOps pipelines. This streamlines their operations and enhances their Kafka workflows, demonstrating the real-world benefits of using Kpow’s API.&lt;/p&gt;
&lt;p&gt;Getting started&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;API_ENABLED&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;API_PORT&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;3001&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Getting started with Kpow’s API is quick and easy. Follow these simple steps to enable and start using the API in your environment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Enable the API&lt;/strong&gt;: Add the following configuration to your Kpow deployment:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The API server will now be served at port 3001.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Verify the API&lt;/strong&gt;: You can verify that the API is running using cURL:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;curl &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;X&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; GET&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//kpow:3001/kafka/v1/clusters&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Authentication setup&lt;/strong&gt;: Configure authentication through API tokens. Instructions on how to set up authentication can be found in Kpow’s &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API reference documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization setup:&lt;/strong&gt; Configure RBAC for authorization to manage access to your Kafka resources. This is also covered in the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API reference documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once you have completed these steps, you will be ready to start using Kpow’s API to enhance your Kafka workflows. For more details and advanced usage, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API reference documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;
&lt;p&gt;At Factor House, we’re committed to continually enhancing our API to provide you with the best possible Kafka management experience. In the coming months, we will be investing heavily in expanding the capabilities of Kpow’s API, with a focus on adding more modules and expanding the surface area.&lt;/p&gt;
&lt;p&gt;Some of the major API enhancements we have planned include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Kpow Data API&lt;/strong&gt;: We are working on a data API that will allow querying and producing to Kafka topics directly through the API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ksqlDB API&lt;/strong&gt;: We plan to add support for the ksqlDB API, allowing you to manage your ksqlDB resources directly from the Kpow API.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stay tuned for more updates on our API development, including guides on integrating the API with GitHub Actions for GitOps and walkthroughs on setting up clients for Kpow’s API in Java and other languages. If you have any specific use cases or features you would like to see covered in future updates, please reach out and let us know!&lt;/p&gt;
&lt;p&gt;You can learn more about the Kpow API by reading our official &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;give-us-your-feedback&quot;&gt;Give us your feedback!&lt;/h2&gt;
&lt;p&gt;Thank you for joining us on this journey, and we look forward to seeing how you leverage Kpow’s API to unlock new possibilities in your Kafka infrastructure.&lt;/p&gt;
&lt;p&gt;Ready to experience the power of Kpow’s API for yourself? Visit our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/api-reference/&quot;&gt;&lt;strong&gt;API reference documentation&lt;/strong&gt;&lt;/a&gt; to learn more about the capabilities of our API and how you can start integrating it into your workflows.&lt;/p&gt;
&lt;p&gt;Have feedback or suggestions for future updates? We’d love to hear from you! Reach out to us and let us know how you’re using Kpow’s API to revolutionize your Kafka management.&lt;/p&gt;
&lt;p&gt;Don’t miss out on the latest updates and features! Subscribe to our newsletter to stay up-to-date with all the latest news and developments from Kpow.&lt;/p&gt;
&lt;p&gt;Unlock the power of Kpow’s API and take your Kafka management to the next level. &lt;a href=&quot;https://factorhouse.io/kpow/get-started/&quot;&gt;&lt;strong&gt;Get started today!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Join the conversation: Factor House launches open Slack for the real-time data community</title><link>https://factorhouse.io/articles/join-the-conversation-community-slack/</link><guid isPermaLink="true">https://factorhouse.io/articles/join-the-conversation-community-slack/</guid><description>Factor House has opened a public Slack for anyone working with streaming data, from seasoned engineers to newcomers exploring real-time systems. This space offers faster peer-to-peer support, open discussion across the ecosystem, and a friendly on-ramp for those just getting started.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;come-and-say-hi&quot;&gt;Come and #say-hi&lt;/h2&gt;
&lt;p&gt;We’ve opened the Factor House Community Slack: a public space for anyone working in the streaming data world. Whether you’re a seasoned data engineer, exploring streaming technologies for the first time, or just curious about real-time systems, this is your place to connect, learn, and share.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;&lt;strong&gt;Join the Factor House Community Slack →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Our community isn’t just a chatroom, it’s part of building the collective intelligence that will shape the future of data in motion. We’re connecting the engineers, operators, and curious learners who will define what streaming infrastructure looks like in the AI era.&lt;/p&gt;
&lt;h2 id=&quot;what-changed-and-why&quot;&gt;What changed (and why)&lt;/h2&gt;
&lt;p&gt;For years, our team, customers, and broader community were all in one busy workspace. It was a good start, but as our products and community grew, so did the noise. Support conversations were mixed with team chatter and community discussions, leaving newcomers unsure of where to begin.&lt;/p&gt;
&lt;p&gt;We’ve split into two focused spaces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Factor House Community (public) - Where the magic happens&lt;/li&gt;
&lt;li&gt;Private workspace - For our team and client engagements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To our existing customers: thank you for your patience during this transition. Your feedback helps us build something better, and we’re grateful for engineers who push us to improve.&lt;/p&gt;
&lt;h2 id=&quot;why-join&quot;&gt;Why join?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Current users:&lt;/strong&gt; Get faster answers from real humans who’ve solved similar problems. Our team is active daily on weekdays (Australian timezone), and we’re building a community that helps each other.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Newcomers:&lt;/strong&gt; This is your friendly on-ramp to the data streaming world. Ask the “obvious” questions - we love helping engineers grow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ecosystem:&lt;/strong&gt; We’re vendor-neutral and open-source friendly. Discuss any tools, share knowledge, and make announcements. The more diverse perspectives, the better.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;heres-where-youll-find-us-and-each-other&quot;&gt;Here’s where you’ll find us (and each other):&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;#say-hi: introduce yourself to the community&lt;/li&gt;
&lt;li&gt;#getting-started: new to Kpow or Flex? Begin your journey here&lt;/li&gt;
&lt;li&gt;#ask-anything: all questions welcome, big or small&lt;/li&gt;
&lt;li&gt;#product-kpow &amp;amp; #product-flex: features, releases, and best practices for FH tooling&lt;/li&gt;
&lt;li&gt;#house-party: off-topic bants, memes, and pet pics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our team is in the mix too. You’ll spot us by the Factor House logo in our status.&lt;/p&gt;
&lt;h2 id=&quot;community-guidelines&quot;&gt;Community Guidelines&lt;/h2&gt;
&lt;p&gt;We want this community to reflect the best parts of engineering culture: openness, generosity, and curiosity. It’s not just about solving problems faster, it’s about building a place where people can do their best work together.&lt;/p&gt;
&lt;p&gt;This is a friendly, moderated space. We ask everyone to be respectful and inclusive (read our &lt;a href=&quot;https://factorhouse.io/community/code-of-conduct&quot;&gt;&lt;strong&gt;code of conduct&lt;/strong&gt;&lt;/a&gt;). Keep conversations in public channels wherever possible so everyone benefits.&lt;/p&gt;
&lt;p&gt;For our customers: use your dedicated support channels or &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt; for SLA-bound requests and bug reports. The community Slack is best-effort support.&lt;/p&gt;
&lt;h2 id=&quot;ready-to-join&quot;&gt;Ready to Join?&lt;/h2&gt;
&lt;p&gt;This Slack is the seed of a wider ecosystem. It’s a place where engineers share knowledge, swap stories, and push the boundaries of what’s possible with streaming data. It’s the beginning of a developer community that will grow alongside our platform.&lt;/p&gt;
&lt;p&gt;This community will become what we make of it. We’re hoping for technical discussions, mutual help, and the kind of engineering conversations that make your day better.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;&lt;strong&gt;Join the Factor House Community Slack →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Come #say-hi and tell us what you’re working on. We’re genuinely curious about what keeps data engineers busy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7cfdf9bea9207a3e47f91_fh-team-community.png&quot; alt=&quot;Factor House Team&quot;&gt;&lt;/p&gt;
</content:encoded><category>Company</category><author>Sarah Brown</author></item><item><title>Kafka 4.1 Release: Queues, Stream Groups, and More</title><link>https://factorhouse.io/articles/kafka-4-1-release-announcement/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-4-1-release-announcement/</guid><description>Apache Kafka 4.1 has landed: with queue support in preview, improved Kafka Streams coordination, and new security and metrics features, this release marks a major milestone for the future of real-time data systems.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Factor House, we’re always tracking what’s new in the Apache Kafka ecosystem, both for our own products and to support the growing community of stream-first developers we work with every day. Here’s a quick rundown of what’s exciting in the 4.1.0 release.&lt;/p&gt;
&lt;h2 id=&quot;queues-for-kafka-move-to-preview-kip-932&quot;&gt;Queues for Kafka move to preview (KIP-932)&lt;/h2&gt;
&lt;p&gt;After a long wait, Kafka’s new native queueing model is moving from Early Access to Preview. That means KIP-932, which introduces Share Consumer Groups, is stabilising. And while it’s not yet production-ready, it’s getting much closer.&lt;/p&gt;
&lt;p&gt;So what does this mean? In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It brings queue-like semantics to Kafka, enabling multiple consumers to read from the same partition in parallel.&lt;/li&gt;
&lt;li&gt;It supports out-of-order processing, with individual message acknowledgements and retries.&lt;/li&gt;
&lt;li&gt;It introduces a more flexible model for building event-driven architectures that straddle the line between pub/sub and traditional messaging queues.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a big shift for teams building scalable consumer architectures, and one we’re watching very closely.&lt;/p&gt;
&lt;h2 id=&quot;kafka-streams-gets-smarter-with-stream-groups-kip-1071&quot;&gt;Kafka Streams gets smarter with Stream Groups (KIP-1071)&lt;/h2&gt;
&lt;p&gt;Kafka Streams applications just got a coordination upgrade.&lt;/p&gt;
&lt;p&gt;KIP-1071 introduces a new rebalance protocol for Streams apps, based on KIP-848’s consumer group protocol. This update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Streamlines how stream tasks are assigned and rebalanced&lt;/li&gt;
&lt;li&gt;Makes scaling Kafka Streams applications smoother&lt;/li&gt;
&lt;li&gt;Adds transparency and predictability to the rebalance process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’ve ever wrangled a cluster of Kafka Streams apps and found yourself wondering “why did that rebalance happen?”, this one’s for you.&lt;/p&gt;
&lt;h2 id=&quot;other-noteworthy-improvements&quot;&gt;Other noteworthy improvements&lt;/h2&gt;
&lt;p&gt;A few other highlights from the release that caught our eye:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-877%3A+Mechanism+for+plugins+and+connectors+to+register+metrics&quot;&gt;&lt;strong&gt;KIP-877&lt;/strong&gt;&lt;/a&gt;: A standardised API for plugin metrics — more visibility into Kafka internals, especially custom components.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-891%3A+Running+multiple+versions+of+Connector+plugins&quot;&gt;&lt;strong&gt;KIP-891&lt;/strong&gt;&lt;/a&gt;: Kafka Connect now supports multiple plugin versions, making upgrades and rollbacks less painful.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-1050%3A+Consistent+error+handling+for+Transactions&quot;&gt;&lt;strong&gt;KIP-1050&lt;/strong&gt;&lt;/a&gt;: Improved error handling for Transactional Producers, with clear exception categories that should simplify recovery strategies.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-1139%3A+Add+support+for+OAuth+jwt-bearer+grant+type&quot;&gt;&lt;strong&gt;KIP-1139&lt;/strong&gt;&lt;/a&gt;: Adds support for JWT Bearer OAuth 2.0, making it easier to manage secure access without static secrets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All told, Kafka 4.1 includes contributions from 167 engineers across the globe. That’s a testament to the strength and growth of the open source streaming community.&lt;/p&gt;
&lt;h2 id=&quot;our-take-at-factor-house&quot;&gt;Our take at Factor House&lt;/h2&gt;
&lt;p&gt;“At Factor House, we’re already preparing to update our clients to Kafka 4.x in an upcoming product release. Features like improved plugin management and transactional error clarity are going to make life easier for developers, and we’re excited about what the queueing model means for the future of real-time stream consumption.” — Derek Troy-West, CEO, Factor House&lt;/p&gt;
&lt;p&gt;Whether you’re running Kafka locally or at scale in production, Kafka 4.1 is a milestone release that makes the platform more powerful, more flexible, and more secure.&lt;/p&gt;
&lt;p&gt;We’ll be diving deeper into some of these changes in future blog posts, particularly around how they affect real-world streaming workloads using tools like Kafka Connect, Kafka Streams, and our own product stack at Factor House.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/blog#apache_kafka_410_release_announcement&quot;&gt;&lt;strong&gt;Official Apache Kafka 4.1 Release Announcement&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://downloads.apache.org/kafka/4.1.0/RELEASE_NOTES.html&quot;&gt;&lt;strong&gt;Apache Kafka 4.1 Release Notes&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation.html&quot;&gt;&lt;strong&gt;Apache Kafka 4.1 Documentation&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Industry</category><author>Sarah Brown</author></item><item><title>Apache Kafka 3.2.0: Idempotent Producer Breaking Change</title><link>https://factorhouse.io/articles/kafka-producer-breaking-change/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-producer-breaking-change/</guid><description>Apache Kafka KIP-679 changes the behaviour of default Producer configuration to enable idempotence by default. This change can cause message production to fail after updating to the 3.2.0 kafka-client libraries.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post explores a breaking change to Apache Kafka producer behaviour, introduced in Kafka 3.2.0.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; We raised a &lt;a href=&quot;https://github.com/apache/kafka/pull/12260&quot;&gt;&lt;strong&gt;PR&lt;/strong&gt;&lt;/a&gt; to the Apache Kafka site and this information is now included in the &lt;a href=&quot;https://kafka.apache.org/32/documentation.html#upgrade_320_notable&quot;&gt;&lt;strong&gt;upgrade-notes for Kafka 3.2.0&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Apache Kafka 3.2.0 implements &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-679%3A+Producer+will+enable+the+strongest+delivery+guarantee+by+default&quot;&gt;&lt;strong&gt;KIP-679&lt;/strong&gt;&lt;/a&gt; that changes the default behaviour of Producer configuration to enable idempotence by default.&lt;/p&gt;
&lt;p&gt;This change can cause message production to fail after you update to the 3.2.0 kafka-client libraries, it briefly impacted Kpow &lt;a href=&quot;https://factorhouse.io/blog/releases/88-6/&quot;&gt;&lt;strong&gt;v88.6&lt;/strong&gt;&lt;/a&gt; (fixed in &lt;a href=&quot;https://factorhouse.io/blog/releases/88-7/&quot;&gt;&lt;strong&gt;v88.7&lt;/strong&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Originally released in Kafka 3.0.0 via &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-10619&quot;&gt;&lt;strong&gt;KAFKA-10619&lt;/strong&gt;&lt;/a&gt;, a bug in config validation meant this change was not fully implemented until &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-13598&quot;&gt;&lt;strong&gt;KAFKA-13598&lt;/strong&gt;&lt;/a&gt; in Kafka 3.2.0.&lt;/p&gt;
&lt;p&gt;It appears that issues were identified with the idea of changing default behaviour as &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-13673&quot;&gt;&lt;strong&gt;KAFKA-13673&lt;/strong&gt;&lt;/a&gt; disables default idempotency when certain configuration is set on the producer, and &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-13759&quot;&gt;&lt;strong&gt;KAFKA-13759&lt;/strong&gt;&lt;/a&gt; disables this change entirely for Kafka Connect. The cause of the issue that briefly impacted Kpow can be found in that last ticket:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;gt; for brokers older than version 2.8 the IDEMPOTENT_WRITE ACL is required to be granted to the principal&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this post we explore:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identifying this issue in Kpow&lt;/li&gt;
&lt;li&gt;Details of the breaking change&lt;/li&gt;
&lt;li&gt;Potential scope of impact&lt;/li&gt;
&lt;li&gt;Issue remediation&lt;/li&gt;
&lt;li&gt;Further implications&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-is-kpow&quot;&gt;What is Kpow?&lt;/h2&gt;
&lt;p&gt;Kpow provides enterprise-grade monitoring, management, and control of Apache Kafka Clusters, Schema Registries, and Connect installations.&lt;/p&gt;
&lt;p&gt;Uniquely for a product in use since 2018, Kpow is &lt;strong&gt;built for and from Kafka&lt;/strong&gt;. We use Kafka Streams and internal topics for system state and long-term metrics computation (e.g. topic last-write, group last-read telemetry, etc). We use Kpow to monitor and build Kpow, it’s turtles all the way down.&lt;/p&gt;
&lt;p&gt;The combination of being widely used and well integrated with Kafka means we often have user-reports of issues before they become commonly known, as can be seen from our recent blog post on &lt;a href=&quot;https://factorhouse.io/blog/articles/corretto-memory-issues/&quot;&gt;&lt;strong&gt;memory issues with Amazon Corretto 11&lt;/strong&gt;&lt;/a&gt;. See the Kpow &lt;a href=&quot;https://factorhouse.io/kpow/changelog&quot;&gt;&lt;strong&gt;Changelog&lt;/strong&gt;&lt;/a&gt; for full release notes.&lt;/p&gt;
&lt;h4 id=&quot;kpow-data-inspect-ui&quot;&gt;Kpow Data Inspect UI&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f6dac5ffc433961f69c35b_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;impact-on-kpow&quot;&gt;Impact on Kpow&lt;/h3&gt;
&lt;p&gt;Within one day of releasing Kpow v88.6 we received a user report that Kpow was showing the following error in the application logs:&lt;/p&gt;
&lt;p&gt;/&lt;/p&gt;
&lt;p&gt;Unfortunately the error log contained this red herring:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;Caused &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;by&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kpow runs with every flavour of Kafka from v1.0.0 onwards, yes there are teams in the wild still using Kafka v1.0.0.&lt;/p&gt;
&lt;p&gt;Our product monitors and manages Kafka clusters in organizations from publishing to payment networks. Where a cluster is protected by ACLs the Kpow user must have the &lt;a href=&quot;https://docs.kpow.io/installation/minimum-acl-permissions&quot;&gt;&lt;strong&gt;minimum set of ACL permissions&lt;/strong&gt;&lt;/a&gt; required for our product to work. When you don’t have the correct permissions configured, you see that error.&lt;/p&gt;
&lt;p&gt;We asked the user to check their ACL permissions, normally this quickly resolves the issue. They insist their permissions are correct. We run up a local 3-node Kafka cluster with ACLs configured (see our &lt;a href=&quot;https://github.com/factorhouse/kafka-local&quot;&gt;&lt;strong&gt;docker-compose configuration&lt;/strong&gt;&lt;/a&gt; for a local SASL authenticated 3-node Kafka cluster that allows ACL testing) and run Kpow without encountering any issue. This is a proper head scratcher. Then two things happen at roughly the same time.&lt;/p&gt;
&lt;p&gt;Firstly we noticed this part of the error log:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;org.apache.kafka.common.KafkaException: Cannot execute transactional method because we are &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; an error state&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don’t use transactional producers. We’re aware of what they are and the role they play, but we have no need for them and have not configured them.&lt;/p&gt;
&lt;p&gt;Then our tenacious user came back to us with an update:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;I think there&apos;s a bug in the 88.6 version.&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;I rolled back to the 88.5 with the same set of permissions listed in this email and I&apos;m able to see the metrics&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We closed a record 49 minor issue tickets in Kpow v88.6. The majority were either old and redundant, or tweaks to our UI/UX. There were zero changes related to message production other than this seemingly innocuous library version bump:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [org.apache.kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;streams &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;3.1.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [org.apache.kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kafka&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;streams &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;3.2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We favour keeping close to latest of major libraries like Apache Kafka since they offer great quality and reliability.&lt;/p&gt;
&lt;p&gt;That said we are fastidious about our dependency management. We read release notes and upgrade guides where available, changelogs if they exist, and if required we’ll look at the commit diff between different versions of a library to determine if it is safe to proceed. We don’t read every KIP though.&lt;/p&gt;
&lt;p&gt;In this case if there was a note on a breaking change, we missed it. Reverting the Kafka-Streams library back to 3.1.0 fixed our issue and we released Kpow v88.7.&lt;/p&gt;
&lt;h3 id=&quot;breaking-change-details&quot;&gt;Breaking Change Details&lt;/h3&gt;
&lt;p&gt;The switch to default idempotent Producers can cause production to fail where:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Kafka Cluster has brokers running version &amp;lt; 2.8.0, and&lt;/li&gt;
&lt;li&gt;The Kafka Cluster has ACLs configured, but not IDEMPOTENT_WRITE and&lt;/li&gt;
&lt;li&gt;Producer configuration is default, or is capable of being defaulted to idempotent, and&lt;/li&gt;
&lt;li&gt;The producing application is using Kafka-Clients version &amp;gt; 3.2.0&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is because in Kafka prior to v2.8.0 there was an ACL specifically for idempotent production named &lt;strong&gt;&lt;code&gt;IDEMPOTENT_WRITE&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you are not concerned with idempotency and have ACL set it is likely that &lt;strong&gt;&lt;code&gt;IDEMPOTENT_WRITE&lt;/code&gt;&lt;/strong&gt; is false, and &lt;strong&gt;&lt;code&gt;TOPIC_WRITE&lt;/code&gt;&lt;/strong&gt; is set instead.&lt;/p&gt;
&lt;p&gt;If your producers have default configuration, or are not explicitly idempotent but fall within the bounds of &lt;a href=&quot;https://issues.apache.org/jira/browse/KAFKA-13673&quot;&gt;&lt;strong&gt;KAFKA-13673&lt;/strong&gt;&lt;/a&gt;, and you update the client libraries to &amp;gt; 3.2.0, you will now have idempotent producers and they will fail to write with the ACL error that we encountered.&lt;/p&gt;
&lt;h3 id=&quot;potential-scope-of-impact&quot;&gt;Potential Scope of Impact&lt;/h3&gt;
&lt;p&gt;Perhaps Kpow is an uncommon application, being widely used against nearly every sort of Kafka cluster.&lt;/p&gt;
&lt;p&gt;However if we take another look at the conditions for the breaking change to occur:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Kafka Cluster has brokers running version &amp;lt; 2.8.0. &lt;strong&gt;This is fairly common.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The Kafka Cluster has ACLs configured but not IDEMPOTENT_WRITE. &lt;strong&gt;This is fairly common.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Producers configuration is default, or is capable of being defaulted to idempotent. &lt;strong&gt;This is fairly common.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The producing application is using Kafka-Clients version &amp;gt; 3.2.0. &lt;strong&gt;This is very easy to do.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first three circumstances are more common than you might expect, and bumping a Kafka client library in your application dependencies is really easy to do. Much easier than upgrading the version of your Kafka brokers, for instance.&lt;/p&gt;
&lt;h3 id=&quot;issue-remediation&quot;&gt;Issue Remediation&lt;/h3&gt;
&lt;p&gt;If you encounter this issue it is fairly easy to remediate, you have several options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rollback your kafka-client library to &amp;lt; 3.2.0, or&lt;/li&gt;
&lt;li&gt;Configure producer idempotency to false, or&lt;/li&gt;
&lt;li&gt;Configure &lt;strong&gt;&lt;code&gt;IDEMPOTENT_WRITE&lt;/code&gt;&lt;/strong&gt; ACLs, or&lt;/li&gt;
&lt;li&gt;Upgrade your broker version to &amp;gt; 2.8.0&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;further-considerations&quot;&gt;Further Considerations&lt;/h3&gt;
&lt;p&gt;This change to Kafka leaves us in a slightly strange state. Is your producer default idempotent or not? The answer is maybe. If you haven’t configured anything contrary to idempotency then yes, it should be idempotent. That’s a weird default position to hold that requires further knowledge from a user than I would expect.&lt;/p&gt;
&lt;p&gt;If enterprise-grade Apache Kafka tooling with a focus on performance and reliability interests you, sign up for a &lt;a href=&quot;https://factorhouse.io/kpow/get-started&quot;&gt;&lt;strong&gt;free 30-day trial today&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Industry</category><author>Derek Troy-West</author></item><item><title>Operatr.IO has a new name: Meet Factor House</title><link>https://factorhouse.io/articles/operatr-io-has-a-new-name-meet-factor-house/</link><guid isPermaLink="true">https://factorhouse.io/articles/operatr-io-has-a-new-name-meet-factor-house/</guid><description>Meet Factor House, we build Kpow for Apache Kafka</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This news has been many months in the making.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Today Derek, Tom, and I are proud to share our new look and name with the world. Factor House has arrived, and Operatr.IO is no more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f781aaad7b213e363bd53b_fh-banner.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;We are proud to be an independent, engineering-led software house. Our new name, &lt;a href=&quot;https://factorhouse.io/&quot;&gt;&lt;strong&gt;Factor House&lt;/strong&gt;&lt;/a&gt;, represents us, our software development style, and our business approach.&lt;/p&gt;
&lt;p&gt;Software houses specialize in building bespoke, high-quality software products. In software design, code refactoring is the process of restructuring existing code or changing the factoring to improve its design, implementation, and structure without impacting its functionality.&lt;/p&gt;
&lt;p&gt;Factor House reflects our commitment to our craft, passion for product development, and &lt;em&gt;pride in our independence&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Building cutting-edge, high-quality enterprise software is at the heart of what we do.&lt;/p&gt;
&lt;h2 id=&quot;hey-you-dont-you-know-who-i-am&quot;&gt;Hey you! Don’t you know who I am?&lt;/h2&gt;
&lt;p&gt;Nothing about bootstrapping a business is easy, including naming it. Truth be told, Operatr.IO was never meant to be our name. It was changed up in one of those ‘your first idea won’t work, and you need to make a decision right now’ moments that often occur in the early days of launching a startup.&lt;/p&gt;
&lt;p&gt;Because of this, our name lacked meaning and personal connection right from the start. It never complimented who we are or what we do. We’ve all worked in big organizations where this matters less, but Kpow and Factor House are deeply personal to us. Our founding team consists of Derek and I, who are married, and Tom, who is a long-time friend and collaborator.&lt;/p&gt;
&lt;p&gt;Kpow is a passion project for us, bringing together 20 years of enterprise development experience and over 10 years of specialization with Apache Kafka®. We’ve all put everything into Factor House and Kpow and have worked tirelessly to get to this point.&lt;/p&gt;
&lt;p&gt;But our lack of pride in our company brand impacted our ability to thrive.&lt;/p&gt;
&lt;p&gt;None of this initially mattered because we’re engineers who just want to build great tech - everything else was secondary. Because of this, our product Kpow is the exact thing we set out to build. No pivots, no compromises. Kpow continues to evolve every day, and our vision has expanded - but we built it, and now it’s time for us to shout about it.&lt;/p&gt;
&lt;p&gt;Factor House is the vehicle for us to do that.&lt;/p&gt;
&lt;p&gt;The need for our company brand to evolve has been compounded by having the quality of our product recognized by global industry leaders; we regularly close deals with Fortune 500 companies yet are virtually unknown in the wider industry. This juxtaposition has motivated us to rebrand properly, expand our product offering, and take Kpow to the broader Kafka market.&lt;/p&gt;
&lt;h2 id=&quot;weve-come-a-long-way&quot;&gt;We’ve come a long way&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f781aaad7b213e363bd533_data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our next major release we’re excited to ship the &lt;strong&gt;free, community edition of Kpow&lt;/strong&gt; (perfect for individual dev use) along with a beautiful brand-new Tailwind UI.&lt;/p&gt;
&lt;p&gt;Our team are currently hard at work on our next major feature which is &lt;strong&gt;ksqlDB integration&lt;/strong&gt;. If that interests you, get in touch!&lt;/p&gt;
&lt;p&gt;I could show you a screenshot of Kpow from three years ago - but wow - thanks to the input of our amazing users we have come a long, long way.&lt;/p&gt;
&lt;h2 id=&quot;hello-weve-arrived&quot;&gt;Hello, we’ve arrived!&lt;/h2&gt;
&lt;p&gt;2022 has given us a series of small victories from 10xing our revenue, onboarding Fortune 500 clients, paying proper salaries, growing the team, setting up our first office, and the ceremonial burning of the desk in the bedroom (co-founding a company with your husband is one thing, a desk in the bedroom is another entirely). I digress. However, the best moment for me was seeing our new logo and brand identity for Factor House for the first time and thinking:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;‘We’re a proper company now. We’ve arrived.’&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Next week we are taking the team on a US tour sponsoring the &lt;a href=&quot;https://thestrangeloop.com/&quot;&gt;&lt;strong&gt;Strange Loop&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://2022.currentevent.io/website/39543/welcome&quot;&gt;&lt;strong&gt;Current 2022&lt;/strong&gt;&lt;/a&gt; conferences. We’re excited about the opportunities this will bring.&lt;/p&gt;
&lt;p&gt;And after that? Well, you’ll just have to wait and see. But we’ve got big ideas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kylie Troy-West is the Co-Founder and COO of Factor House.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Factor House build Kpow, a simple, secure, enterprise tool for Apache Kafka®.&lt;/p&gt;
</content:encoded><category>Company</category><author>Kylie Troy-West</author></item><item><title>Our Commitment to Engineers</title><link>https://factorhouse.io/articles/our-commitment-to-engineers/</link><guid isPermaLink="true">https://factorhouse.io/articles/our-commitment-to-engineers/</guid><description>With our funding announcement and the upcoming launch of the Factor Platform, we know some of our existing customers might be wondering: What does this mean for Kpow and Flex? Will we be forced to upgrade? Will prices spike? Keep one thing in mind - at Factor House we&apos;re here for engineers.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;our-commitment-to-engineers-no-forced-upgrades-no-breaking-changes&quot;&gt;Our Commitment to Engineers: No Forced Upgrades, No Breaking Changes&lt;/h1&gt;
&lt;p&gt;With our latest funding announcement and the upcoming launch of the Factor Platform, we know some of our existing customers might be wondering: What does this mean for Kpow and Flex? Will we be forced to upgrade? Will prices suddenly spike?&lt;/p&gt;
&lt;p&gt;Let’s clear that up now: &lt;strong&gt;Kpow and Flex are here to stay&lt;/strong&gt;. No forced upgrades to platform, no breaking changes, no artificial roadblocks. Engineers trust us because we build tools that work for them, not against them—and that will never change. Period.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f79704baf60f77f83fa4ed_no-breaking-changes.png&quot; alt=&quot;Our Commitment To Engineers Image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-factor-house-philosophy-build-dont-break&quot;&gt;The Factor House Philosophy: Build, Don’t Break&lt;/h2&gt;
&lt;p&gt;Some companies in our space have taken a different approach; adding new products and forcing customers to move to them or making sudden, dramatic price changes. That’s not how we operate. We believe software engineers deserve better.&lt;/p&gt;
&lt;p&gt;If you’re using Kpow or Flex today, you’ll continue to have full access, support, and ongoing updates into the foreseeable future. &lt;strong&gt;We don’t make breaking changes. We don’t sunset products just because a new one exists.&lt;/strong&gt; We’ll always act in the best interests of engineers while continuing to build enterprise solutions that support your evolving needs.&lt;/p&gt;
&lt;h2 id=&quot;why-build-a-platform-then&quot;&gt;Why Build a Platform, Then?&lt;/h2&gt;
&lt;p&gt;If Kpow and Flex will still be supported, you might be wondering: Why introduce a platform at all? The answer is simple: while our individual tools solve specific challenges, Factor Platform is designed to solve the bigger picture.&lt;/p&gt;
&lt;p&gt;Factor Platform isn’t a replacement - it’s a &lt;strong&gt;step up&lt;/strong&gt; for those who need it. Here’s what it will offer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralized Management&lt;/strong&gt;: Kpow, Flex, and all future tools in one place - streamlined and and enterprise scale. A single Web UI and API for data in motion at your organization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control and Automation&lt;/strong&gt;: Factor Platform is completely dynamically configurable via the UI and API, no more restarts when your RBAC configuration changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Insights and Empowerment&lt;/strong&gt;: Engineers exist in the space between Kafka, Flink, and other systems. That’s where the data lives. That’s where Factor Platform thrives.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;We love our customers&lt;/strong&gt;. Great news if you’re operating in the real-time space, we love your customers too.&lt;/p&gt;
&lt;p&gt;We think Kpow and Flex are the right tools for most engineering teams today - we’re going to sell a lot more licenses.&lt;/p&gt;
&lt;p&gt;If you’re happily using Kpow or Flex already, you can keep using them as always. If you’re looking for a way to scale, simplify, and centralize your real-time data tools, Factor Platform will be there when you need it.&lt;/p&gt;
&lt;p&gt;This isn’t about locking anyone in - it’s about giving engineers &lt;strong&gt;more options&lt;/strong&gt;, not fewer. That’s a philosophy we’ll always stand by.&lt;/p&gt;
&lt;h2 id=&quot;join-the-conversation&quot;&gt;Join the Conversation&lt;/h2&gt;
&lt;p&gt;We know engineers value transparency, and we want to keep the conversation open. If you have thoughts, questions, or feedback, we’d love to hear from you. Your insights shape the tools we build, and we’re committed to making sure they continue to serve your needs.&lt;/p&gt;
&lt;p&gt;Factor Platform is on the horizon, and we’re excited to share more soon. If you’d like an early look, reach out - we’d love to show you what’s coming.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/platform/get-started/&quot;&gt;&lt;strong&gt;Tell us what you need in a unified platform for streaming data&lt;/strong&gt;&lt;/a&gt; and we’ll let you know when Factor Platform is ready for early access.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/blog/articles/from-bootstrap-to-blackbird/&quot;&gt;&lt;strong&gt;Read more&lt;/strong&gt;&lt;/a&gt; about our $5M seed round and where we go from here.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Company</category><author>Derek Troy-West</author></item><item><title>Melbourne Kafka x Flink July Meetup Recap: Real-time Data Hosted by Factor House &amp; Confluent</title><link>https://factorhouse.io/articles/real-time-data-to-insights-meetup-july25/</link><guid isPermaLink="true">https://factorhouse.io/articles/real-time-data-to-insights-meetup-july25/</guid><description>From structuring data streams to spinning up full pipelines locally, our latest Kafka x Flink meetup in Melbourne was packed with hands-on demos and real-time insights. Catch the highlights and what&apos;s next.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On July 31st, we welcomed the Melbourne data streaming community to our Factor House HQ in Northcote for an evening of real-time insights, local dev tools, and excellent company, cohosted with our friends at Confluent.&lt;/p&gt;
&lt;p&gt;The event, &lt;a href=&quot;https://www.meetup.com/en-AU/melbourne-distributed/events/310165634/&quot;&gt;&lt;strong&gt;From Real-Time Data to Insights &amp;amp; Local Development with Kafka and Flink&lt;/strong&gt;&lt;/a&gt;, was packed with engineers, data practitioners, and curious minds looking to deepen their knowledge of stream processing and hands-on development workflows.&lt;/p&gt;
&lt;p&gt;Pizza, drinks, and chatter kicked things off, and the vibes didn’t disappoint. The crowd was buzzing with conversation, from real-time architectures to favorite CLI tools.&lt;/p&gt;
&lt;h2 id=&quot;talks-that-delivered&quot;&gt;Talks That Delivered&lt;/h2&gt;
&lt;h3 id=&quot;olena-kutsenko-staff-developer-advocate-at-confluent&quot;&gt;Olena Kutsenko, Staff Developer Advocate at Confluent&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://drive.google.com/file/d/1Kmi0Q7sGd9v5wuWpuPdv11p7iJ3WFhaN/view?usp=sharing&quot;&gt;&lt;strong&gt;See Olena’s presentation&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Olena Kutsenko, Staff Developer Advocate at Confluent, took the stage first with The Art of Structuring Real-time Data Streams into Actionable Insights. Her talk offered a compelling walkthrough of the Kafka–Flink–Iceberg stack, showing how to tame messy real-time data and prepare it for analytics and AI. Attendees praised her clarity and technical depth:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;“Olena managed to communicate her ideas in a way that was comprehensible to non-technical people, whilst still articulating the value for more experienced attendees.”&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;jaehyeon-kim-developer-experience-engineer-at-factor-house&quot;&gt;Jaehyeon Kim, Developer Experience Engineer at Factor House&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/blog/articles/intro-to-factor-house-local/&quot;&gt;&lt;strong&gt;Read Jae’s blog post about Factor House Local &amp;amp; Labs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next up, Jaehyeon Kim, Developer Experience Engineer at Factor House, gave a tour of Hands-On Local Development: Kafka, Flink, Docker, and more. Jae introduced Factor House Labs — a collection of open-source Docker environments designed to get engineers experimenting with modern data platforms fast.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;“Jae’s passion for the product was infectious. His demo showed just how easy it is to spin up a full end-to-end data pipeline locally.”&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7de607313a11c91334563_factor-house-confluent-data-streaming-meetup-presentations.jpeg&quot; alt=&quot;presentations by Olena and Jae&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;community-connection-and-whats-next&quot;&gt;Community, Connection, and What’s Next&lt;/h2&gt;
&lt;p&gt;Beyond the talks, a big highlight was the crowd itself. We saw first-timers, veterans, and everyone in between sharing ideas and chatting about use cases.&lt;/p&gt;
&lt;p&gt;This meetup also marked the launch of the &lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;&lt;strong&gt;Factor House Community on Slack&lt;/strong&gt;&lt;/a&gt;, a space to keep the conversation going, swap tips, and collaborate on all things real-time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f7de607313a11c91334558_factor-house-confluent-meetup-july2025.jpeg&quot; alt=&quot;presentations by Olena and Jae&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;next-stop-sydney&quot;&gt;Next Stop: Sydney!&lt;/h3&gt;
&lt;p&gt;Couldn’t make it to Melbourne? This meetup will happen again in Sydney on September 16. &lt;a href=&quot;https://www.meetup.com/en-AU/apache-kafka-sydney/events/308854185/&quot;&gt;&lt;strong&gt;Sydney Apache Kafka x Flink Meetup&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;stay-connected&quot;&gt;Stay Connected&lt;/h3&gt;
&lt;p&gt;Want to hear about future events, tools, and hands-on learning experiences?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;&lt;strong&gt;Join the Factor House Community on Slack&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/company/factorhouse/&quot;&gt;&lt;strong&gt;Follow Factor House on LinkedIn&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>Company</category><author>Sarah Brown</author></item><item><title>Updates to container specifics (DockerHub and Helm Charts)</title><link>https://factorhouse.io/articles/updates-to-container-specifics/</link><guid isPermaLink="true">https://factorhouse.io/articles/updates-to-container-specifics/</guid><description>Discover how our 94.1 release has streamlined DockerHub, Helm Charts, and AWS Marketplace deployments!</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;updates-to-container-specifics-dockerhub-and-helm-charts&quot;&gt;Updates to Container Specifics (DockerHub and Helm Charts)&lt;/h2&gt;
&lt;p&gt;As part of our 94.1 release we have seen some big, sweeping changes streamlining our deployment pipeline. Key improvements in this space include &lt;a href=&quot;https://factorhouse.io/blog/articles/final-goodbye-operatr-io/&quot;&gt;&lt;strong&gt;cleaning up of artifact names&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/blog/articles/java-compatibility-and-evolution-strategy/&quot;&gt;&lt;strong&gt;solidifying our strategy around Java version compatibility and evolution&lt;/strong&gt;&lt;/a&gt;. Along with these major improvements we have also poured some love into our base Dockerfiles for Kpow and Flex.&lt;/p&gt;
&lt;p&gt;These changes ensure that our products are more ergonomic for our end-users to consume. Read on to learn more about our container improvements!&lt;/p&gt;
&lt;h3 id=&quot;new-aws-marketplace-ecr-co-ordinates&quot;&gt;New AWS Marketplace ECR co-ordinates&lt;/h3&gt;
&lt;p&gt;For users purchasing Kpow for Apache Kafka &lt;a href=&quot;https://aws.amazon.com/marketplace/seller-profile?id=ab356f1d-3394-4523-b5d4-b339e3cca9e0&quot;&gt;&lt;strong&gt;on the AWS Marketplace&lt;/strong&gt;&lt;/a&gt;, the location of our Hourly and Annual products has been updated:&lt;/p&gt;
&lt;p&gt;For &lt;a href=&quot;https://aws.amazon.com/marketplace/pp/prodview-5jvke6codhrsm&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka (Hourly)&lt;/strong&gt;&lt;/a&gt; the new coordinates are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker container - 709825985650.dkr.ecr.us-east-1.amazonaws.com/factor-house/kpow-hourly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For &lt;a href=&quot;https://aws.amazon.com/marketplace/pp/prodview-vgghgqdsplhvc&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka (Annual)&lt;/strong&gt;&lt;/a&gt; the new coordinates are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker container - 709825985650.dkr.ecr.us-east-1.amazonaws.com/factor-house/kpow-annual&lt;/li&gt;
&lt;li&gt;Helm chart - 709825985650.dkr.ecr.us-east-1.amazonaws.com/factor-house/kpow-annual-chart&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;new-dockerhub-co-ordinates&quot;&gt;New DockerHub co-ordinates&lt;/h3&gt;
&lt;p&gt;We are excited to announce a significant improvement in our DockerHub management strategy, aimed at enhancing clarity and streamlining your experience with Factor House products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introducing Our New Docker Image Structure&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Previously, we offered separate Docker images for different Kpow editions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;factorhouse/kpow-ee&lt;/code&gt;&lt;/strong&gt; - (Enterprise edition)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;factorhouse/kpow-se&lt;/code&gt;&lt;/strong&gt; - (Standard edition)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have now collapsed both &lt;strong&gt;&lt;code&gt;kpow-se&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;kpow-ee&lt;/code&gt;&lt;/strong&gt; into a single DockerHub image found at &lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow&quot;&gt;&lt;strong&gt;factorhouse/kpow&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By consolidating our Docker images, we aim to eliminate confusion and streamline the deployment process for all customers. A single image will cater to every license type, ensuring clarity and ease of use in your experience with Factor House products.&lt;/p&gt;
&lt;p&gt;The community edition still remains available at &lt;a href=&quot;https://hub.docker.com/r/factorhouse/kpow-ce&quot;&gt;&lt;strong&gt;factorhouse/kpow-ce&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://hub.docker.com/r/factorhouse/flex-ce&quot;&gt;&lt;strong&gt;factorhouse/flex-ce&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;new-helm-charts-co-ordinates-and-charts&quot;&gt;New Helm Charts co-ordinates and Charts!&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Welcome to Our Expanded Ecosystem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We have transitioned from the previous Charts repository &lt;strong&gt;&lt;code&gt;kpow/kpow&lt;/code&gt;&lt;/strong&gt; to a new, centralized location under the Factor House banner. This change aligns with our rebranding and underscores our status as a multi-product company dedicated to providing best-in-class tools and services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Streamlined Helm Chart Management&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To access our updated Helm Charts, please update and use the following repository:&lt;/p&gt;
&lt;h2 id=&quot;helm-repo-add-factorhouse-httpschartsfactorhouseiohelm-repo-update&quot;&gt;helm repo add factorhouse &lt;a href=&quot;https://charts.factorhouse.iohelm&quot;&gt;https://charts.factorhouse.iohelm&lt;/a&gt; repo update&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Key Highlights of Our New Offering&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flex Helm Charts&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We are excited to introduce Helm charts for &lt;a href=&quot;https://factorhouse.io/flex&quot;&gt;&lt;strong&gt;Flex&lt;/strong&gt;&lt;/a&gt;, our Apache Flink product!&lt;/li&gt;
&lt;li&gt;Installation usage:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;helm install &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;--namespace&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;LICENSE_ID&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;00000000-0000-0000-0000-000000000001&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;LICENSE_CODE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;FLEX_CREDIT&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;LICENSEE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Factor House&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;\,&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; Inc.&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;LICENSE_EXPIRY&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;2022-01-01&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;LICENSE_SIGNATURE&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;638......A51&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  --&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; env&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;FLINK_REST_URL&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;http://flink-dev.svc&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More detailed installation instructions can be found at our &lt;a href=&quot;https://github.com/factorhouse/helm-charts/tree/main/charts/flex&quot;&gt;&lt;strong&gt;GitHub repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Community Edition (CE) Helm Charts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We are excited to introduce community Helm charts for our products! This has been a much requested feature from our growing community userbase:&lt;/p&gt;
&lt;p&gt;Install Kpow Community Edition with ease:&lt;/p&gt;
&lt;p&gt;/&lt;/p&gt;
&lt;p&gt;Experience the power of Flex Community, now available as a Helm chart:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;helm install my&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;ce factorhouse&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;ce&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Availability&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All Helm Charts are open source and hosted on GitHub at &lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;factorhouse/helm-charts&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;They are also listed on &lt;a href=&quot;https://artifacthub.io/packages/search?org=factorhouse&quot;&gt;&lt;strong&gt;ArtifactHub&lt;/strong&gt;&lt;/a&gt;, ensuring discoverability and ease of use.&lt;/li&gt;
&lt;li&gt;For AWS Marketplace users, our Amazon-specific charts can now be found under the &lt;a href=&quot;https://aws.amazon.com/marketplace/pp/prodview-vgghgqdsplhvc&quot;&gt;&lt;strong&gt;Factor House organization&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;amazon-corretto-17-as-the-default-base-image&quot;&gt;Amazon Corretto 17 as the default base image&lt;/h3&gt;
&lt;p&gt;Starting with 94.1 the base image for our products is &lt;a href=&quot;https://aws.amazon.com/corretto&quot;&gt;&lt;strong&gt;Amazon Coretto 17&lt;/strong&gt;&lt;/a&gt;. For almost all customers will be a completely transparent change.&lt;/p&gt;
&lt;p&gt;As part of this clean up, we have dropped a few tags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;alpine&lt;/code&gt;&lt;/strong&gt; tag - we dropped the &lt;strong&gt;&lt;code&gt;alpine&lt;/code&gt;&lt;/strong&gt; tag so that we could focus solely on supporting &lt;a href=&quot;https://aws.amazon.com/amazon-linux-2/?amazon-linux-whats-new.sort-by=item.additionalFields.postDateTime&amp;amp;amazon-linux-whats-new.sort-order=desc&quot;&gt;&lt;strong&gt;amazonlinux&lt;/strong&gt;&lt;/a&gt; as our base. We aim to support a single, stable long-term support distro as the base for our DockerFiles. Customers can still create their own custom DockerFile targeting alpine with our products.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;java17&lt;/code&gt;**tag - now the default base image, previously Java11.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changes-to-dockerfile-specifics&quot;&gt;Changes to DockerFile specifics&lt;/h3&gt;
&lt;h4 id=&quot;new-entrypoint-location&quot;&gt;New entrypoint location&lt;/h4&gt;
&lt;p&gt;We have changed the entrypoint from &lt;strong&gt;&lt;code&gt;/opt/operatr&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;/opt/factorhouse&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; for some customers this might be a breaking change if you use our provided Dockerfile as a base image and reference our old &lt;strong&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt;&lt;/strong&gt; anywhere.&lt;/p&gt;
&lt;h4 id=&quot;new-default-java_opts&quot;&gt;New default JAVA_OPTS&lt;/h4&gt;
&lt;p&gt;We have added extra flags to our JAVA_OPTS:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;opens&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;java.xml&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;com.sun.org.apache.xerces.internal.dom&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ALL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;UNNAMED&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;opens&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;java.xml&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;com.sun.org.apache.xerces.internal.jaxp&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ALL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;UNNAMED&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;opens&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;java.xml&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;com.sun.org.apache.xerces.internal.util&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;ALL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;UNNAMED&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are required to run our products with Java 17+. One of our external dependencies requires internal XML processing classes (from Xerces) and JDK 17+ enforces stricter module boundaries, blocking reflective access to internal APIs by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; if you set custom &lt;strong&gt;&lt;code&gt;JAVA_OPTS&lt;/code&gt;&lt;/strong&gt; when using our Dockerfiles, you will need to update your opts to include these additional flags.&lt;/p&gt;
</content:encoded><category>Product</category><author>Factor House</author></item><item><title>Web Accessibility at Factor House</title><link>https://factorhouse.io/articles/web-accessibility-at-factor-house/</link><guid isPermaLink="true">https://factorhouse.io/articles/web-accessibility-at-factor-house/</guid><description>How do skateboards and green suns drive web accessibility at Factor House? Learn why building accessible products is important to us, and how we&apos;ve changed to ensure that accessibility is embedded in our development process.</description><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-importance-of-accessibility-at-factor-house-a-ceos-perspective&quot;&gt;The Importance of Accessibility at Factor House: A CEO’s Perspective&lt;/h2&gt;
&lt;p&gt;When I was eleven there was an activity at school to draw a picture for your grandparents, the picture would then be laminated for you to give them as a gift. I was in my cool skateboarding phase, so I drew myself skateboarding on a half-pipe - and then I colored the sun in green. My teacher came over asked about my choice in colors. Some of the other kids had a curious peek, because they could see that I had done something wrong — even if I couldn’t. That was the moment I learned that I am colorblind.&lt;/p&gt;
&lt;p&gt;Color blindness hasn’t significantly impacted my career beyond curtailing early thoughts of being a pilot. These days I use color blind modes in tools like GitHub to help with my work, and the biggest practical problem that protanopia gives me is that I can’t tell when a banana is ripe until it starts getting spots. I eat a lot of green bananas.&lt;/p&gt;
&lt;p&gt;Color blindness has, however, given me a better awareness of how easily products can become inaccessible. When a customer asked us if we could undertake to provide a &lt;a href=&quot;https://en.wikipedia.org/wiki/Voluntary_Product_Accessibility_Template&quot;&gt;&lt;strong&gt;Voluntary Product Accessibility Template (VPAT)&lt;/strong&gt;&lt;/a&gt; for &lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, we jumped at the chance to understand where our products were lacking and how to improve them.&lt;/p&gt;
&lt;h3 id=&quot;achieving-wcag-21-aa-compliance&quot;&gt;Achieving WCAG 2.1 AA Compliance&lt;/h3&gt;
&lt;p&gt;A quick note before we congratulate ourselves too much - while the request for a VPAT was new to me, &lt;a href=&quot;https://www.w3.org/TR/WCAG21/&quot;&gt;&lt;strong&gt;Web Content Accessibility Guidelines (WCAG)&lt;/strong&gt;&lt;/a&gt; were not. I first heard of WCAG while working on a user portal for an insurance company in England way back in 2002. Web UI have changed a lot since those days, but the expectation that quality work results in accessible UI has remained the same.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f785753cd0e57115f61f33_kpow-accessibility.png&quot; alt=&quot;Kpow For Apache Kafka Topic UI Accessibility Report&quot;&gt;&lt;/p&gt;
&lt;p&gt;Providing a VPAT requires a commitment to achieve and document established accessibility standards like WCAG and Section 508 of the Rehabilitation Act. Working towards this commitment helps organisations assess and communicate the accessibility features and compliance of their products, particularly for users who rely on assistive technologies or have accessibility needs. We understood that accessibility isn’t just a product feature - it’s a necessity for creating inclusive products that everyone can use effectively.&lt;/p&gt;
&lt;p&gt;We partnered with an independent consultancy, &lt;a href=&quot;https://www.accessibilityoz.com/&quot;&gt;&lt;strong&gt;AccessibilityOZ&lt;/strong&gt;&lt;/a&gt;, to ensure we were doing this right. AccessibilityOZ involves people who use various assistive technologies and have different accessibility needs in the testing of your website and product. Their comprehensive approach included multiple audits as we completed our work, and was exactly what we needed to not just meet the accessibility standards but to exceed them.&lt;/p&gt;
&lt;p&gt;I’m happy to say that Kpow for Apache Kafka is &lt;a href=&quot;https://factorhouse.io/blog/releases/92-4/&quot;&gt;&lt;strong&gt;WCAG 2.1 AA compliant&lt;/strong&gt;&lt;/a&gt; since March 2024, with a &lt;a href=&quot;https://factorhouse.io/pdf/vpat/kpow-93-4-vpat.pdf&quot;&gt;&lt;strong&gt;VPAT published&lt;/strong&gt;&lt;/a&gt; for each release since. &lt;a href=&quot;https://factorhouse.io/flex/&quot;&gt;&lt;strong&gt;Flex for Apache Flink&lt;/strong&gt;&lt;/a&gt; will have VPAT published from release 94.1 onwards - due shortly!&lt;/p&gt;
&lt;h3 id=&quot;web-accessibility-is-not-a-tick-box-exercise&quot;&gt;Web Accessibility is not a Tick-Box Exercise&lt;/h3&gt;
&lt;p&gt;The process of obtaining our VPAT was a significant undertaking, it required us to assess every aspect of Kpow and make necessary changes to ensure compliance with accessibility guidelines. We had to dig deep into our codebase, working from the ground up to close over 100 tickets raised by AccessibilityOz. It was a challenging process taking over 12 months, but it was also incredibly rewarding. We are better developers for it.&lt;/p&gt;
&lt;p&gt;Working on accessibility not only improved our products for users with accessibility needs but has also enhanced the overall quality of Kpow for everyone. The improvements we made, such as better navigation and more readable layouts, have made Kpow a faster, more user-friendly tool.&lt;/p&gt;
&lt;p&gt;There is no trade-off between accessibility and functionality; in fact, the two go hand in hand. The process of undertaking VPAT has made our products better.&lt;/p&gt;
&lt;p&gt;Accessibility is now embedded in our development process at Factor House. We’ve implemented new tools like Storybook to detect accessibility issues, ensuring that every new feature we release is accessible from the start. We’ve also upskilled our team, making sure that all of our developers understand the importance of accessibility and are capable of delivering products that meet these standards.&lt;/p&gt;
&lt;p&gt;As we continue to grow and evolve, accessibility will remain a core focus for us. We believe that providing accessible tools is not only the right thing to do but also essential for attracting and retaining the best customers. In today’s market, organisations have a duty of care to their employees, and providing accessible tools is a fundamental part of that responsibility.&lt;/p&gt;
&lt;p&gt;My advice to other tech leaders and developers is simple: prioritise accessibility. It’s not just about meeting ticking a box; it’s about improving your product for every user. At Factor House, we’ve seen firsthand how focusing on accessibility has made our products better for everyone, and I’m proud of the work we’ve done.&lt;/p&gt;
&lt;p&gt;For us at Factor House, accessibility is not a one-time task but an ongoing commitment to excellence in product development.&lt;/p&gt;
</content:encoded><category>Company</category><author>Derek Troy-West</author></item><item><title>Set Up Kpow with NetApp Instaclustr Platform</title><link>https://factorhouse.io/articles/set-up-kpow-with-instaclustr/</link><guid isPermaLink="true">https://factorhouse.io/articles/set-up-kpow-with-instaclustr/</guid><description>Integrate Kpow with Instaclustr in minutes. Gain unified visibility and control over your managed Kafka brokers, Karapace Schema Registry, and Kafka Connect through our market-leading engineering toolkit.</description><pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.instaclustr.com/&quot;&gt;NetApp Instaclustr&lt;/a&gt; provides a robust, fully managed platform for open-source Apache Kafka, eliminating the complex operational overhead of running a distributed streaming ecosystem. While the platform ensures infrastructure reliability and scalability, engineering teams still need a powerful, intuitive tool to monitor, manage, and interact with their live Kafka resources.&lt;/p&gt;
&lt;p&gt;Kpow bridges this gap by acting as a comprehensive engineering toolkit for your Instaclustr environment. Fully compatible out of the box, Kpow connects seamlessly to your managed brokers, Karapace Schema Registry, and Kafka Connect clusters using standard Kafka protocols, which delivers a single pane of glass without the need for proprietary plugins, sidecars, or complex custom configurations.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to Instaclustr, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running Instaclustr Kafka cluster:&lt;/strong&gt; Reachable from the host where you intend to run Kpow.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Network reachability (Firewall Rules):&lt;/strong&gt; You must add the public IP address of the machine running Kpow to the Firewall Rules in the Instaclustr console.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Connection Details:&lt;/strong&gt; Your Kafka Bootstrap Server addresses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Authentication:&lt;/strong&gt; Your Kafka cluster username and password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Karapace Schema Registry (Optional):&lt;/strong&gt; The Schema Registry URL (secured with a CA-signed certificate) and its associated username and password. &lt;em&gt;Note: You must also add your IP to the Schema Registry Firewall Rules.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka Connect (Optional):&lt;/strong&gt; The Connect REST URL and its associated username and password. &lt;em&gt;Note: You must also add your IP to the Kafka Connect Firewall Rules.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;‍&lt;strong&gt;Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to Instaclustr is using Docker.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific cluster details found on the &lt;strong&gt;Connection Info&lt;/strong&gt; page of your Instaclustr console:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;Instaclustr Demo&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;&amp;#x3C;KAFKA-IP1&gt;:9092,&amp;#x3C;KAFKA-IP2&gt;:9092,&amp;#x3C;KAFKA-IP3&gt;:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_PLAINTEXT&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;SCRAM-SHA-256&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&apos;org.apache.kafka.common.security.scram.ScramLoginModule required username=&quot;&amp;#x3C;KAFKA_USERNAME&gt;&quot; password=&quot;&amp;#x3C;KAFKA_PASSWORD&gt;&quot;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Security protocol:&lt;/strong&gt; Depending on your Instaclustr setup, your &lt;code&gt;SECURITY_PROTOCOL&lt;/code&gt; may be &lt;code&gt;SASL_SSL&lt;/code&gt; instead of &lt;code&gt;SASL_PLAINTEXT&lt;/code&gt;. Always verify this in your Instaclustr Connection Info.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, open a browser and navigate to &lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000&lt;/a&gt;. You will immediately see your Instaclustr topics, consumer groups, and brokers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb424a94db7638e78a2b27_kpow-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;Connecting to Instaclustr is straightforward, but it requires gathering the correct information and ensuring network access is permitted.&lt;/p&gt;
&lt;h3 id=&quot;connection-info&quot;&gt;Connection Info&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Instaclustr Karapace&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;https://&amp;#x3C;REGISTRY_URL_WITH_CA_CERTIFICATE&gt;:8085&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_AUTH=&quot;USER_INFO&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_USER=&quot;&amp;#x3C;SCHEMA_REGISTRY_USERNAME&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_PASSWORD=&quot;&amp;#x3C;SCHEMA_REGISTRY_PASSWORD&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the necessary configuration parameters, including your Bootstrap Server IPs, username, and password, can be found on the &lt;strong&gt;Connection Info&lt;/strong&gt; page of your cluster within the Instaclustr console.&lt;/p&gt;
&lt;h3 id=&quot;firewall-rules&quot;&gt;Firewall Rules&lt;/h3&gt;
&lt;p&gt;Instaclustr secures clusters by blocking external traffic by default. If your Kpow container is failing to connect or timing out, ensure you have navigated to the &lt;strong&gt;Firewall Rules&lt;/strong&gt; section of the Instaclustr console and added the IP address of the environment hosting Kpow.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;If you have provisioned the Karapace Schema Registry as an enterprise add-on, or are running a Managed Kafka Connect cluster in your Instaclustr environment, Kpow can manage them natively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Just like the Kafka brokers, you must manually update the Firewall Rules in the Instaclustr console for both the Schema Registry and Kafka Connect endpoints to allow Kpow to connect to them.&lt;/p&gt;
&lt;h3 id=&quot;karapace-schema-registry&quot;&gt;Karapace Schema Registry&lt;/h3&gt;
&lt;p&gt;To manage your schemas directly within Kpow, add the following environment variables to your deployment. Be sure to use the URL secured with a CA-signed certificate (found on the Schema Registry Connection Info page).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Instaclustr Karapace&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;https://&amp;#x3C;REGISTRY_URL_WITH_CA_CERTIFICATE&gt;:8085&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_AUTH=&quot;USER_INFO&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_USER=&quot;&amp;#x3C;SCHEMA_REGISTRY_USERNAME&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_PASSWORD=&quot;&amp;#x3C;SCHEMA_REGISTRY_PASSWORD&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;managed-kafka-connect&quot;&gt;Managed Kafka Connect&lt;/h3&gt;
&lt;p&gt;To monitor connectors, tasks, and configurations, add your Connect cluster details. Instaclustr often requires permissive SSL for its Connect REST endpoints.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_NAME=&quot;Instaclustr Connect&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_REST_URL=&quot;https://&amp;#x3C;KAFKA-CONNECT-IP&gt;:8083&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_PERMISSIVE_SSL=&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_AUTH=&quot;BASIC&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_BASIC_AUTH_USER=&quot;&amp;#x3C;KAFKA_CONNECT_USERNAME&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONNECT_BASIC_AUTH_PASS=&quot;&amp;#x3C;KAFKA_CONNECT_PASSWORD&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters running alongside your Instaclustr instances, we recommend using our official Helm Charts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your Instaclustr managed streaming infrastructure. By using standard Kafka protocols, you can unify your Kafka clusters, Karapace Schema Registry, and Managed Connect environments in minutes.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Instaclustr integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;related-content&quot;&gt;Related Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-aws&quot;&gt;Set Up Kpow with Amazon Managed Streaming for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Set Up Kpow with Confluent Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-gcp&quot;&gt;Set Up Kpow with Google Cloud Managed Service for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/integrate-kpow-with-oci-streaming&quot;&gt;How to Integrate Kpow with OCI Streaming with Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Introducing Webhook Support in Kpow</title><link>https://factorhouse.io/articles/introducing-webhook-support-in-kpow/</link><guid isPermaLink="true">https://factorhouse.io/articles/introducing-webhook-support-in-kpow/</guid><description>This guide demonstrates how to enhance Kafka monitoring and data governance by integrating Kpow&apos;s audit logs with external systems. We provide a step-by-step walkthrough for configuring webhooks to send real-time user activity alerts from your Kafka environment directly into collaboration platforms like Slack and Microsoft Teams, streamlining your operational awareness and response.</description><pubDate>Wed, 10 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Kpow is an enterprise-grade toolkit for managing and monitoring Apache Kafka. A central feature for maintaining data governance is its audit log, which records all user actions. To enhance real-time monitoring and integration, Kpow can forward these audit log records to external systems via webhooks.&lt;/p&gt;
&lt;p&gt;Kpow has long supported sending these notifications to &lt;strong&gt;Slack&lt;/strong&gt; , and now also supports &lt;strong&gt;Microsoft Teams&lt;/strong&gt; and any &lt;strong&gt;generic HTTP webhook server&lt;/strong&gt;. This makes it possible to receive immediate alerts in your collaboration tools or integrate with custom monitoring systems that accept HTTP &lt;strong&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/strong&gt; requests.&lt;/p&gt;
&lt;p&gt;This guide provides a step-by-step walkthrough for configuring webhook integrations in Kpow for Slack, Microsoft Teams, and generic webhook servers. By the end, you’ll be able to stream real-time Kafka activity notifications directly into the platform of your choice.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;If you’re interested in setting up monitoring and configuring alerts to stay on top of system performance, take a look at our earlier post: &lt;a href=&quot;/articles/kafka-alerting-with-kpow-prometheus-and-alertmanager/&quot;&gt;Kafka Alerting with Kpow, Prometheus, and Alertmanager&lt;/a&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;/products/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df945123_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Webhook integration is an Enterprise feature of Kpow. To follow this guide, you will need an Enterprise license. If you do not have one, you can &lt;a href=&quot;https://account.factorhouse.io/auth/getting_started&quot;&gt;&lt;strong&gt;request a trial license&lt;/strong&gt;&lt;/a&gt; from Factor House to explore this functionality.&lt;/p&gt;
&lt;h2 id=&quot;configure-webhooks&quot;&gt;Configure webhooks&lt;/h2&gt;
&lt;p&gt;Kpow has long supported sending webhook notifications to &lt;strong&gt;Slack&lt;/strong&gt; , and now also supports &lt;strong&gt;Microsoft Teams&lt;/strong&gt; and any &lt;strong&gt;generic HTTP webhook server&lt;/strong&gt;. Configuration is handled via environment variables:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;‘WEBHOOK_PROVIDER’&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The target provider: slack, teams, or generic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;‘WEBHOOK_URL’&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The endpoint that will receive webhook events via POST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;‘WEBHOOK_VERBOSITY’&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Event types to send: MUTATIONS, QUERIES, or ALL (default: MUTATIONS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Before starting your Kafka environment, ensure that webhook URLs are created in your chosen platform (Slack, Teams, or generic endpoint).&lt;/p&gt;
&lt;h3 id=&quot;slack&quot;&gt;Slack&lt;/h3&gt;
&lt;p&gt;To integrate Kpow with Slack, you need to create a Slack App and generate an incoming webhook URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a Slack app&lt;/strong&gt; : Navigate to the &lt;a href=&quot;https://api.slack.com/apps&quot;&gt;&lt;strong&gt;Slack API website&lt;/strong&gt;&lt;/a&gt; and click on “Create New App”. Choose to create it “From scratch”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8cc464d2a02e050a90_slack-01.png&quot; alt=&quot;Create Slack App&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Name your app and choose a workspace&lt;/strong&gt; : Provide a name for your application and select the Slack workspace you want to post messages to.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050aa4_slack-02.png&quot; alt=&quot;Configure Slack App&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enable incoming webhooks&lt;/strong&gt; : In your app’s settings page, go to “Incoming Webhooks” under the “Features” section. Toggle the feature on and then click “Add New Webhook to Workspace”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050aca_slack-03.png&quot; alt=&quot;Configure Webhook&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Select a channel&lt;/strong&gt; : Choose the channel where you want the Kpow notifications to be posted and click “Allow”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050a9b_slack-04.png&quot; alt=&quot;Select Slack Channel&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy the webhook URL&lt;/strong&gt; : After authorizing, you will be redirected back to the webhook configuration page. Copy the newly generated webhook URL. This URL is what you will use to configure Kpow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ad4_slack-05.png&quot; alt=&quot;Copy Webhook URL&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;microsoft-teams&quot;&gt;Microsoft Teams&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# Clone the examples repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;git clone &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;https&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//github.com/factorhouse/examples.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# Move to the web&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;cd features&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;webhook&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# Start Kafka environment &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; multiple Kpow instances that target different webhook backends&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# Replace the placeholder values &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; your actual license and webhook URLs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; KPOW_LICENSE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;path-to-license-file&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;slack-webhook-url&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; TEAMS_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;teams-webhook-url&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; GENERIC_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;http://webhook-server:9000&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;docker compose up&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Microsoft Teams, integration can be set up through &lt;a href=&quot;https://support.microsoft.com/en-au/office/browse-and-add-workflows-in-microsoft-teams-4998095c-8b72-4b0e-984c-f2ad39e6ba9a&quot;&gt;&lt;strong&gt;workflows&lt;/strong&gt;&lt;/a&gt; by creating a flow that listens for an HTTP request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a new flow&lt;/strong&gt; : Navigate to workflows and start creating a new flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050aaa_teams-01.png&quot; alt=&quot;Create New Flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Search for the webhook template&lt;/strong&gt; : In the flow creation interface, search for the keyword “webhook” to find relevant templates. Select the &lt;strong&gt;“Send webhook alterts to a channel”&lt;/strong&gt; template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8cc464d2a02e050a8d_teams-02.png&quot; alt=&quot;Search Webhook Template&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Name the flow and click next&lt;/strong&gt; : Enter a name for your flow, then click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ab9_teams-03.png&quot; alt=&quot;Type Flow Name&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Select the team and channel Name&lt;/strong&gt; : Choose the Microsoft Teams team and channel name, then click &lt;strong&gt;Create flow&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ab0_teams-04.png&quot; alt=&quot;Selecct Team and Channel&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy the webhook URL&lt;/strong&gt; : Copy the newly generated webhook URL. This URL is what you will use to configure Kpow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050aa7_teams-05.png&quot; alt=&quot;Copy Webhook URL&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;generic-webhook-server&quot;&gt;Generic webhook server&lt;/h3&gt;
&lt;p&gt;A generic webhook allows you to send Kpow’s audit log events to any custom application or third-party service that can receive HTTP POST requests. This is useful for integrating with systems that are not officially supported out-of-the-box or for building custom automation workflows. The payload is sent in JSON format, allowing for easy parsing and processing.&lt;/p&gt;
&lt;p&gt;For this guide, we will be using a simple web server developed using Python Flask.&lt;/p&gt;
&lt;h2 id=&quot;launch-kafka-environment&quot;&gt;Launch Kafka environment&lt;/h2&gt;
&lt;p&gt;To test the webhook functionality, use the &lt;strong&gt;webhook-demo&lt;/strong&gt; in the &lt;em&gt;features&lt;/em&gt; folder of the &lt;a href=&quot;https://github.com/factorhouse/examples&quot;&gt;&lt;strong&gt;Factor House examples&lt;/strong&gt;&lt;/a&gt; repository on GitHub. This demo spins up three Kpow instances, each configured to send audit log messages to a different destination: Slack, Microsoft Teams, and a generic web server running on ports 3000, 4000, and 5000.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Clone the examples repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; https://github.com/factorhouse/examples.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Move to the web&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/webhook-demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Start Kafka environment with multiple Kpow instances that target different webhook backends&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Replace the placeholder values with your actual license and webhook URLs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KPOW_LICENSE&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;path-to-license-file&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;slack-webhook-url&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; TEAMS_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&amp;#x3C;teams-webhook-url&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; GENERIC_WEBHOOK_URL&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;http://webhook-server:9000&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; up&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;verify-slack-webhook-messages&quot;&gt;Verify Slack webhook messages&lt;/h3&gt;
&lt;p&gt;To test the Slack integration, perform an action in Kpow that generates an audit event, such as creating and then deleting a topic. You can access the Kpow UI at &lt;strong&gt;&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a topic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The example below shows how to create a new topic in Kpow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050afa_topic-create.png&quot; alt=&quot;Topic Create&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Delete a topic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Similarly, you can delete a topic in Kpow as shown here.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050af7_topic-delete.png&quot; alt=&quot;Topic Delete&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View audit logs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After performing these actions, you can verify they have been logged by navigating to &lt;strong&gt;Settings &amp;gt; Audit log&lt;/strong&gt; in the Kpow UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ab3_topic-logs.png&quot; alt=&quot;Audit Log&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the Slack channel, you should see messages detailing the actions. Each message includes information such as the user who performed the action, the type of action (e.g., &lt;strong&gt;&lt;code&gt;create-topic&lt;/code&gt;&lt;/strong&gt;), and the cluster environment name.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8cc464d2a02e050a93_logs-slack.png&quot; alt=&quot;Slack Webhook Messages&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;verify-teams-webhook-messages&quot;&gt;Verify Teams webhook messages&lt;/h3&gt;
&lt;p&gt;The process to verify messages in Microsoft Teams is the same. After creating and deleting a topic in the Kpow UI (accessible at &lt;strong&gt;&lt;code&gt;http://localhost:4000&lt;/code&gt;&lt;/strong&gt;), your Power Automate flow will trigger, and you will see the corresponding formatted message in your designated Teams channel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ac0_logs-teams.png&quot; alt=&quot;Teams Webhook Messages&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;verify-generic-webhook-messages&quot;&gt;Verify generic webhook messages&lt;/h3&gt;
&lt;p&gt;For the generic webhook, inspect the logs of the webhook server container by running &lt;strong&gt;&lt;code&gt;docker logs webhook-server&lt;/code&gt;&lt;/strong&gt;. The logs display the raw JSON payloads that Kpow sends for topic creation and deletion events, giving you insight into the data structure you can leverage for custom integrations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8ef8dc464d2a02e050ab6_logs-generic.png&quot; alt=&quot;Generic Webhook Messages&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow’s webhook integration is a powerful feature for enhancing the monitoring and security of your Apache Kafka environment. By sending real-time audit log notifications to platforms like Slack and Microsoft Teams, or to any custom endpoint via a generic webhook, you can ensure that your team is immediately aware of important events and changes. This capability not only improves transparency and collaboration but also allows for the creation of custom automation and integration with other operational tools, making your Kafka management more proactive and efficient.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>From Batch to Real-Time: A Hands-On CDC Project with Debezium, Kafka, and theLook eCommerce Data</title><link>https://factorhouse.io/articles/from-batch-to-real-time-a-hands-on-cdc-project-with-debezium-kafka-and-thelook-ecommerce-data/</link><guid isPermaLink="true">https://factorhouse.io/articles/from-batch-to-real-time-a-hands-on-cdc-project-with-debezium-kafka-and-thelook-ecommerce-data/</guid><description>This project transforms the static &quot;theLook&quot; eCommerce dataset into a live data stream. It uses a Python generator to simulate user activity in PostgreSQL, while Debezium captures every database change and streams it to Kafka. This creates a hands-on environment for building and testing real-time CDC pipelines.</description><pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://console.cloud.google.com/marketplace/product/bigquery-public-data/thelook-ecommerce&quot;&gt;&lt;strong&gt;theLook eCommerce dataset&lt;/strong&gt;&lt;/a&gt; is a valuable resource for data professionals. It provides a realistic, comprehensive schema for testing analytics queries and BI tools. However, it has one major limitation: it’s static. It’s a snapshot in time, designed for traditional batch workloads.&lt;/p&gt;
&lt;p&gt;Modern data applications thrive on live, event-driven data. From real-time dashboards to responsive microservices, the ability to react to data as it changes is essential. How can we practice building these systems with a dataset that feels real?&lt;/p&gt;
&lt;p&gt;To solve this, we’ve re-engineered theLook eCommerce data into a &lt;strong&gt;real-time, streaming data source&lt;/strong&gt;. This project transforms the classic batch dataset into a dynamic environment for building and testing Change Data Capture (CDC) pipelines with Debezium and Kafka.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;The complete project, including all source code and setup instructions, is available on &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/projects/thelook-ecomm-cdc&quot;&gt;GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Looking for more hands-on labs and projects? Check out our previous posts: &lt;a href=&quot;https://factorhouse.io/articles/intro-to-factor-house-local&quot;&gt;Introduction to Factor House Local&lt;/a&gt; and &lt;a href=&quot;https://factorhouse.io/articles/building-a-real-time-leaderboard-with-kafka-and-flink&quot;&gt;Building a Real-Time Leaderboard with Kafka and Flink&lt;/a&gt; to level up your streaming data skills.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f0894e61f8bd09443d9b_thelook-datagen.gif&quot; alt=&quot;CDC with Debezium Architecture&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-change-data-capture-cdc-with-debezium&quot;&gt;What is Change Data Capture (CDC) with Debezium?&lt;/h3&gt;
&lt;p&gt;Change Data Capture is a design pattern for tracking row-level changes in a database (&lt;strong&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/strong&gt;) and making them available as an event stream. Instead of repeatedly querying a database for updates, CDC systems read the database’s transaction log directly, capturing every committed change as it happens.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://debezium.io/&quot;&gt;&lt;strong&gt;Debezium&lt;/strong&gt;&lt;/a&gt; is a leading open-source, distributed platform for CDC. It provides a library of connectors that turn your existing databases into event streams. In this project, we use the &lt;strong&gt;Debezium PostgreSQL connector&lt;/strong&gt; , which works by reading the database’s write-ahead log (WAL). To enable this, the PostgreSQL server’s &lt;strong&gt;&lt;code&gt;wal_level&lt;/code&gt;&lt;/strong&gt; is set to &lt;strong&gt;&lt;code&gt;logical&lt;/code&gt;&lt;/strong&gt; , which enriches the log with the detailed information needed for logical decoding.&lt;/p&gt;
&lt;p&gt;With the Debezium PostgreSQL connector, we can use PostgreSQL’s built-in &lt;strong&gt;&lt;code&gt;pgoutput&lt;/code&gt;&lt;/strong&gt; logical decoding plugin to stream the sequence of changes from the WAL. It operates on a push-based model, where the database actively sends changes to the Debezium connector as they are committed. The connector then processes these changes and pushes them as events to Kafka topics, ensuring low-latency data streaming.&lt;/p&gt;
&lt;h3 id=&quot;project-architecture-a-live-ecommerce-store-in-a-box&quot;&gt;Project Architecture: A Live eCommerce Store in a Box&lt;/h3&gt;
&lt;p&gt;This project combines a dynamic data generator with a complete CDC pipeline, allowing you to see the end-to-end flow of data.&lt;/p&gt;
&lt;h4 id=&quot;real-time-data-generator&quot;&gt;Real-Time Data Generator&lt;/h4&gt;
&lt;p&gt;At the heart of the project is a Python-based simulator that brings theLook eCommerce dataset to life. It:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simulates continuous user activity&lt;/strong&gt; , including new user sign-ups, product browsing, purchases, and even order updates like cancellations or returns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Writes this data directly into a PostgreSQL database&lt;/strong&gt; , creating a constantly changing, realistic data source.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Models complex user journeys&lt;/strong&gt; , from anonymous browsing sessions to multi-item orders.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This component transforms PostgreSQL from a static warehouse into a transactional database that mirrors a live application.&lt;/p&gt;
&lt;h4 id=&quot;cdc-pipeline-with-debezium-and-kafka&quot;&gt;CDC Pipeline with Debezium and Kafka&lt;/h4&gt;
&lt;p&gt;With data flowing continuously into PostgreSQL, we can now capture it in real-time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The PostgreSQL database is prepared with a &lt;strong&gt;&lt;code&gt;PUBLICATION&lt;/code&gt;&lt;/strong&gt; for all tables in our eCommerce schema. This publication acts as a logical grouping of tables whose changes should be made available to subscribers, in this case, the Debezium connector.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Debezium PostgreSQL connector&lt;/strong&gt; is deployed and configured to monitor all tables within the schema.&lt;/li&gt;
&lt;li&gt;As the data generator writes new records, Debezium reads the WAL, captures every &lt;strong&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/strong&gt; , and &lt;strong&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/strong&gt; operation.&lt;/li&gt;
&lt;li&gt;It then serializes these change events into Avro format and streams them into distinct &lt;strong&gt;Kafka topics&lt;/strong&gt; for each table (e.g., &lt;strong&gt;&lt;code&gt;ecomm.demo.users&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;ecomm.demo.orders&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is a reliable, low-latency stream of every single event happening in your e-commerce application, ready for consumption.&lt;/p&gt;
&lt;h3 id=&quot;why-is-this-a-good-way-to-learn&quot;&gt;Why is This a Good Way to Learn?&lt;/h3&gt;
&lt;p&gt;This project provides a sandbox that is both realistic and easy to manage. You get hands-on experience with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Realistic schema:&lt;/strong&gt; Work with interconnected tables for users, orders, products, and events—not just a simple demo table.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Industry standard stack:&lt;/strong&gt; Get familiar with the tools that power modern data platforms: PostgreSQL, Debezium, Kafka, and Docker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;End-to-end environment:&lt;/strong&gt; The entire pipeline is runnable on your local machine, giving you a complete picture of how data flows from source to stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-can-you-build-with-this&quot;&gt;What Can You Build With This?&lt;/h3&gt;
&lt;p&gt;A real-time stream of eCommerce events in Kafka opens up many possibilities for development. This project is the perfect starting point for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Building real-time analytics dashboards&lt;/strong&gt; with tools like Apache Flink or Apache Pinot to monitor sales and user activity as it happens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingesting data into a lakehouse&lt;/strong&gt; (e.g., Apache Iceberg) with Apache Flink to keep your warehouse continuously updated with real-time data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developing event-driven microservices&lt;/strong&gt; that react to business events. For example, you could build a &lt;strong&gt;&lt;code&gt;NotificationService&lt;/code&gt;&lt;/strong&gt; that listens to the &lt;strong&gt;&lt;code&gt;ecomm.demo.orders&lt;/code&gt;&lt;/strong&gt; topic and sends a confirmation email when an order’s status changes to &lt;strong&gt;&lt;code&gt;Shipped&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;get-started-in-minutes&quot;&gt;Get Started in Minutes&lt;/h3&gt;
&lt;p&gt;The entire project is containerized and easy to set up.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Clone the &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/projects/thelook-ecomm-cdc&quot;&gt;factorhouse/examples&lt;/a&gt; repository&lt;/strong&gt; from GitHub.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start the infrastructure&lt;/strong&gt; (Kafka, PostgreSQL, etc.) using Docker Compose.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run the data generator&lt;/strong&gt; via Docker Compose to populate the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy the Debezium connector&lt;/strong&gt; and monitor Kafka topics as they are created and populated with real-time data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’d love to see what you build with this. Join the &lt;a href=&quot;https://join.slack.com/t/factorhousecommunity/shared_invite/zt-39x5pms9g-iMBphNvhS2eGrT_6Pl_jkw&quot;&gt;&lt;strong&gt;Factor House Community Slack&lt;/strong&gt;&lt;/a&gt; and share what you’re working on.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This project bridges the gap between static, batch-oriented datasets and the dynamic, real-time world of modern data engineering. It provides a practical, hands-on environment to learn, experiment, and build production-ready CDC pipelines with confidence.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Building a Real-Time Leaderboard with Kafka and Flink</title><link>https://factorhouse.io/articles/building-a-real-time-leaderboard-with-kafka-and-flink/</link><guid isPermaLink="true">https://factorhouse.io/articles/building-a-real-time-leaderboard-with-kafka-and-flink/</guid><description>Learn how to build a real-time &quot;Top-K&quot; analytics pipeline from scratch using a modern data stack. This open-source project guides you through using Apache Kafka, Apache Flink, and Streamlit to ingest, process, and visualize live data, turning a continuous stream of events into actionable insights on an interactive dashboard.</description><pubDate>Tue, 05 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In today’s data-driven world, the ability to process and analyze information in real-time is a significant competitive advantage across many industries. Whether it’s tracking top-selling products in e-commerce, identifying trending topics on social media, or monitoring high-performing assets in finance, real-time analytics pipelines are essential for gaining immediate insights.&lt;/p&gt;
&lt;p&gt;This post explores a complete, open-source project that demonstrates how to build a real-time “Top-K” analytics pipeline. You’ll learn how to ingest a continuous stream of data, process it on the fly to compute key performance metrics, and visualize the results on an interactive dashboard.&lt;/p&gt;
&lt;p&gt;The core of this project is a robust data pipeline that can be broken down into three key stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data Generation&lt;/strong&gt; : A Python script continuously generates a stream of simulated user events, which are then published to an Apache Kafka topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics Processing&lt;/strong&gt; : Four distinct Apache Flink SQL jobs consume the raw data stream from Kafka. Each job is tailored to calculate a specific real-time leaderboard metric: Top Teams, Top Players, Hot Streakers, and Team MVPs. The results are written to their own dedicated Kafka topics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dashboard Visualization&lt;/strong&gt; : A Streamlit web application reads the processed metrics from the Flink output topics and presents them on a dynamic, real-time dashboard, offering at-a-glance insights into performance.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;The complete project, including all source code and setup instructions, is available on &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/projects/mobile-game-top-k-analytics&quot;&gt;GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;This project uses Factor House Local to spin up the development environment. See &lt;a href=&quot;https://factorhouse.io/articles/intro-to-factor-house-local&quot;&gt;Introduction to Factor House Local&lt;/a&gt; to learn more about experimenting with modern data architectures using Kafka, Flink, Spark, Iceberg, and Pinot.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f15ce11e388d0c6552af_mobile-game-top-k-analytics.gif&quot; alt=&quot;Mobile Game Analytics&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;diving-into-the-real-time-metrics&quot;&gt;Diving into the Real-Time Metrics&lt;/h2&gt;
&lt;p&gt;The pipeline continuously computes four different leaderboard-style metrics using Flink SQL. A DDL script initially sets up the necessary source and sink tables. The source table, &lt;strong&gt;&lt;code&gt;user_scores&lt;/code&gt;&lt;/strong&gt; , reads directly from a Kafka topic. Each Flink SQL query consumes this stream, performs its calculations, and writes the output to a corresponding sink table (&lt;strong&gt;&lt;code&gt;top_teams&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;top_players&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;hot_streakers&lt;/code&gt;&lt;/strong&gt; , or &lt;strong&gt;&lt;code&gt;team_mvps&lt;/code&gt;&lt;/strong&gt;). These sink tables use the &lt;strong&gt;&lt;code&gt;upsert-kafka&lt;/code&gt;&lt;/strong&gt; connector, which ensures that the leaderboards are continuously updated as new data arrives.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Top Teams&lt;/strong&gt; : This metric identifies the top 10 entities (grouped as “teams”) with the highest cumulative scores, providing a global view of group performance. The underlying Flink SQL query groups the data by &lt;strong&gt;&lt;code&gt;team_id&lt;/code&gt;&lt;/strong&gt; , calculates a running sum of scores, and then ranks the teams. To ensure accuracy over long periods, the state for this data has a time-to-live (TTL) of 60 minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Top Players&lt;/strong&gt; : Similar to the Top Teams metric, this leaderboard showcases the top 10 individual entities (or “players”) with the highest scores. The logic is much the same: the stream is grouped by &lt;strong&gt;&lt;code&gt;user_id&lt;/code&gt;&lt;/strong&gt; , a cumulative score is calculated, and the entities are ranked globally. This also has a 60-minute TTL to maintain consistent stats over extended sessions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hot Streakers&lt;/strong&gt; : This metric is designed to highlight the top 10 entities currently on a “hot streak,” meaning their short-term performance is significantly outpacing their historical average. The query for this uses sliding time windows to calculate a short-term average (over 10 seconds) and a long-term average (over 60 seconds). The ratio between these two averages determines the “hotness.” Since this metric focuses on recent activity, it uses a shorter state TTL of 5 minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team MVPs&lt;/strong&gt; : This metric first identifies the Most Valuable Player (MVP) for each team—the entity that contributed the largest percentage of the team’s total score. It then ranks these MVPs across all teams to find the top 10 overall. This is achieved using Common Table Expressions (CTEs) in SQL to first calculate total scores per entity and per team, and then these are joined to determine each entity’s contribution ratio.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these metrics offer a rich, real-time view of system dynamics, highlighting top-performing groups, standout individuals, and rising stars. The final results are streamed to a responsive dashboard that displays the leaderboards in continuously refreshing bar charts, with each chart powered by its own dedicated Kafka topic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f15ce11e388d0c6552bb_dashboard.gif&quot; alt=&quot;Analytics Dashboard&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This project serves as a practical blueprint for building powerful, real-time analytics systems. By combining the high-throughput messaging of Apache Kafka, the stateful stream processing of Apache Flink, and the rapid UI development of Streamlit, you can create sophisticated pipelines that deliver valuable insights with minimal latency.&lt;/p&gt;
&lt;p&gt;The “Top-K” pattern is a versatile one, applicable to countless domains beyond the example shown here. The principles of stream ingestion, real-time aggregation, and interactive visualization form a solid foundation for any developer looking to harness the power of live data. We encourage you to clone the repository, run the project yourself, and adapt the architecture to your own unique use cases.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Integrate Kpow with Google Managed Schema Registry</title><link>https://factorhouse.io/articles/integrate-kpow-with-google-schema-registry/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-google-schema-registry/</guid><description>Kpow 94.3 now integrates with Google Cloud&apos;s managed Schema Registry, enabling native OAuth authentication. This guide walks through the complete process of configuring authentication and using Kpow to create, manage, and inspect data validated against Avro schemas.</description><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Google Cloud has enhanced its platform with the launch of a managed &lt;a href=&quot;https://cloud.google.com/managed-service-for-apache-kafka/docs/schema-registry/schema-registry-overview&quot;&gt;&lt;strong&gt;Schema Registry for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, a critical service for ensuring data quality and schema evolution in streaming architectures. &lt;strong&gt;Kpow 94.3&lt;/strong&gt; expands its support for Google Managed Service for Apache Kafka by integrating the managed schema registry. This allows users to manage Kafka clusters, topics, consumer groups, and schemas from a single interface.&lt;/p&gt;
&lt;p&gt;Building on our &lt;a href=&quot;https://factorhouse.io/blog/how-to/set-up-kpow-with-gcp/&quot;&gt;&lt;strong&gt;earlier setup guide&lt;/strong&gt;&lt;/a&gt;, this post details how to configure the new schema registry integration and demonstrates how to leverage the Kpow UI for working effectively with Avro schemas.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df945123_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In this tutorial, we will use the Community Edition of Kpow, where the default user has all the necessary permissions to complete the tasks. For those using the Kpow Enterprise Edition with user authorization enabled, the logged-in user must have the &lt;strong&gt;&lt;code&gt;SCHEMA_CREATE&lt;/code&gt;&lt;/strong&gt; permission for Role-Based Access Control or have &lt;strong&gt;&lt;code&gt;ALLOW_SCHEMA_CREATE=true&lt;/code&gt;&lt;/strong&gt; set for Simple Access Control. More information can be found in the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/authorization/overview/&quot;&gt;&lt;strong&gt;Kpow User Authorization documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We also assume that a Managed Kafka cluster has already been created, as detailed in the &lt;a href=&quot;https://factorhouse.io/blog/how-to/set-up-kpow-with-gcp/&quot;&gt;&lt;strong&gt;earlier setup guide&lt;/strong&gt;&lt;/a&gt;. This cluster will serve as the foundation for the configurations and operations covered in this tutorial.&lt;/p&gt;
&lt;h2 id=&quot;create-a-google-managed-schema-registry&quot;&gt;Create a Google Managed Schema Registry&lt;/h2&gt;
&lt;p&gt;We can create a schema registry using the &lt;strong&gt;&lt;code&gt;gcloud beta managed-kafka schema-registries create&lt;/code&gt;&lt;/strong&gt; command as shown below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; REGION&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;gcp-region&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;gcloud&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; beta&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; managed-kafka&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; schema-registries&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; demo_schema_registry&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --location=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$REGION&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the command completes, we can verify that the new registry, &lt;strong&gt;&lt;code&gt;demo_schema_registry&lt;/code&gt;&lt;/strong&gt; , is visible in the GCP Console under the Kafka services.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f275b913531d3090de35_schema-registry-gcp.png&quot; alt=&quot;Kpow Overview&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;set-up-a-client-vm&quot;&gt;Set up a client VM&lt;/h3&gt;
&lt;p&gt;The default service account used by the client VM is granted the following roles. While these roles provide Kpow with administrative access, user-level permissions can still be controlled using &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/authorization/overview/&quot;&gt;&lt;strong&gt;User Authorization&lt;/strong&gt;&lt;/a&gt; - an enterprise-only feature:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/iam/docs/roles-permissions/managedkafka#managedkafka.admin&quot;&gt;&lt;strong&gt;Managed Kafka Admin&lt;/strong&gt;&lt;/a&gt;: Grants full access to manage Kafka topics, configurations, and access controls in GCP’s managed Kafka environment.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/iam/docs/roles-permissions/managedkafka#managedkafka.schemaRegistryAdmin&quot;&gt;&lt;strong&gt;Schema Registry Admin&lt;/strong&gt;&lt;/a&gt;: Allows registering, evolving, and managing schemas and compatibility settings in the Schema Registry.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To connect to the Kafka cluster, Kpow must run on a machine with network access to it. In this setup, we use a Google Cloud Compute Engine VM that must be in the &lt;strong&gt;same region&lt;/strong&gt; , &lt;strong&gt;VPC&lt;/strong&gt; , and &lt;strong&gt;subnet&lt;/strong&gt; as the Kafka cluster. We also attach the &lt;strong&gt;&lt;code&gt;http-server&lt;/code&gt;&lt;/strong&gt; tag to allow HTTP traffic, enabling browser access to Kpow’s UI.&lt;/p&gt;
&lt;p&gt;We can create the client VM using the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;gcloud&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; compute&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; instances&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kafka-test-instance&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --scopes=https://www.googleapis.com/auth/cloud-platform&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --tags=http-server&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --subnet=projects/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$PROJECT_ID&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;/regions/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$REGION&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;/subnetworks/default&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --zone=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$REGION&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;-a&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;launch-a-kpow-instance&quot;&gt;Launch a Kpow Instance&lt;/h2&gt;
&lt;p&gt;Once our client VM is up and running, we’ll connect to it using the &lt;a href=&quot;https://cloud.google.com/compute/docs/connect/standard-ssh#console&quot;&gt;&lt;strong&gt;SSH-in-browser&lt;/strong&gt;&lt;/a&gt; tool provided by Google Cloud. After establishing the connection, the first step is to install Docker Engine, as Kpow will be launched using Docker. Refer to the official &lt;a href=&quot;https://docs.docker.com/engine/install/debian/&quot;&gt;&lt;strong&gt;installation&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.docker.com/engine/install/linux-postinstall/&quot;&gt;&lt;strong&gt;post-installation&lt;/strong&gt;&lt;/a&gt; guides for detailed instructions.&lt;/p&gt;
&lt;h3 id=&quot;preparing-kpow-configuration&quot;&gt;Preparing Kpow Configuration&lt;/h3&gt;
&lt;p&gt;To get Kpow running with a Google Cloud managed Kafka cluster and its schema registry, we prepare a configuration file (&lt;strong&gt;&lt;code&gt;gcp-trial.env&lt;/code&gt;&lt;/strong&gt;) that defines all necessary connection and authentication settings, as well as the Kpow license details.&lt;/p&gt;
&lt;p&gt;The configuration is divided into three main parts: Kafka cluster connection, schema registry integration, and license activation.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Kafka Cluster Configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ENVIRONMENT_NAME=GCP Kafka Cluster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;BOOTSTRAP=bootstrap.&amp;#x3C;cluster-id&gt;.&amp;#x3C;gcp-region&gt;.managedkafka.&amp;#x3C;gcp-project-id&gt;.cloud.goog:9092&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SECURITY_PROTOCOL=SASL_SSL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SASL_MECHANISM=OAUTHBEARER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SASL_LOGIN_CALLBACK_HANDLER_CLASS=com.google.cloud.hosted.kafka.auth.GcpLoginCallbackHandler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SASL_JAAS_CONFIG=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Schema Registry Configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SCHEMA_REGISTRY_NAME=GCP Schema Registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SCHEMA_REGISTRY_URL=https://managedkafka.googleapis.com/v1/projects/&amp;#x3C;gcp-project-id&gt;/locations/&amp;#x3C;gcp-region&gt;/schemaRegistries/&amp;#x3C;registry-id&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SCHEMA_REGISTRY_BEARER_AUTH_CUSTOM_PROVIDER_CLASS=com.google.cloud.hosted.kafka.auth.GcpBearerAuthCredentialProvider&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SCHEMA_REGISTRY_BEARER_AUTH_CREDENTIALS_SOURCE=CUSTOM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Your License Details&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;LICENSE_ID=&amp;#x3C;license-id&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;LICENSE_CODE=&amp;#x3C;license-code&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;LICENSEE=&amp;#x3C;licensee&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;LICENSE_EXPIRY=&amp;#x3C;license-expiry&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;LICENSE_SIGNATURE=&amp;#x3C;license-signature&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;strong&gt;Kafka Cluster Configuration&lt;/strong&gt; section, the &lt;strong&gt;&lt;code&gt;ENVIRONMENT_NAME&lt;/code&gt;&lt;/strong&gt; variable sets a friendly label visible in the Kpow user interface. The &lt;strong&gt;&lt;code&gt;BOOTSTRAP&lt;/code&gt;&lt;/strong&gt; variable specifies the Kafka bootstrap server address, incorporating the cluster ID, Google Cloud region, and project ID.&lt;/p&gt;
&lt;p&gt;Authentication and secure communication are handled via SASL over SSL using OAuth tokens. The &lt;strong&gt;&lt;code&gt;SASL_MECHANISM&lt;/code&gt;&lt;/strong&gt; is set to &lt;strong&gt;&lt;code&gt;OAUTHBEARER&lt;/code&gt;&lt;/strong&gt; , enabling OAuth-based authentication. The class &lt;strong&gt;&lt;code&gt;GcpLoginCallbackHandler&lt;/code&gt;&lt;/strong&gt; automatically manages OAuth tokens using the VM’s service account or a specified credentials file, simplifying token management and securing Kafka connections.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Schema Registry Configuration&lt;/strong&gt; section integrates Kpow with Google Cloud’s managed Schema Registry service. The &lt;strong&gt;&lt;code&gt;SCHEMA_REGISTRY_NAME&lt;/code&gt;&lt;/strong&gt; is a descriptive label for the registry in Kpow. The &lt;strong&gt;&lt;code&gt;SCHEMA_REGISTRY_URL&lt;/code&gt;&lt;/strong&gt; points to the REST API endpoint for the schema registry; placeholders must be replaced with the actual project ID, region, and registry ID.&lt;/p&gt;
&lt;p&gt;For authentication, Kpow uses Google’s &lt;strong&gt;&lt;code&gt;GcpBearerAuthCredentialProvider&lt;/code&gt;&lt;/strong&gt; to acquire OAuth2 tokens when accessing the schema registry API. Setting &lt;strong&gt;&lt;code&gt;SCHEMA_REGISTRY_BEARER_AUTH_CREDENTIALS_SOURCE&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;CUSTOM&lt;/code&gt;&lt;/strong&gt; tells Kpow to use this provider, allowing seamless and secure schema fetch and management with Google Cloud’s identity controls.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;strong&gt;License Details&lt;/strong&gt; section contains essential license parameters required to activate and run Kpow.&lt;/p&gt;
&lt;h3 id=&quot;launching-kpow&quot;&gt;Launching Kpow&lt;/h3&gt;
&lt;p&gt;Once the &lt;strong&gt;&lt;code&gt;gcp-trial.env&lt;/code&gt;&lt;/strong&gt; file is ready, we can launch Kpow using Docker. The command below pulls the latest Community Edition image, loads the environment config, and binds port &lt;strong&gt;3000&lt;/strong&gt; (Kpow UI) to port &lt;strong&gt;80&lt;/strong&gt; on the host VM. This allows us to access the Kpow UI directly in the browser at &lt;strong&gt;&lt;code&gt;http://&amp;lt;vm-external-ip&amp;gt;&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --pull=always&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 80:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env-file&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; gcp-trial.env&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse/kpow-ce:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f275b913531d3090de32_kpow-overview.png&quot; alt=&quot;Kpow Overview&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;schema-management&quot;&gt;Schema Management&lt;/h2&gt;
&lt;p&gt;With our environment up and running, we can use Kpow to create a new schema subject in the &lt;strong&gt;GCP Schema Registry&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;Schema&lt;/strong&gt; menu, click &lt;strong&gt;Create subject&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;Since we only have one registry configured, the &lt;strong&gt;GCP Schema Registry&lt;/strong&gt; is selected by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enter a subject name (e.g., &lt;strong&gt;&lt;code&gt;demo-gcp-value&lt;/code&gt;&lt;/strong&gt;), choose &lt;strong&gt;&lt;code&gt;AVRO&lt;/code&gt;&lt;/strong&gt; as the type, and provide a schema definition. Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f276b913531d3090de60_create-subject.png&quot; alt=&quot;Create Schema on GCP Registry&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once created, the new subject appears in the Schema menu within Kpow. This allows us to easily view, manage, and interact with the schema.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f275b913531d3090de38_view-subject.png&quot; alt=&quot;View Schema Records&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;working-with-avro-data&quot;&gt;Working with Avro Data&lt;/h2&gt;
&lt;p&gt;Next, we’ll produce and inspect an Avro record that is validated against the schema we just created.&lt;/p&gt;
&lt;p&gt;First, create a new topic named &lt;strong&gt;&lt;code&gt;demo-gcp&lt;/code&gt;&lt;/strong&gt; from the Kpow UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f276b913531d3090de5d_create-topic.png&quot; alt=&quot;Create Topic&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, to produce a record to the &lt;strong&gt;&lt;code&gt;demo-gcp&lt;/code&gt;&lt;/strong&gt; topic:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Data&lt;/strong&gt; menu, select the topic, and open the &lt;strong&gt;Produce&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;&lt;code&gt;String&lt;/code&gt;&lt;/strong&gt; as the &lt;strong&gt;Key Serializer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Value Serializer&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;AVRO&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;GCP Schema Registry&lt;/strong&gt; as the &lt;strong&gt;Schema Registry&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;&lt;code&gt;demo-gco-value&lt;/code&gt;&lt;/strong&gt; subject.&lt;/li&gt;
&lt;li&gt;Enter key/value data and click &lt;strong&gt;Produce&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f275b913531d3090de3c_produce-record.png&quot; alt=&quot;Produce Records on GCP Registry&quot;&gt;&lt;/p&gt;
&lt;p&gt;To see the result, navigate back to the &lt;strong&gt;Inspect&lt;/strong&gt; tab and select the &lt;strong&gt;&lt;code&gt;demo-gcp&lt;/code&gt;&lt;/strong&gt; topic. In the deserializer options, choose &lt;strong&gt;String&lt;/strong&gt; as the &lt;strong&gt;Key deserializer&lt;/strong&gt; and &lt;strong&gt;AVRO&lt;/strong&gt; as the &lt;strong&gt;Value deserializer&lt;/strong&gt; , then select &lt;strong&gt;GCP Schema Registry&lt;/strong&gt;. Kpow automatically fetches the correct schema version, deserializes the binary Avro message, and presents the data as easy-to-read JSON.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tip: Kpow 94.3 introduces automatic deserialization of keys and values. For users unfamiliar with a topic’s data format, selecting Auto lets Kpow attempt to infer and deserialize the records automatically as they are consumed.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f275b913531d3090de3f_inspect-record.png&quot; alt=&quot;Inspect Records on GCP Registry&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating Kpow with Google Cloud’s Managed Schema Registry consolidates our entire Kafka management workflow into a single, powerful platform. By following this guide, we have seen how to configure Kpow to securely connect to both GCP Managed Kafka and the Schema Registry using native OAuth authentication, completely removing the need for manual token handling.&lt;/p&gt;
&lt;p&gt;The result is a seamless, end-to-end experience where we can create and manage schemas, produce and consume schema-validated data, and inspect the records—all from the Kpow UI. This powerful combination streamlines development, enhances data governance, and empowers engineering teams to fully leverage Google Cloud’s managed Kafka services.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Integrate Kpow with Bufstream</title><link>https://factorhouse.io/articles/integrate-kpow-with-bufstream/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-bufstream/</guid><description>Integrate Kpow with Bufstream in minutes. Gain unified visibility and control over your Kafka-compatible broker and Buf Schema Registry through our market-leading engineering console.</description><pubDate>Mon, 23 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://buf.build/product/bufstream&quot;&gt;Bufstream&lt;/a&gt; is a cloud-native, Kafka-compatible streaming solution designed to drop seamlessly into existing Kafka architectures while providing advanced, native schema management. However, as your real-time data pipelines scale, maintaining deep observability and control over your topics, schemas, and consumer groups remains essential.&lt;/p&gt;
&lt;p&gt;Kpow serves as the perfect engineering toolkit for this environment. Fully compatible with &lt;strong&gt;Bufstream&lt;/strong&gt; out of the box, Kpow connects directly to your Bufstream brokers and Confluent-compatible Schema Registry using standard Kafka protocols. This delivers a unified, single-pane-of-glass experience without requiring proprietary plugins, sidecars, or complex custom configurations.&lt;/p&gt;
&lt;p&gt;Kpow connects natively to a wide range of Kafka vendors and managed service providers. See our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider&quot;&gt;Kafka Providers documentation&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow this guide and connect Kpow to a local Bufstream environment, you must have the following ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker:&lt;/strong&gt; Installed and running on your host machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to test Kpow with Bufstream is by spinning up a local development environment using Docker. We will create a shared Docker network, launch a single-node Bufstream broker (running in memory), and then connect Kpow to both the local broker and Buf’s public remote demo registry.&lt;/p&gt;
&lt;p&gt;Run the following commands in your terminal. Be sure to replace the license placeholders with your actual Kpow license details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 1. Create a dedicated Docker network&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 2. Start a Bufstream broker (in-memory mode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 9092:9092&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; bufstream&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BUFSTREAM_KAFKA_HOST=&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BUFSTREAM_KAFKA_PUBLIC_HOST=&quot;bufstream&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BUFSTREAM_KAFKA_PUBLIC_PORT=&quot;9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  bufbuild/bufstream:latest&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; serve&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --inmemory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 3. Start Kpow and connect it to Bufstream&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;Bufstream&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;bufstream:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Buf Schema Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;https://demo.buf.dev/integrations/confluent/bufstream-demo&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the containers are running, navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; to access the Kpow UI. Kpow has automatically discovered and connected to your local Bufstream broker and Buf’s public demo registry.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb474eb3949264f831dfb0_kpow-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Bufstream seamlessly integrates with the Buf Schema Registry, which provides a Confluent-compatible API endpoint.&lt;/p&gt;
&lt;p&gt;To manage your schemas directly within Kpow, simply provide the URL of the Buf Schema Registry. In our local Docker example, we connected to Buf’s public remote demo registry.&lt;/p&gt;
&lt;p&gt;As demonstrated in the Quick Start command, you connect the registry by appending the following environment variables to your deployment configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Buf Schema Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;https://demo.buf.dev/integrations/confluent/bufstream-demo&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once connected, you can use Kpow’s intuitive UI to explore subjects, manage schema versions, and automatically deserialize schema-governed topic data (such as Protobuf) without writing any custom code.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters running alongside your streaming infrastructure, we recommend using our official Helm Charts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your Bufstream infrastructure. By using standard Kafka protocols, you can unify your Kafka-compatible brokers and Schema Registry environments in minutes, empowering developers to build and debug real-time pipelines effortlessly.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Bufstream integration, reach out to our engineering support team at&lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Integrate Kpow with the Redpanda Streaming Platform</title><link>https://factorhouse.io/articles/integrate-kpow-with-redpanda/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-kpow-with-redpanda/</guid><description>Integrate Kpow with Redpanda in minutes. Gain unified visibility and control over your Redpanda brokers and built-in Schema Registry through our market-leading engineering console.</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.redpanda.com/&quot;&gt;Redpanda&lt;/a&gt; offers a simple, powerful, and Kafka®-compatible streaming data platform. However, as your real-time data pipelines scale, maintaining deep observability and control over your topics, schemas, and consumer groups becomes essential.&lt;/p&gt;
&lt;p&gt;Kpow serves as the perfect engineering toolkit for this environment. Fully compatible with &lt;strong&gt;Redpanda&lt;/strong&gt; out of the box, Kpow connects directly to your Redpanda brokers and its built-in Confluent-compatible Schema Registry using standard Kafka protocols. This delivers a unified, single-pane-of-glass experience without requiring proprietary plugins, sidecars, or complex custom configurations.&lt;/p&gt;
&lt;p&gt;Kpow connects natively to a wide range of Kafka vendors and managed service providers. See our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider&quot;&gt;Kafka Providers documentation&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To follow this guide and connect Kpow to a local Redpanda environment, you must have the following ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker:&lt;/strong&gt; Installed and running on your host machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to test Kpow with Redpanda is by spinning up a local development environment using Docker. We will create a shared Docker network, launch a single-node Redpanda cluster with its built-in Schema Registry enabled, and then launch Kpow.&lt;/p&gt;
&lt;p&gt;Run the following commands in your terminal. Be sure to replace the license placeholders with your actual Kpow license details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 1. Create a dedicated Docker network&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 2. Start a Redpanda broker with the built-in Schema Registry enabled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 19092:19092&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 18081:18081&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; redpanda&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --hostname&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; redpanda&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  redpandadata/redpanda:latest&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; redpanda&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --kafka-addr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; internal://0.0.0.0:9092,external://0.0.0.0:19092&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --advertise-kafka-addr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; internal://redpanda:9092,external://localhost:19092&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --schema-registry-addr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; internal://0.0.0.0:8081,external://0.0.0.0:18081&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --rpc-addr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; redpanda:33145&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --advertise-rpc-addr&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; redpanda:33145&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    --mode&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; dev-container&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## 3. Start Kpow and connect it to Redpanda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; kpow&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; --network&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;Local Redpanda Cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;redpanda:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Local Redpanda Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;http://redpanda:8081&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the containers are running, navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; to access the Kpow UI. Kpow will automatically discover and connect to your Redpanda broker and Schema Registry.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb4594d9b5a115a9bd15c8_kpow-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Redpanda features a built-in Schema Registry that is fully compatible with the Confluent REST API.&lt;/p&gt;
&lt;p&gt;To manage your schemas directly within Kpow, simply provide the URL of Redpanda’s integrated schema registry. In our local Docker example, this is exposed on port &lt;code&gt;8081&lt;/code&gt; within the Docker network.&lt;/p&gt;
&lt;p&gt;As demonstrated in the Quick Start command, you connect the registry by appending the following environment variables to your deployment configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;Local Redpanda Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;http://redpanda:8081&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once connected, you can use Kpow’s intuitive UI to create subjects, manage schema versions, and automatically deserialize schema-governed topic data (like Avro or Protobuf) without writing any custom code.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment (whether against a self-hosted Redpanda cluster or Redpanda Cloud), we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters running alongside your Redpanda infrastructure, we recommend using our official Helm Charts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your Redpanda infrastructure. By using standard Kafka protocols, you can unify your Redpanda clusters and Schema Registry environments in minutes, empowering developers to build and debug real-time pipelines effortlessly.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Redpanda integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Integrate Confluent-compatible schema registries with Kpow</title><link>https://factorhouse.io/articles/integrate-confluent-compatible-registries-kpow/</link><guid isPermaLink="true">https://factorhouse.io/articles/integrate-confluent-compatible-registries-kpow/</guid><description>This guide demonstrates how to address the operational complexity of managing multiple Kafka schema registries. We integrate Confluent-compatible registries—Confluent Schema Registry, Apicurio Registry, and Karapace—and manage them all through a single pane of glass using Kpow.</description><pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In modern data architectures built on Apache Kafka, a Schema Registry is an essential component for enforcing data contracts and supporting strong data governance. While the &lt;a href=&quot;https://docs.confluent.io/platform/current/schema-registry/index.html&quot;&gt;&lt;strong&gt;Confluent Schema Registry&lt;/strong&gt;&lt;/a&gt; set the original standard, the ecosystem has expanded to include powerful &lt;strong&gt;Confluent-compatible&lt;/strong&gt; alternatives such as Red Hat’s &lt;a href=&quot;https://www.apicur.io/registry/&quot;&gt;&lt;strong&gt;Apicurio Registry&lt;/strong&gt;&lt;/a&gt; and Aiven’s &lt;a href=&quot;https://www.karapace.io/&quot;&gt;&lt;strong&gt;Karapace&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Whether driven by a gradual migration, the need to support autonomous teams, or simply technology evaluation, many organizations find themselves running multiple schema registries in parallel. This inevitably leads to operational complexity and a fragmented view of their data governance.&lt;/p&gt;
&lt;p&gt;This guide demonstrates how Kpow directly solves this challenge. We will integrate these popular schema registries into a single Kafka environment and show how to manage them all seamlessly through Kpow’s single, unified interface.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df945123_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To create subjects in Kpow, the logged-in user must have the necessary permissions. If Role-Based Access Control (RBAC) is enabled, this requires the &lt;strong&gt;SCHEMA_CREATE&lt;/strong&gt; action. For Simple Access Control, set &lt;strong&gt;&lt;code&gt;ALLOW_SCHEMA_CREATE=true&lt;/code&gt;&lt;/strong&gt;. For details, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/authorization/overview/&quot;&gt;&lt;strong&gt;Kpow User Authorization docs&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;launch-kafka-environment&quot;&gt;Launch Kafka Environment&lt;/h2&gt;
&lt;p&gt;To accelerate the setup, we will use the &lt;a href=&quot;https://github.com/factorhouse/factorhouse-local&quot;&gt;&lt;strong&gt;Factor House Local&lt;/strong&gt;&lt;/a&gt; repository, which provides a solid foundation with pre-built configurations for authentication and authorization.&lt;/p&gt;
&lt;p&gt;First, clone the repository:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git clone https://github.com/factorhouse/factorhouse-local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, navigate into the project root and create a Docker Compose file named &lt;strong&gt;&lt;code&gt;compose-kpow-multi-registries.yml&lt;/code&gt;&lt;/strong&gt;. This file defines our entire stack: a 3-broker Kafka cluster, our three schema registries, and Kpow.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  schema&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;confluentinc/cp-schema-registry:7.8.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    container_name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;schema_registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;8081:8081&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    networks&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    depends_on&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;zookeeper&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_HOST_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;schema&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_LISTENERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;http://schema:8081,http://${DOCKER_HOST_IP:-127.0.0.1}:8081&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;kafka-1:19092,kafka-2:19093,kafka-3:19094&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_AUTHENTICATION_METHOD&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;BASIC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_AUTHENTICATION_REALM&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_AUTHENTICATION_ROLES&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;schema-admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      SCHEMA_REGISTRY_OPTS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;-Djava.security.auth.login.config=/etc/schema/schema_jaas.conf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    volumes&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;./resources/kpow/schema:/etc/schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  apicurio&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;apicurio/apicurio-registry:3.0.9&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    container_name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;apicurio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;8080:8080&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    networks&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_KAFKASQL_BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-1:19092,kafka-2:19093,kafka-3:19094&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_STORAGE_KIND&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafkasql&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_AUTH_ENABLED&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_AUTH_ROLE_BASED_AUTHORIZATION&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_AUTH_STATIC_USERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;admin=admin&quot;&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; # Format: user1=pass1,user2=pass2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      APICURIO_AUTH_STATIC_ROLES&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;admin:sr-admin&quot;&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; # Format: user:role,user2:role2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  karapace&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ghcr.io/aiven-open/karapace:develop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    container_name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    entrypoint&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;python3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;-m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;8082:8081&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    networks&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    depends_on&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;zookeeper&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_KARAPACE_REGISTRY&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_ADVERTISED_HOSTNAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_ADVERTISED_PROTOCOL&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;http&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_BOOTSTRAP_URI&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kafka-1:19092,kafka-2:19093,kafka-3:19094&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_PORT&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;8081&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_HOST&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;0.0.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_CLIENT_ID&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace-0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_GROUP_ID&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_MASTER_ELECTION_STRATEGY&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;highest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_MASTER_ELIGIBILITY&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;_karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;      KARAPACE_COMPATIBILITY&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;BACKWARD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;  kpow&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    container_name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;kpow-ee&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    pull_policy&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;always&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    restart&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;always&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;3000:3000&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    networks&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;factorhouse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    depends_on&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;apicurio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;karapace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    env_file&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;resources/kpow/config/multi-registry.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;```&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;javascript&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## AauthN + AuthZ&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;JAVA_TOOL_OPTIONS=&quot;-Djava.awt.headless=true -Djava.security.auth.login.config=/etc/kpow/jaas/hash-jaas.conf&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;AUTH_PROVIDER_TYPE=jetty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;RBAC_CONFIGURATION_FILE=/etc/kpow/rbac/hash-rbac.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## Kafka environments&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;ENVIRONMENT_NAME=Multi-registry Integration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;BOOTSTRAP=kafka-1:19092,kafka-2:19093,kafka-3:19094&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;SCHEMA_REGISTRY_RESOURCE_IDS=CONFLUENT,APICURIO,KARAPACE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;CONFLUENT_SCHEMA_REGISTRY_URL=http://schema:8081&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;CONFLUENT_SCHEMA_REGISTRY_AUTH=USER_INFO&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;CONFLUENT_SCHEMA_REGISTRY_USER=admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;CONFLUENT_SCHEMA_REGISTRY_PASSWORD=admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;APICURIO_SCHEMA_REGISTRY_URL=http://apicurio:8080/apis/ccompat/v7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;APICURIO_SCHEMA_REGISTRY_AUTH=USER_INFO&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;APICURIO_SCHEMA_REGISTRY_USER=admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;APICURIO_SCHEMA_REGISTRY_PASSWORD=admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;KARAPACE_SCHEMA_REGISTRY_URL=http://karapace:8081&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - ${KPOW_TRIAL_LICENSE:-resources/kpow/config/trial-license.env}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mem_limit: 2G&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;volumes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - ./resources/kpow/jaas:/etc/kpow/jaas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - ./resources/kpow/rbac:/etc/kpow/rbac&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;zookeeper:
image: confluentinc/cp-zookeeper:7.8.0
container_name: zookeeper
ports:
- “2181:2181”
networks:
- factorhouse
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000&lt;/p&gt;
&lt;p&gt;kafka-1:
image: confluentinc/cp-kafka:7.8.0
container_name: kafka-1
ports:
- “9092:9092”
networks:
- factorhouse
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka-1:19092,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: “false”
KAFKA_LOG4J_ROOT_LOGLEVEL: INFO
KAFKA_NUM_PARTITIONS: “3”
KAFKA_DEFAULT_REPLICATION_FACTOR: “3”
depends_on:
- zookeeper&lt;/p&gt;
&lt;p&gt;kafka-2:
image: confluentinc/cp-kafka:7.8.0
container_name: kafka-2
ports:
- “9093:9093”
networks:
- factorhouse
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka-2:19093,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9093
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: “false”
KAFKA_LOG4J_ROOT_LOGLEVEL: INFO
KAFKA_NUM_PARTITIONS: “3”
KAFKA_DEFAULT_REPLICATION_FACTOR: “3”
depends_on:
- zookeeper&lt;/p&gt;
&lt;p&gt;kafka-3:
image: confluentinc/cp-kafka:7.8.0
container_name: kafka-3
ports:
- “9094:9094”
networks:
- factorhouse
environment:
KAFKA_BROKER_ID: 3
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka-3:19094,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: “false”
KAFKA_LOG4J_ROOT_LOGLEVEL: INFO
KAFKA_NUM_PARTITIONS: “3”
KAFKA_DEFAULT_REPLICATION_FACTOR: “3”
depends_on:
- zookeeper&lt;/p&gt;
&lt;p&gt;networks:
factorhouse:
name: factorhouse&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Here&apos;s an overview of the three schema registries and Kpow:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * Confluent Schema Registry (**`schema`**)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Image** : **`confluentinc/cp-schema-registry:7.8.0`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Storage** : It uses the connected Kafka cluster for durable storage, persisting schemas in an internal topic (named **`_schemas`** by default).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Security** : This service is secured using **`BASIC`** HTTP authentication. Access requires a valid username and password, which are defined in the **`schema_jaas.conf`** file mounted via the **`volumes`** directive.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * Apicurio Registry (**`apicurio`**)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Image** : **`apicurio/apicurio-registry:3.0.9`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Storage** : It&apos;s configured to use the **`kafkasql`** storage backend, and schemas are stored in a Kafka topic (**`kafkasql-journal`**).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Security** : Authentication is enabled and managed directly through environment variables. This setup creates a static user (**`admin`** with password **`admin`**) and grants it administrative privileges.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **API Endpoint** : To align with the Kafka environment, we&apos;ll use **`/apis/ccompat/v7`** as the Confluent Compatibility API endpoint.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * Karapace Registry (**`karapace`**)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Image** : **`ghcr.io/aiven-open/karapace:develop`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Storage** : Like the others, it uses a Kafka topic (**`_karapace`**) to store its schema data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Security** : For simplicity, authentication is not configured, leaving the API openly accessible on the network. However, the logged-in Kpow user must still have the appropriate permissions to manage schema resources—highlighting one of the key access control benefits Kpow offers in enterprise environments.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * Kpow (**`kpow`**)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Image** : **`factorhouse/kpow:latest`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Host Port** : 3000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Configuration** :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      * **`env_file`** : Its primary configuration is loaded from external files. The **`multi-registry.env`** file is crucial, as it contains the connection details for the Kafka cluster and all three schema registries.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      * **Licensing** : The configuration also loads a license file. It uses a local **`trial-license.env`** by default, but this can be overridden by setting the **`KPOW_TRIAL_LICENSE`** environment variable to a different file path.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    * **Volumes** :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      * **`./resources/kpow/jaas`** : This mounts authentication configuration (JAAS file) into Kpow.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      * **`./resources/kpow/rbac`** : This mounts Role-Based Access Control (RBAC) file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;We also need to create the Kpow configuration file (**`resources/kpow/config/multi-registry.env`**). The environment variables in this file configures Kpow&apos;s own user security, the connection to the Kafka cluster, and the integration with all three schema registries.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;aauthn--authz&quot;&gt;AauthN + AuthZ&lt;/h2&gt;
&lt;p&gt;JAVA_TOOL_OPTIONS=“-Djava.awt.headless=true -Djava.security.auth.login.config=/etc/kpow/jaas/hash-jaas.conf”
AUTH_PROVIDER_TYPE=jetty
RBAC_CONFIGURATION_FILE=/etc/kpow/rbac/hash-rbac.yml&lt;/p&gt;
&lt;h2 id=&quot;kafka-environments&quot;&gt;Kafka environments&lt;/h2&gt;
&lt;p&gt;ENVIRONMENT_NAME=Multi-registry Integration
BOOTSTRAP=kafka-1:19092,kafka-2:19093,kafka-3:19094&lt;/p&gt;
&lt;p&gt;SCHEMA_REGISTRY_RESOURCE_IDS=CONFLUENT,APICURIO,KARAPACE&lt;/p&gt;
&lt;p&gt;CONFLUENT_SCHEMA_REGISTRY_URL=&lt;a href=&quot;http://schema:8081&quot;&gt;http://schema:8081&lt;/a&gt;
CONFLUENT_SCHEMA_REGISTRY_AUTH=USER_INFO
CONFLUENT_SCHEMA_REGISTRY_USER=admin
CONFLUENT_SCHEMA_REGISTRY_PASSWORD=admin&lt;/p&gt;
&lt;p&gt;APICURIO_SCHEMA_REGISTRY_URL=&lt;a href=&quot;http://apicurio:8080/apis/ccompat/v7&quot;&gt;http://apicurio:8080/apis/ccompat/v7&lt;/a&gt;
APICURIO_SCHEMA_REGISTRY_AUTH=USER_INFO
APICURIO_SCHEMA_REGISTRY_USER=admin
APICURIO_SCHEMA_REGISTRY_PASSWORD=admin&lt;/p&gt;
&lt;p&gt;KARAPACE_SCHEMA_REGISTRY_URL=&lt;a href=&quot;http://karapace:8081&quot;&gt;http://karapace:8081&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;We can start all services in the background using the Docker Compose file:&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;docker compose -f ./compose-kpow-multi-registries.yml up -d&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Once the containers are running, navigate to **`http://localhost:3000`** to access the Kpow UI (**`admin`** as both username and password). In the left-hand navigation menu under **Schema** , you will see all three registries - CONFLUENT, APICURIO, and KARAPACE.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Registries](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbc5_registries.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Unified Schema Management&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Now, we will create a schema subject in each registry directly from Kpow.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  1. In the **Schema** menu, click **Create subject**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  2. Select **CONFLUENT** from the **Registry** dropdown.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  3. Enter a subject name (e.g., **`demo-confluent-value`**), choose **`AVRO`** as the type, and provide a schema definition. Click **Create**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Subject: **demo-confluent-value**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Create Schema on Confluent Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbc2_create-subject-01.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Following the same pattern, create subjects for the other two registries:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * **Apicurio** : Select **`APICURIO`** and create the **`demo-apicurio-value`** subject.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * **Karapace** : Select **`KARAPACE`** and create the **`demo-karapace-value`** subject.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Subject: **demo-apicurio-value**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Create Schema on Apicurio Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbd7_create-subject-02.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Subject: **demo-karapace-value**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Create Schema on Karapace Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57f48153bdc4457dbe0_create-subject-03.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Each registry persists its schemas in an internal Kafka topic. We can verify this in Kpow&apos;s **Data** tab by inspecting the contents of their respective storage topics:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * **CONFLUENT** : **`_schemas`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * **APICURIO** : **`kafkasql-journal`** (the default topic for its **`kafkasql`** storage engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  * **KARAPACE** : **`_karapace`**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Inspect Schema Records](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57f48153bdc4457dbf1_schema-records.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Produce and Inspect Records Across All Registries&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Finally, we&apos;ll produce and inspect Avro records, leveraging the schemas from each registry.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;First, create the topics **`demo-confluent`** , **`demo-apicurio`** , and **`demo-karapace`** in Kpow.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Create Topic](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbc8_produce-records-01.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;To produce a record for the **`demo-confluent`** topic:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  1. Go to the **Data** menu, select the topic, and open the **Produce** tab.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  2. Select **`String`** as the **Key Serializer**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  3. Set the **Value Serializer** to **`AVRO`**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  4. Choose **CONFLUENT** as the **Schema Registry**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  5. Select the **`demo-confluent-value`** subject.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  6. Enter key/value data and click **Produce**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-confluent**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Product Records on Confluent Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57f48153bdc4457dc0a_produce-records-02.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Repeat this for the other topics, making sure to select the corresponding registry and subject for **`demo-apicurio`** and **`demo-karapace`**.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-apicurio**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Product Records on Apicurio Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbd4_produce-records-03.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-karapace**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Product Records on Karapace Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbda_produce-records-04.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;To inspect the records, navigate back to the **Data** tab for each topic. Select the correct **Schema Registry** in the deserializer options. Kpow will automatically fetch the correct schema, deserialize the binary Avro data, and present it as human-readable JSON.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-confluent**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Inspect Records on Confluent Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbbf_inspect-records-01.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-apicurio**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Inspect Records on Apicurio Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57f48153bdc4457dbe3_inspect-records-02.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Topic: **demo-karapace**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;![Inspect Records on Karapace Registry](https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8f57e48153bdc4457dbcd_inspect-records-03.png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Conclusion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;This guide has demonstrated that managing a heterogeneous, multi-registry Kafka environment does not have to be a fragmented or complex task. By leveraging the Confluent-compatible APIs of Apicurio and Karapace, we can successfully integrate them alongside the standard Confluent Schema Registry.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;With Kpow providing a single pane of glass, we gain centralized control and visibility over all our schema resources. This unified approach simplifies critical operations like schema management, data production, and inspection, empowering teams to use the best tool for their needs without sacrificing governance or operational efficiency.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Set Up Kpow with Google Cloud Managed Service for Apache Kafka</title><link>https://factorhouse.io/articles/set-up-kpow-with-gcp/</link><guid isPermaLink="true">https://factorhouse.io/articles/set-up-kpow-with-gcp/</guid><description>Integrate Kpow with Google Cloud Managed Service for Apache Kafka (MSAK) in minutes. Gain unified visibility and control over your managed Kafka brokers and Schema Registry through our market-leading engineering console.</description><pubDate>Sun, 25 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Managing Apache Kafka at scale requires robust visibility and control. Kpow delivers exactly that as a comprehensive engineering console, offering a single pane of glass for your streaming infrastructure.&lt;/p&gt;
&lt;p&gt;Kpow is fully compatible with &lt;a href=&quot;https://cloud.google.com/products/managed-service-for-apache-kafka&quot;&gt;&lt;strong&gt;Google Cloud Managed Service for Apache Kafka (MSAK)&lt;/strong&gt;&lt;/a&gt; out of the box. Because Kpow uses standard Kafka protocols, it integrates seamlessly with your GCP cluster without requiring proprietary plugins, external agents, or complex custom configurations.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to Google Cloud MSAK, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running Google Cloud MSAK Cluster:&lt;/strong&gt; Reachable from the host where you intend to run Kpow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Managed Schema Registry (Optional):&lt;/strong&gt; Provisioned within the same GCP region if you intend to manage schemas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network reachability:&lt;/strong&gt; The Compute Engine VM or GKE cluster running Kpow must reside in the same VPC and subnet as the Kafka cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; The compute instance must have a Service Account attached with the &lt;code&gt;Managed Kafka Admin&lt;/code&gt; IAM role (and optionally the &lt;code&gt;Schema Registry Admin&lt;/code&gt; role). Since Kpow acts as a centralized management and monitoring tool, granting it these broad infrastructure permissions allows it to seamlessly fetch OAuth tokens and interact with your resources. You can then enforce strict, fine-grained access control for your individual engineers directly within Kpow using its built-in &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/role-based-access-control&quot;&gt;Role Based Access Control&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Details:&lt;/strong&gt; Your Bootstrap Server address (and optionally, your Schema Registry URL).&lt;strong&gt;‍&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to Google Cloud is using Docker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; To use Google MSAK with Kpow, you &lt;strong&gt;must&lt;/strong&gt; use the GCP-specific build of Kpow (e.g., &lt;code&gt;factorhouse/kpow:95.3-temurin-ubi&lt;/code&gt;). Due to a &lt;a href=&quot;https://github.com/googleapis/managedkafka/issues/51&quot;&gt;known bug&lt;/a&gt; in Google’s &lt;code&gt;GcpBearerAuthCredentialProvider&lt;/code&gt; that disallows multiple credential providers on the classpath, GCP functionality is provided in a separate Docker image until Google resolves the issue. Ensure you replace &lt;code&gt;95.3&lt;/code&gt; with the current Kpow release version.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific cluster details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ENVIRONMENT_NAME=&quot;GCP Kafka Cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;bootstrap.&amp;#x3C;cluster-id&gt;.&amp;#x3C;gcp-region&gt;.managedkafka.&amp;#x3C;gcp-project-id&gt;.cloud.goog:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;OAUTHBEARER&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_LOGIN_CALLBACK_HANDLER_CLASS=&quot;com.google.cloud.hosted.kafka.auth.GcpLoginCallbackHandler&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&quot;org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:95.3-temurin-ubi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, open a browser and navigate to &lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000&lt;/a&gt;. You will immediately see your Google Cloud topics, consumer groups, and brokers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb3fd16e53e451aab14398_kpow-overview.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;integrating-the-managed-schema-registry-optional&quot;&gt;Integrating the Managed Schema Registry (Optional)&lt;/h3&gt;
&lt;p&gt;To manage your schemas directly within Kpow, you can integrate Google Cloud’s Managed Schema Registry by adding the following environment variables to your deployment command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_NAME=&quot;GCP Schema Registry&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_URL=&quot;https://managedkafka.googleapis.com/v1/projects/&amp;#x3C;gcp-project-id&gt;/locations/&amp;#x3C;gcp-region&gt;/schemaRegistries/&amp;#x3C;registry-id&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_BEARER_AUTH_CUSTOM_PROVIDER_CLASS=&quot;com.google.cloud.hosted.kafka.auth.GcpBearerAuthCredentialProvider&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SCHEMA_REGISTRY_BEARER_AUTH_CREDENTIALS_SOURCE=&quot;CUSTOM&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;Connecting to Google Cloud MSAK requires specific authentication settings to integrate smoothly with Google IAM.&lt;/p&gt;
&lt;h3 id=&quot;cluster-authentication&quot;&gt;Cluster Authentication&lt;/h3&gt;
&lt;p&gt;Kpow supports connecting to Google Cloud via both OAUTHBEARER (recommended) and SASL/PLAIN.&lt;/p&gt;
&lt;p&gt;By configuring SASL_MECHANISM=OAUTHBEARER and setting the &lt;code&gt;GcpLoginCallbackHandler&lt;/code&gt;, Kpow will automatically and securely retrieve short-lived OAuth tokens from the Google Cloud metadata server using the IAM Service Account attached to the host VM.&lt;/p&gt;
&lt;h3 id=&quot;access-control&quot;&gt;Access Control&lt;/h3&gt;
&lt;p&gt;Google Cloud MSAK relies on two levels of access control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google Cloud IAM Roles:&lt;/strong&gt; These roles (like &lt;code&gt;Managed Kafka Admin&lt;/code&gt;) authorize Kpow to connect to the cluster via Google Cloud APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Kafka ACLs:&lt;/strong&gt; For more granular control over access to specific resources within a cluster, such as topics and consumer groups, you must configure native Kafka ACLs. Kpow provides robust support for managing these ACLs directly within its UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a comprehensive list of configuration options, limitations, and detailed IAM policy examples, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/google-msak/kafka-cluster&quot;&gt;Google MSAK Kafka cluster documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Kpow connects seamlessly to the wider Google Cloud streaming ecosystem, though there are specific limitations to be aware of.&lt;/p&gt;
&lt;h3 id=&quot;google-managed-schema-registry&quot;&gt;Google Managed Schema Registry&lt;/h3&gt;
&lt;p&gt;Kpow connects to the Google Managed Schema Registry using a custom bearer token provider (GcpBearerAuthCredentialProvider). The Google registry implements the Confluent REST API, ensuring compatibility with Kpow’s schema interface.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Supported Formats:&lt;/strong&gt; The managed registry currently supports Apache Avro and Protocol Buffers (Protobuf). JSON schemas are not supported by the Google API at this time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For detailed access control and connection options, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/google-msak/schema-registry&quot;&gt;Google MSAK Schema Registry documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;google-managed-kafka-connect&quot;&gt;Google Managed Kafka Connect&lt;/h3&gt;
&lt;p&gt;Google Managed Kafka Connect is currently in Preview. At this time, Google has wrapped the functionality into its own proprietary Google Cloud API rather than exposing the standard Apache Kafka Connect REST API (which typically runs on port 8083).&lt;/p&gt;
&lt;p&gt;Because the standard REST endpoint is not exposed, Kpow cannot currently integrate with it. Integration with Google Managed Kafka Connect will be addressed in a future update.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Google Kubernetes Engine (GKE) clusters running alongside your MSAK instances, we recommend using our official Helm Charts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;&lt;strong&gt;Guide: Installing Kpow with Helm&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Google Compute Engine (GCE) Virtual Machine, you can download the Kpow JAR file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;&lt;strong&gt;Kpow JAR Quickstart&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your Google Cloud Managed Service for Apache Kafka infrastructure. By using standard Kafka protocols and direct IAM integrations, you can unify your Kafka clusters and Schema Registry environments in minutes.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Google Cloud integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;related-content&quot;&gt;Related Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-aws&quot;&gt;Set Up Kpow with Amazon Managed Streaming for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Set Up Kpow with Confluent Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/integrate-kpow-with-oci-streaming&quot;&gt;How to Integrate Kpow with OCI Streaming with Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-instaclustr&quot;&gt;Set Up Kpow with NetApp Instaclustr Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Manage Kafka Consumer Offsets with Kpow</title><link>https://factorhouse.io/articles/manage-kafka-consumer-offsets-with-kpow/</link><guid isPermaLink="true">https://factorhouse.io/articles/manage-kafka-consumer-offsets-with-kpow/</guid><description>Kpow version 94.2 enhances consumer group management capabilities, providing greater control and visibility into Kafka consumption. This article provides a step-by-step guide on how to manage consumer offsets in Kpow.</description><pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;In Apache Kafka, offset management actions such as &lt;strong&gt;clearing&lt;/strong&gt; , &lt;strong&gt;resetting&lt;/strong&gt; , and &lt;strong&gt;skipping&lt;/strong&gt; play a vital role in controlling consumer group behavior.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resetting offsets&lt;/strong&gt; enables consumers to reprocess messages from a specific point in time or offset - commonly used during recovery, testing, or reprocessing scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping offsets&lt;/strong&gt; allows consumers to move past specific records, such as malformed or corrupted records, without disrupting the entire processing flow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clearing offsets&lt;/strong&gt; removes committed offsets for a consumer group or member, effectively resetting its consumption history and causing it to re-consume.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these capabilities are essential tools for maintaining data integrity, troubleshooting issues, and ensuring robust and flexible stream processing.&lt;/p&gt;
&lt;p&gt;Kpow version 94.2 enhances consumer group management capabilities, providing greater control and visibility into Kafka consumption. This article provides a step-by-step guide on how to manage consumer offsets in Kpow. We will walk through these features using simple Kafka producer and consumer clients.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;&lt;strong&gt;Apache Kafka®&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;&lt;strong&gt;Apache Flink®&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8eaf5c5f7cdb2df945123_kpow-hero-data.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To manage consumer group offsets in Kpow, the logged-in user must have appropriate permissions, including the &lt;strong&gt;GROUP_EDIT&lt;/strong&gt; action. For more information, refer to the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/authorization/overview/&quot;&gt;&lt;strong&gt;User Authorization&lt;/strong&gt;&lt;/a&gt; section of the Kpow documentation.&lt;/p&gt;
&lt;p&gt;Also, if your Kafka cluster has ACLs enabled, the Kafka user specified in Kpow’s cluster connection must have the necessary permissions for Kpow to operate correctly. You can find the full list of required permissions in the &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/installation/minimum-acl-permissions/&quot;&gt;&lt;strong&gt;Minimum ACL Permissions&lt;/strong&gt;&lt;/a&gt; guide.&lt;/p&gt;
&lt;h2 id=&quot;development-environment&quot;&gt;Development Environment&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;## clone examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ git clone &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;https&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//github.com/factorhouse/examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ cd examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;## install python packages &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; a virtual environment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ python &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;m venv venv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ source venv&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;bin&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ pip install &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;r features&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;offset&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;management&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;requirements.txt &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;## clone factorhouse&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ git clone &lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;https&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;//github.com/factorhouse/factorhouse-local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;## show key files and folders&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;$ tree &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;L&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; factorhouse&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; features&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;offset&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;management&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# factorhouse&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;LICENSE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;README&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;analytics.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;community.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;trial.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kpow&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;community.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;kpow&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;trial.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── compose&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;pinot.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── quickstart&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# └── resources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# features&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;offset&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;management&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;README&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── consumer.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# ├── producer.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# └── requirements.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; directories, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At Factor House, we recently created a GitHub repository called &lt;a href=&quot;https://github.com/factorhouse/examples&quot;&gt;&lt;strong&gt;examples&lt;/strong&gt;&lt;/a&gt;. As the name suggests, it holds Factor House product feature and integration examples.&lt;/p&gt;
&lt;p&gt;The Kafka clients used in this post can be found in the &lt;a href=&quot;https://github.com/factorhouse/examples/tree/main/offset-management&quot;&gt;&lt;strong&gt;offset-management&lt;/strong&gt;&lt;/a&gt; folder. Also, we use &lt;a href=&quot;https://github.com/factorhouse/factorhouse-local&quot;&gt;&lt;strong&gt;Factor House Local&lt;/strong&gt;&lt;/a&gt; to deploy a Kpow instance and a 3-node Kafka cluster.&lt;/p&gt;
&lt;p&gt;We can set up a local development environment as follows.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## clone examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; https://github.com/factorhouse/examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; cd&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; examples&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## install python packages in a virtual environment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; venv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; source&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; venv/bin/activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; pip&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/requirements.txt&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## clone factorhouse-local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; https://github.com/factorhouse/factorhouse-local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;## show key files and folders&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; tree&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -L&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse-local/&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use either the Kpow Community or Enterprise edition. &lt;strong&gt;To get started, let’s make sure a valid Kpow license is available.&lt;/strong&gt; For details on how to request and configure a license, refer to &lt;a href=&quot;https://github.com/factorhouse/factorhouse-local?tab=readme-ov-file#update-kpow-and-flex-licenses&quot;&gt;&lt;strong&gt;this section&lt;/strong&gt;&lt;/a&gt; of the project &lt;em&gt;README&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In this post, we’ll be using the Community edition.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse-local/compose-kpow-community.yml&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; up&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; compose&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; factorhouse-local/compose-kpow-community.yml&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;kafka-clients&quot;&gt;Kafka Clients&lt;/h2&gt;
&lt;p&gt;The Kafka producer interacts with a Kafka cluster using the &lt;strong&gt;&lt;code&gt;confluent_kafka&lt;/code&gt;&lt;/strong&gt; library to create a topic and produce messages. It first checks if a specified Kafka topic exists and creates it if necessary - a topic having a single partition is created for simplicity. The app then generates 10 JSON messages containing the current timestamp (both in ISO 8601 and Unix epoch format) and sends them to the Kafka topic, using a callback function to log success or failure upon delivery. Configuration values like the Kafka &lt;strong&gt;bootstrap servers&lt;/strong&gt; and &lt;strong&gt;topic name&lt;/strong&gt; are read from environment variables or default to &lt;strong&gt;&lt;code&gt;localhost:9092&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;offset-management&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; datetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; logging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; confluent_kafka &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Producer, Message, KafkaError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; confluent_kafka.admin &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; AdminClient, NewTopic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; topic_exists&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(admin_client: AdminClient, topic_name: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## check if a topic exists&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    metadata &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; admin_client.list_topics()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; value &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; iter&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(metadata.topics.values()):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        logging.info(value.topic)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; value.topic &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topic_name:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; True&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; create_topic&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(admin_client: AdminClient, topic_name: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## create a new topic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    new_topic &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; NewTopic(topic_name, &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;num_partitions&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;replication_factor&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    result_dict &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; admin_client.create_topics([new_topic])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topic, future &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; result_dict.items():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            future.result()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            logging.info(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Topic &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;topic&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; created&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        except&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; e:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            logging.info(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Failed to create topic &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;topic&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; callback&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(err: KafkaError, event: Message):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## callback function that gets triggered when a message is delivered&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; err:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        logging.info(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Produce to topic &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;event.topic()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; failed for event: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;event.key()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    else&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        value &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; event.value().decode(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        logging.info(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Sent: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; to partition &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;event.partition()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;, offset &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;event.offset()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; __name__&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    logging.basicConfig(&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;level&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;logging.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;INFO&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os.getenv(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;BOOTSTRAP_SERVERS&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os.getenv(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;TOPIC_NAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;offset-management&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## create a topic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    conf &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;bootstrap.servers&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    admin_client &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; AdminClient(conf)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; topic_exists(admin_client, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        create_topic(admin_client, &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## producer messages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    producer &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Producer(conf)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; _ &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        dt &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; datetime.datetime.now()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        epoch &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; int&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(dt.timestamp() &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; 1000&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        ts &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; dt.isoformat(&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;timespec&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;seconds&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        producer.produce(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209&quot;&gt;            topic&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209&quot;&gt;            value&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;json.dumps({&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;epoch&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: epoch, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: ts}).encode(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209&quot;&gt;            key&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(epoch),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209&quot;&gt;            on_delivery&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;callback,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        producer.flush()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        time.sleep(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Kafka consumer polls messages from a Kafka topic using the &lt;strong&gt;&lt;code&gt;confluent_kafka&lt;/code&gt;&lt;/strong&gt; library. It configures a Kafka consumer with parameters like &lt;strong&gt;bootstrap.servers&lt;/strong&gt; , &lt;strong&gt;group.id&lt;/strong&gt; , and &lt;strong&gt;auto.offset.reset&lt;/strong&gt;. The consumer subscribes to a specified topic, and a callback function (&lt;strong&gt;&lt;code&gt;assignment_callback&lt;/code&gt;&lt;/strong&gt;) is used to log when partitions are assigned to the consumer. The main loop continuously polls for messages, processes them by decoding their values, logs the message details (including partition and offset), and commits the message offset. If any errors occur, the script raises a &lt;strong&gt;&lt;code&gt;KafkaException&lt;/code&gt;&lt;/strong&gt;. The script gracefully handles a user interrupt (&lt;strong&gt;&lt;code&gt;KeyboardInterrupt&lt;/code&gt;&lt;/strong&gt;), ensuring proper cleanup by closing the consumer connection.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; logging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; List&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; confluent_kafka &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Consumer, KafkaException, TopicPartition, Message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; assignment_callback&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;(_: Consumer, partitions: List[TopicPartition]):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    ## callback function that gets triggered when a topic partion is assigned&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; partitions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        logging.info(&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Assigned to &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;p.topic&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;, partiton &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;p.partition&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; __name__&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    logging.basicConfig(&lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;level&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;logging.&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;INFO&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os.getenv(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;BOOTSTRAP_SERVERS&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;localhost:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;    TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; os.getenv(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;TOPIC_NAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;offset-management&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    conf &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;        &quot;bootstrap.servers&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;BOOTSTRAP_SERVERS&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;        &quot;group.id&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{TOPIC_NAME}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;-group&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;        &quot;auto.offset.reset&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;earliest&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;        &quot;enable.auto.commit&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;False&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    consumer &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; Consumer(conf)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;    consumer.subscribe([&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;TOPIC_NAME&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;color:#E36209&quot;&gt;on_assign&lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;assignment_callback)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;        while&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; True&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;            message: Message &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; consumer.poll(&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; message &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; None&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; message.error():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                raise&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; KafkaException(message.error())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;            else&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                value &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; message.value().decode(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                partition &lt;/span&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; message.partition()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                logging.info(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;                    f&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Reveived &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; from partition &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;message.partition()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;, offset &lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;message.offset()&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;                consumer.commit(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    except&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; KeyboardInterrupt&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        logging.warning(&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cancelled by user&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;    finally&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;        consumer.close()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before diving into offset management, let’s first create a Kafka topic named &lt;strong&gt;offset-management&lt;/strong&gt; and produce 10 sample messages.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/producer.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let the consumer keep polling messages from the topic. By subscribing the topic, it creates a consumer group named &lt;strong&gt;offset-management-group&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/consumer.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;offset-management&quot;&gt;Offset Management&lt;/h2&gt;
&lt;h3 id=&quot;clear-offsets&quot;&gt;Clear Offsets&lt;/h3&gt;
&lt;p&gt;This feature removes committed offsets for an entire consumer group, a specific group member, or a group member’s partition assignment. When you trigger &lt;strong&gt;Clear offsets&lt;/strong&gt; and confirm the action, it is scheduled and visible under the &lt;em&gt;Mutations&lt;/em&gt; tab. The action remains in a scheduled state until its prerequisite is fulfilled. For group offsets, the prerequisite is that the consumer group must be in an &lt;strong&gt;EMPTY&lt;/strong&gt; state - meaning no active members. To meet this condition, you must manually scale down or stop all instances of the consumer group. This requirement exists because offset-related mutations cannot be applied to a running consumer group. When we stop the Kafka consumer, we see the status becomes &lt;strong&gt;Success&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8fe435ef48d9d16469ea8_clear-offset.gif&quot; alt=&quot;Clear Offsets&quot;&gt;&lt;/p&gt;
&lt;p&gt;Below shows that the consumer polls messages from the earliest offset when it is restarted.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/consumer.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;reset-offsets&quot;&gt;Reset Offsets&lt;/h3&gt;
&lt;p&gt;Kpow provides powerful and flexible controls for managing consumer group offsets across various levels of granularity. You can adjust offsets based on different dimensions, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Whole group assignments&lt;/strong&gt; - reset offsets for the entire consumer group at once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Host-level assignments&lt;/strong&gt; - target offset changes to consumers running on specific hosts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topic-level assignments&lt;/strong&gt; - reset offsets for specific topics within the group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partition-level assignments&lt;/strong&gt; - make fine-grained adjustments at the individual partition level.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to flexible targeting, Kpow supports multiple methods for resetting offsets, tailored to different operational needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Offset&lt;/strong&gt; - reset to a specific offset value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timestamp&lt;/strong&gt; - rewind or advance to the earliest offset with a timestamp equal to or later than the given epoch timestamp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Datetime (Local)&lt;/strong&gt; - use a human-readable local date and time to select the corresponding offset.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These options enable precise control over Kafka consumption behavior, whether for incident recovery, message reprocessing, or routine operational adjustments.&lt;/p&gt;
&lt;p&gt;In this example, we reset the offset of topic partition &lt;strong&gt;&lt;code&gt;0&lt;/code&gt;&lt;/strong&gt; using the &lt;strong&gt;Timestamp&lt;/strong&gt; method. After entering the timestamp value &lt;strong&gt;&lt;code&gt;1744847503339&lt;/code&gt;&lt;/strong&gt; , Kpow displays both the current committed offset (&lt;strong&gt;Commit Offset&lt;/strong&gt;) and the calculated target offset (&lt;strong&gt;New Offset&lt;/strong&gt;). Once we confirm the action, it appears in the &lt;em&gt;Mutations&lt;/em&gt; tab as a scheduled mutation. Similar to the &lt;strong&gt;Clear offsets&lt;/strong&gt; feature, the mutation remains pending until the consumer is stopped - at which point its status updates to &lt;strong&gt;Success&lt;/strong&gt;. We can then verify the updated offset value directly within the group member’s assignment record, confirming that the reset has taken effect.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8fe435ef48d9d16469ea5_reset-offset.gif&quot; alt=&quot;Reset Offsets&quot;&gt;&lt;/p&gt;
&lt;p&gt;As expected, the consumer polls messages from the new offset.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/consumer.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;skip-offsets&quot;&gt;Skip Offsets&lt;/h3&gt;
&lt;p&gt;We can demonstrate the &lt;strong&gt;Skip offsets&lt;/strong&gt; feature in two steps. First, reset the offset of topic partition &lt;strong&gt;&lt;code&gt;0&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;8&lt;/code&gt;&lt;/strong&gt; and then stop the consumer. This ensures the offset update is applied successfully. Next, trigger the &lt;strong&gt;Skip offsets&lt;/strong&gt; action, which advances the offset from &lt;strong&gt;&lt;code&gt;8&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;9&lt;/code&gt;&lt;/strong&gt; , effectively skipping the message at offset &lt;strong&gt;&lt;code&gt;8&lt;/code&gt;&lt;/strong&gt;. We can verify that the new offset has taken effect by checking the updated value in the group member’s assignment record, and it confirms that the change has been applied as expected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f8fe435ef48d9d16469eab_skip-offset.gif&quot; alt=&quot;Skip Offsets&quot;&gt;&lt;/p&gt;
&lt;p&gt;We can also verify the change by restarting the consumer.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; features/offset-management/consumer.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Managing Kafka consumer offsets is crucial for reliable data processing and operational flexibility. Actions such as &lt;strong&gt;clearing&lt;/strong&gt; , &lt;strong&gt;resetting&lt;/strong&gt; (by offset, timestamp, or datetime), and &lt;strong&gt;skipping&lt;/strong&gt; offsets provide powerful capabilities for debugging, data recovery, and bypassing problematic messages.&lt;/p&gt;
&lt;p&gt;This article demonstrated these concepts using simple Kafka clients and showcased how Kpow offers an intuitive interface with granular controls - spanning entire groups down to individual partitions - to execute these essential offset management tasks effectively. Kpow significantly simplifies these complex Kafka operations, ultimately providing developers and operators with enhanced visibility and precise control over their Kafka consumption patterns.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Set Up Kpow with Amazon Managed Streaming for Apache Kafka</title><link>https://factorhouse.io/articles/set-up-kpow-with-aws/</link><guid isPermaLink="true">https://factorhouse.io/articles/set-up-kpow-with-aws/</guid><description>Integrate Kpow with Amazon Managed Streaming for Apache Kafka (MSK) in minutes. Gain unified visibility and control over your AWS brokers, MSK Connect, and Glue Schema Registry through our market-leading engineering toolkit.</description><pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Managing real-time data pipelines on AWS requires robust visibility and control. Kpow is the all-in-one engineering toolkit designed to provide exactly that, offering a unified interface to monitor, manage, and explore your streaming infrastructure.&lt;/p&gt;
&lt;p&gt;Built to work securely within the AWS ecosystem, Kpow is fully compatible with &lt;a href=&quot;https://aws.amazon.com/msk/&quot;&gt;&lt;strong&gt;Amazon Managed Streaming for Apache Kafka (MSK)&lt;/strong&gt;&lt;/a&gt; out of the box. Because it relies on standard Kafka protocols and integrates directly with AWS IAM, Kpow connects to your brokers seamlessly, eliminating the need for proprietary plugins, external agents, or complex sidecars.&lt;/p&gt;
&lt;p&gt;Whether your architecture relies on the traditional &lt;strong&gt;Amazon MSK (Provisioned)&lt;/strong&gt; offering for fine-grained configuration, or the auto-scaling &lt;strong&gt;Amazon MSK Serverless&lt;/strong&gt; for operational simplicity, Kpow serves as a single pane of glass for your entire Kafka deployment.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to Amazon MSK, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running Amazon MSK cluster:&lt;/strong&gt; Either Provisioned or Serverless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network reachability:&lt;/strong&gt; The instance running Kpow must reside in the same VPC, or have peered access, with Security Groups allowing inbound traffic on the appropriate Kafka port.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Details &amp;amp; Authentication:&lt;/strong&gt; MSK supports multiple authentication methods. You will need your Bootstrap Server URL and the corresponding port:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IAM Access Control&lt;/strong&gt; (Port 9098)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SASL/SCRAM&lt;/strong&gt; (Port 9096)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mTLS&lt;/strong&gt; (Port 9094)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; A valid license is required unless you are deploying via the AWS Marketplace.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to MSK is using our standard Enterprise Docker image.&lt;/p&gt;
&lt;p&gt;Since &lt;strong&gt;IAM Access Control&lt;/strong&gt; is the recommended, native AWS approach, the following example demonstrates an IAM connection. If you are running this on an EC2 instance, ECS task, or EKS pod with an attached IAM role, Kpow will automatically inherit the necessary permissions.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific MSK details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;[MSK_BOOTSTRAP_ADDRESS]:9098&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;AWS_MSK_IAM&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&quot;software.amazon.msk.auth.iam.IAMLoginModule required;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_CLIENT_CALLBACK_HANDLER_CLASS=&quot;software.amazon.msk.auth.iam.IAMClientCallbackHandler&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration:&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Running outside AWS:&lt;/strong&gt; If running Docker on a local machine or external server, provide static AWS credentials and region via environment variables by adding the following to the command above:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;--env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; AWS_ACCESS_KEY_ID=&quot;xxx&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;--env &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;AWS_SECRET_ACCESS_KEY=&quot;xxx&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;--env &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;AWS_REGION=&quot;xxx&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the container starts, navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;. You will see an overview of your MSK topics, brokers, and consumer groups.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69c31aa1af7e292f0e4c6441_msk-demo.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;p&gt;This screenshot displays a cluster with eight Kafka brokers and one instance each of Kafka Connect and Schema Registry. Your specific view will vary depending on your environment configuration.&lt;/p&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;While standard Kafka configuration applies, Amazon MSK has specific authentication options and serverless constraints.&lt;/p&gt;
&lt;h3 id=&quot;authentication&quot;&gt;Authentication&lt;/h3&gt;
&lt;p&gt;Kpow offers first-class support for IAM Access Control, SASL/SCRAM, and mTLS. When using IAM, ensure your AWS principal has the necessary kafka-cluster:* permissions attached.&lt;/p&gt;
&lt;p&gt;For a comprehensive list of configuration options, detailed IAM policy examples, and SCRAM/mTLS setups, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/amazon-msk/kafka-cluster&quot;&gt;Kpow MSK Provider Documentation&lt;/a&gt; and the &lt;a href=&quot;https://docs.aws.amazon.com/msk/latest/developerguide/kafka_apis_iam.html&quot;&gt;Amazon MSK Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;msk-serverless-constraints&quot;&gt;MSK Serverless Constraints&lt;/h3&gt;
&lt;p&gt;If you are connecting to an &lt;strong&gt;Amazon MSK Serverless&lt;/strong&gt; cluster, you must add the following environment variable to your Kpow configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;--env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; KAFKA_VARIANT=&quot;MSK_SERVERLESS&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures Kpow creates its internal topics with the constrained &lt;a href=&quot;https://docs.aws.amazon.com/msk/latest/developerguide/serverless-config.html&quot;&gt;topic configuration properties&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/msk/latest/developerguide/limits.html&quot;&gt;service limitations&lt;/a&gt; specific to MSK Serverless.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Kpow provides comprehensive support for the AWS streaming ecosystem, allowing you to monitor and manage your wider MSK architecture natively.&lt;/p&gt;
&lt;h3 id=&quot;amazon-msk-connect&quot;&gt;Amazon MSK Connect&lt;/h3&gt;
&lt;p&gt;Kpow authenticates with Amazon MSK Connect using the AWS SDK (via the Default Credentials Chain, Static Credentials, or STS AssumeRole). You must specify the region where your Connect cluster is deployed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka Connect Region:&lt;/strong&gt; &lt;code&gt;--env CONNECT_AWS_REGION=&quot;us-east-1&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For cross-account setups and IAM policy requirements, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/amazon-msk/kafka-connect&quot;&gt;MSK Connect integration guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;aws-glue-schema-registry&quot;&gt;AWS Glue Schema Registry&lt;/h3&gt;
&lt;p&gt;Kpow connects to the AWS Glue Schema Registry using environment variables. Similar to MSK Connect, it authenticates using the AWS SDK.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Schema Registry ARN:&lt;/strong&gt; &lt;code&gt;--env SCHEMA_REGISTRY_ARN=&quot;arn:aws:glue:us-east-1:123456789012:registry/my-registry&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema Registry Region:&lt;/strong&gt; &lt;code&gt;--env SCHEMA_REGISTRY_REGION=&quot;us-east-1&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/amazon-msk/schema-registry&quot;&gt;Glue Schema Registry integration guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we highly recommend utilizing the AWS Marketplace.&lt;/p&gt;
&lt;h3 id=&quot;aws-marketplace-recommended&quot;&gt;AWS Marketplace (Recommended)&lt;/h3&gt;
&lt;p&gt;Kpow integrates seamlessly with AWS and is easy to deploy directly from the &lt;a href=&quot;https://aws.amazon.com/marketplace/seller-profile?id=ab356f1d-3394-4523-b5d4-b339e3cca9e0&quot;&gt;&lt;strong&gt;AWS Marketplace&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Marketplace containers are &lt;strong&gt;automatically licensed&lt;/strong&gt; to your AWS account, meaning you do not need to arrange a separate license key with us. Usage is billed directly through your AWS invoice.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kpow Hourly:&lt;/strong&gt; Pay-as-you-go with no ongoing commitment. Ideal for ECS and Fargate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kpow Annual:&lt;/strong&gt; For long-term commitments and Enterprise Discount Program (EDP) purchases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a complete walkthrough, see our guide: &lt;a href=&quot;https://factorhouse.io/how-to/deploy-kpow-on-eks-via-aws-marketplace&quot;&gt;Deploy Kpow on EKS via AWS Marketplace using Helm&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-byol&quot;&gt;Kubernetes (BYOL)&lt;/h3&gt;
&lt;p&gt;If you have a direct Enterprise License (Bring Your Own License) and want to run Kpow on &lt;strong&gt;Amazon EKS&lt;/strong&gt; , we recommend using our official &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;Helm Installation Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;vm--bare-metal-byol&quot;&gt;VM / Bare Metal (BYOL)&lt;/h3&gt;
&lt;p&gt;For running Kpow directly on an &lt;strong&gt;Amazon EC2&lt;/strong&gt; instance, you can use the &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;Kpow JAR artifact&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a single pane of glass for your Amazon MSK infrastructure, making it easy to monitor and manage your data streams, connectors, and schemas in real-time.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Amazon MSK integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;related-content&quot;&gt;Related Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-confluent-cloud&quot;&gt;Set Up Kpow with Confluent Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-gcp&quot;&gt;Set Up Kpow with Google Cloud Managed Service for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/integrate-kpow-with-oci-streaming&quot;&gt;How to Integrate Kpow with OCI Streaming with Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-instaclustr&quot;&gt;Set Up Kpow with NetApp Instaclustr Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Set Up Kpow with Confluent Cloud</title><link>https://factorhouse.io/articles/set-up-kpow-with-confluent-cloud/</link><guid isPermaLink="true">https://factorhouse.io/articles/set-up-kpow-with-confluent-cloud/</guid><description>Integrate Kpow with Confluent Cloud in minutes. Gain unified visibility and control over your managed Kafka brokers, Schema Registry, Managed Connect, and ksqlDB through our market-leading engineering toolkit.</description><pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Managing Apache Kafka within a platform like &lt;a href=&quot;https://www.confluent.io/confluent-cloud/&quot;&gt;&lt;strong&gt;Confluent Cloud&lt;/strong&gt;&lt;/a&gt; provides significant advantages in scalability and managed services. However, as your streaming architecture grows to include schemas, connectors, and stream processing, maintaining complete observability across those components becomes essential.&lt;/p&gt;
&lt;p&gt;This guide details the process of connecting Kpow to your wider Confluent Cloud ecosystem—including Kafka brokers, Schema Registry, Managed Connect, and ksqlDB. By leveraging Kpow’s direct API integrations, you can unify these managed resources into a single, powerful engineering console. This grants your team deep visibility and control over your data streams, schemas, and connectors without the need to manage complex sidecars or proprietary agents.&lt;/p&gt;
&lt;h2 id=&quot;about-factor-house&quot;&gt;About Factor House&lt;/h2&gt;
&lt;p&gt;Factor House is a leader in real-time data tooling, empowering engineers with innovative solutions for &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka®&lt;/a&gt; and &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink®&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our flagship product, &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;Kpow for Apache Kafka&lt;/a&gt;, is the market-leading enterprise solution for Kafka management and monitoring.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69aa39273c6060ff8b0d909d_kpow-hero-data.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To connect Kpow to Confluent Cloud, you must have the following resources provisioned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A running Confluent Cloud cluster:&lt;/strong&gt; Reachable from the host where you intend to run Kpow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Details:&lt;/strong&gt; Your Confluent Cloud Bootstrap Server address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cluster Authentication:&lt;/strong&gt; A Confluent Cloud API Key and Secret generated for your cluster to authorize Kafka connections.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics Authentication (Optional):&lt;/strong&gt; A Cloud API Key and Secret generated specifically for Cloud Metrics to view disk usage telemetry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Kpow Enterprise License:&lt;/strong&gt; Get a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;p&gt;The fastest way to connect Kpow to Confluent Cloud is using our standard Enterprise Docker image.&lt;/p&gt;
&lt;p&gt;Run the following command in your terminal, replacing the placeholder values with your specific cluster connection details:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; 3000:3000&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; BOOTSTRAP=&quot;[BOOTSTRAP_SERVER_ADDRESS]:9092&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SECURITY_PROTOCOL=&quot;SASL_SSL&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_MECHANISM=&quot;PLAIN&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SASL_JAAS_CONFIG=&apos;org.apache.kafka.common.security.plain.PlainLoginModule required username=&quot;[CLUSTER_API_KEY]&quot; password=&quot;[CLUSTER_API_SECRET]&quot;;&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; SSL_ENDPOINT_IDENTIFICATION_ALGORITHM=&quot;https&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_ID=&quot;&amp;#x3C;LICENSE_ID&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_CODE=&quot;&amp;#x3C;LICENSE_CODE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSEE=&quot;&amp;#x3C;LICENSEE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_EXPIRY=&quot;&amp;#x3C;LICENSE_EXPIRY&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; LICENSE_SIGNATURE=&quot;&amp;#x3C;LICENSE_SIGNATURE&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62&quot;&gt;  factorhouse/kpow:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;notes&quot;&gt;Notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;License details:&lt;/strong&gt; The license details can be obtained from your signup email or via the Factor House license portal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization configuration&lt;/strong&gt; For brevity, Kpow authorization configuration has been omitted. See &lt;a href=&quot;https://docs.factorhouse.io/kpow/authorization/simple-access-control&quot;&gt;Simple Access Control&lt;/a&gt; to enable necessary user actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the container starts, open a browser and navigate to &lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000&lt;/a&gt;. You will immediately see your Confluent Cloud topics, consumer groups, and brokers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/69cb3cc5670b904f528beea5_confluent-demo.png&quot; alt=&quot;__wf_reserved_inherit&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;configuration-details&quot;&gt;Configuration Details&lt;/h2&gt;
&lt;p&gt;Confluent Cloud uses SASL/PLAIN authentication over TLS. Note the strict requirement of SSL_ENDPOINT_IDENTIFICATION_ALGORITHM=“https” in the Docker command above, which is necessary to successfully verify the broker hostname.&lt;/p&gt;
&lt;h3 id=&quot;unlocking-disk-metrics-optional&quot;&gt;Unlocking Disk Metrics (Optional)&lt;/h3&gt;
&lt;p&gt;By default, Confluent Cloud does not expose broker disk metrics through standard Kafka APIs. To view retained bytes and disk usage in the Kpow UI, you must provide Kpow with a &lt;strong&gt;Cloud API Key&lt;/strong&gt; (distinct from your Cluster API Key) to query the Confluent Metrics API directly.&lt;/p&gt;
&lt;p&gt;Add the following environment variables to your Kpow deployment:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONFLUENT_API_KEY=&quot;[CLOUD_API_KEY]&quot;&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5&quot;&gt;  --env&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; CONFLUENT_API_SECRET=&quot;[CLOUD_API_SECRET]&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a comprehensive list of configuration options, including OAuth authentication, mTLS, and advanced Metrics API settings to handle rate limits, refer to our &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/confluent-cloud/kafka-cluster&quot;&gt;Confluent Kafka cluster documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ecosystem-integration&quot;&gt;Ecosystem Integration&lt;/h2&gt;
&lt;p&gt;Kpow connects seamlessly to the wider Confluent Cloud ecosystem. You can monitor and manage these resources by adding the corresponding environment variables to your deployment.&lt;/p&gt;
&lt;h3 id=&quot;confluent-managed-connect&quot;&gt;Confluent Managed Connect&lt;/h3&gt;
&lt;p&gt;Connect to your Managed Kafka Connect environment using standard basic authentication.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONNECT_REST_URL=&quot;https://[YOUR_CONNECT_ENDPOINT]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONNECT_AUTH=&quot;BASIC&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONNECT_BASIC_AUTH_USER=&quot;[CONNECT_API_KEY]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONNECT_BASIC_AUTH_PASS=&quot;[CONNECT_API_SECRET]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, refer to the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/confluent-cloud/kafka-connect&quot;&gt;Confluent Managed Connect documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;confluent-schema-registry&quot;&gt;Confluent Schema Registry&lt;/h3&gt;
&lt;p&gt;Kpow connects to the Confluent Schema Registry using HTTPS and basic user authentication.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SCHEMA_REGISTRY_URL=&quot;https://[YOUR_REGISTRY_ENDPOINT]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCHEMA_REGISTRY_AUTH=&quot;USER_INFO&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCHEMA_REGISTRY_USER=&quot;[REGISTRY_API_KEY]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCHEMA_REGISTRY_PASSWORD=&quot;[REGISTRY_API_SECRET]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For detailed access control and connection options, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/provider/confluent-cloud/schema-registry&quot;&gt;Confluent Schema Registry documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ksqldb&quot;&gt;ksqlDB&lt;/h3&gt;
&lt;p&gt;Kpow offers a rich UI for ksqlDB. Confluent Cloud requires TLS and Application-Layer Protocol Negotiation (ALPN) to be enabled.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_HOST=&quot;[YOUR_KSQLDB_ENDPOINT]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_PORT=&quot;443&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_USE_TLS=&quot;true&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_USE_ALPN=&quot;true&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_BASIC_AUTH_USER=&quot;[KSQLDB_API_KEY]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KSQLDB_BASIC_AUTH_PASSWORD=&quot;[KSQLDB_API_SECRET]&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See our general &lt;a href=&quot;https://docs.factorhouse.io/kpow/configuration/ksql-db&quot;&gt;ksqlDB configuration guide&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2 id=&quot;production-deployment&quot;&gt;Production Deployment&lt;/h2&gt;
&lt;p&gt;When you are ready to move from a local Docker test to a production deployment, we recommend the following paths:&lt;/p&gt;
&lt;h3 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;For deploying Kpow to Kubernetes clusters alongside Confluent Cloud—such as Amazon EKS, GKE, or AKS, we recommend using our &lt;a href=&quot;https://github.com/factorhouse/helm-charts&quot;&gt;official Helm Charts&lt;/a&gt;. For step-by-step instructions, refer to the &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/helm&quot;&gt;Helm installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;bare-metal--vm&quot;&gt;Bare Metal / VM&lt;/h3&gt;
&lt;p&gt;If you prefer running Kpow directly on a Virtual Machine, you can download the Kpow JAR file. For setup instructions, see the, see the &lt;a href=&quot;https://docs.factorhouse.io/kpow/installation/java-jar&quot;&gt;Java JAR Installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kpow provides a powerful, single pane of glass view into your Confluent Cloud infrastructure. By using standard Kafka protocols and direct API integrations, you can unify your Kafka clusters, Schema Registry, Managed Connect, and ksqlDB environments in minutes.&lt;/p&gt;
&lt;p&gt;Explore these features in your own environment with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow.&lt;/p&gt;
&lt;p&gt;If you need assistance with your Confluent Cloud integration, reach out to our engineering support team at &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;related-content&quot;&gt;Related Content&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-aws&quot;&gt;Set Up Kpow with Amazon Managed Streaming for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-gcp&quot;&gt;Set Up Kpow with Google Cloud Managed Service for Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/integrate-kpow-with-oci-streaming&quot;&gt;How to Integrate Kpow with OCI Streaming with Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/how-to/set-up-kpow-with-instaclustr&quot;&gt;Set Up Kpow with NetApp Instaclustr Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>How to query a Kafka topic</title><link>https://factorhouse.io/articles/query-a-kafka-topic/</link><guid isPermaLink="true">https://factorhouse.io/articles/query-a-kafka-topic/</guid><description>Querying Kafka topics is a critical task for engineers working on data streaming applications, but it can often be a complex and time-consuming process. Enter Kpow&apos;s data inspect feature—designed to simplify and optimize Kafka topic queries, making it an essential tool for professionals working with Apache Kafka.</description><pubDate>Thu, 22 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Querying Kafka topics is a critical task for engineers working on data streaming applications, but it can often be a complex and time-consuming process.&lt;/p&gt;
&lt;p&gt;Whether you’re a developer prototyping a new feature or an infrastructure engineer ensuring the stability of a production environment, having a powerful querying tool at your disposal can make all the difference. Enter Kpow’s data inspect feature—designed to simplify and optimize Kafka topic queries, making it an essential tool for professionals working with Apache Kafka.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Apache Kafka and its ecosystem have emerged as the cornerstone of modern data-in-motion solutions. Our customers leverage a variety of technologies, including Kafka Streams, Apache Flink, Kafka Connect, and custom services using Java, Go, or Python, to build their data streaming applications.&lt;/p&gt;
&lt;p&gt;Regardless of the technology stack, engineers need reliable tools to examine the foundational Kafka topics in their streaming applications. This is where Kpow’s data inspect feature comes into play. Data inspect offers ad hoc, bounded queries across Kafka topics, proving valuable in both development and production scenarios. Here’s how it can be particularly useful:&lt;/p&gt;
&lt;h3 id=&quot;key-use-cases-in-development&quot;&gt;Key use cases in development&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Validating data structures&lt;/strong&gt; : Verifying and validating the shape of data (both key and value) during the prototyping phase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring message flow&lt;/strong&gt; : Ensuring that messages are flowing to the topic as expected and that topic message distribution is well balanced across all partitions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debugging and troubleshooting&lt;/strong&gt; : Identifying and resolving issues in the development phase. For example validating that your topic’s configuration is applying its compaction policy as expected or that segments are being deleted as expected.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;critical-applications-in-production&quot;&gt;Critical applications in production&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identifying poison messages&lt;/strong&gt; : Quickly identifying and addressing messages that can cause downstream issues that may have caused consumer groups to break.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reconciliation and Analytics&lt;/strong&gt; : Querying for specific events for reconciliation or analytic purposes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring and Alerting&lt;/strong&gt; : Keeping track of Kafka topics for anomalies or unusual activity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance and Auditing&lt;/strong&gt; : Ensuring compliance with data governance standards and auditing access to sensitive data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capacity Planning&lt;/strong&gt; : Planning and scaling infrastructure based on the volume and velocity of data flowing through topics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article will dive into the technical details of Kpow’s data inspect query engine and how you can maximise your own querying in Kafka. Whether you’re a developer looking to validate data during development or part of the infrastructure team tasked with ensuring the stability and performance of your production Kafka clusters, data inspect offers a powerful set of tools to help you get the most out of your Kafka deployments.&lt;/p&gt;
&lt;h2 id=&quot;introduction-to-data-inspect&quot;&gt;Introduction to Data Inspect&lt;/h2&gt;
&lt;p&gt;Kpow’s data inspect gives teams the ability to perform bounded queries across one or more Kafka topics. A bounded query retrieves a specific range or subset of data from a Kafka topic, informed by user input through the data inspect form. Users can specify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Date Range: An ISO 8601 date range specifying the start and end bounds of the query.&lt;/li&gt;
&lt;li&gt;An Offset Range: The start offset from where you’d like the query to begin (especially useful when searching against a partition or key).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kpow’s data inspect form simplifies the querying process by offering common query options as defaults. For instance, to view the most recent messages in a topic, Kpow’s default query window is set to ‘Recent’ (e.g., the last 15 minutes). Users can also specify custom date times or timestamps for more fine-grained queries.&lt;/p&gt;
&lt;p&gt;Additionally, the data inspect form allows input of topic SerDes and any filters to apply against the result set, which will be explained below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f905ff529f63456b3dea4b_data-inspect-form.png&quot; alt=&quot;Data inspect form&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-query-plan&quot;&gt;The Query Plan&lt;/h3&gt;
&lt;p&gt;Once all inputs are provided, Kpow constructs a query plan similar to that of a SQL engine. This plan optimizes the execution of the query and efficiently parallelizes queries across a pool of consumer groups. It’s this query engine that powers Kpow’s blazingly fast multi-topic search.&lt;/p&gt;
&lt;p&gt;The query engine ensures an even distribution of records from all topic partitions when querying. An even distribution is crucial for understanding a topic’s performance because it ensures that the analysis is based on a representative sample of the data. If certain partitions are overrepresented, the analysis may be skewed, leading to inaccurate insights.&lt;/p&gt;
&lt;p&gt;The cursors table, part of the data inspect result metadata, displays the comprehensive progress of the query, detailing the start and end offsets for each topic partition, the number of records scanned, and the remaining offsets to query.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f905ff529f63456b3dea4f_query-plan.png&quot; alt=&quot;Query engine cursors&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;we-understand-your-data&quot;&gt;We understand your data&lt;/h3&gt;
&lt;p&gt;Kpow supports a wide-array of commonly used data formats (known as SerDes). These formats include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;AVRO&lt;/li&gt;
&lt;li&gt;JSON Schema&lt;/li&gt;
&lt;li&gt;Protobuf&lt;/li&gt;
&lt;li&gt;Clojure formats such as EDN or Transit/JSON&lt;/li&gt;
&lt;li&gt;XML, YAML and raw strings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kpow integrates with both Confluent’s Schema Registry and AWS Glue. Our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/config/schema-registry/&quot;&gt;&lt;strong&gt;documentation&lt;/strong&gt;&lt;/a&gt; has guides on how you can configure Kpow’s Schema Registry integration.&lt;/p&gt;
&lt;p&gt;If we don’t support a data format you use (for example you use Protobuf with your own encryption-at-rest) you can import your own custom SerDes to use with Kpow. Visit our &lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/features/data-inspect/serdes/#custom-serdes&quot;&gt;&lt;strong&gt;documentation&lt;/strong&gt;&lt;/a&gt; to learn more about custom SerDes.&lt;/p&gt;
&lt;h3 id=&quot;jq-filters-for-kafka&quot;&gt;jq filters for Kafka&lt;/h3&gt;
&lt;p&gt;No matter which message format you use, filtering messages in Kpow works transparently across every deserializer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/features/data-inspect/kjq-filters/&quot;&gt;&lt;strong&gt;kJQ&lt;/strong&gt;&lt;/a&gt; is the filtering engine we have built at Kpow. It’s a subset of the &lt;a href=&quot;https://jqlang.github.io/jq/&quot;&gt;&lt;strong&gt;jq programming language&lt;/strong&gt;&lt;/a&gt; built specifically for Kafka workloads, and is embedded within Kpow’s data inspect.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;jq is like sed for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead of creating yet another bespoke querying language that our customers would have to learn, we chose jq, one of the most concise, powerful, and immediately familiar querying languages available.&lt;/p&gt;
&lt;p&gt;An example of a kJQ query:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.key.currency == &quot;GBP&quot; and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.value.tx.price | to-double &amp;#x3C; 16.50 and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.value.tx.pan | endswith(&quot;8649&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are unfamiliar with jq, or want to learn more we generally recommend the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.devtoolsdaily.com/jq_playground/&quot;&gt;&lt;strong&gt;jq playground&lt;/strong&gt;&lt;/a&gt;: an online interactive playground for jq filters.&lt;/li&gt;
&lt;li&gt;Kpow’s kREPL: Kpow has a built in REPL. It is our programmatic interface into Kpow’s data inspect functionality. Within the kREPL you can experiment with kJQ queries - much like the jq playground.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.factorhouse.io/kpow-ee/features/data-inspect/kjq-filters/&quot;&gt;&lt;strong&gt;Kpow’s kJQ documentation&lt;/strong&gt;&lt;/a&gt;: a quick guide on kJQ’s grammar, including examples.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the kREPL is out of scope for this blog post, stay tuned for future articles where we’ll take a deep dive into how you can use kJQ to construct sophisticated filters and data transformations right within Kpow.&lt;/p&gt;
&lt;h3 id=&quot;enterprise-security-built-in&quot;&gt;Enterprise security built-in&lt;/h3&gt;
&lt;p&gt;Filtering data is only part of the equation. In order to perform ad-hoc queries against production data, Kpow provides enterprise-grade security features:&lt;/p&gt;
&lt;h4 id=&quot;role-based-access-control-rbac&quot;&gt;Role-Based Access Control (RBAC)&lt;/h4&gt;
&lt;p&gt;Kpow’s declarative RBAC system is defined in a YAML file, where you can assign policies to user roles authenticated from an external identity provider (IdP). This allows you to permit or deny access to Kafka topics based on user roles.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;policies&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;confluent-cloud1&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;tx_trade_*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    effect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;TOPIC_INSPECT&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    role&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    effect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;TOPIC_INSPECT&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    role&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above RBAC policy defines that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Any user assigned to the &lt;strong&gt;&lt;code&gt;dev&lt;/code&gt;&lt;/strong&gt; role will have access to any topic starting with &lt;strong&gt;&lt;code&gt;tx_trade_*&lt;/code&gt;&lt;/strong&gt; for only the &lt;strong&gt;&lt;code&gt;confluent-cloud1&lt;/code&gt;&lt;/strong&gt; cluster. All other topics will be implicitly denied.&lt;/li&gt;
&lt;li&gt;Any user assigned to the &lt;strong&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/strong&gt; role will have access to all topics for all clusters managed by Kpow.&lt;/li&gt;
&lt;li&gt;All other users are implicitly denied access to data inspect functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;data-masking&quot;&gt;Data Masking&lt;/h4&gt;
&lt;p&gt;In environments where compliance with PII requirements is mandatory, data masking is essential. Kpow’s data masking feature allows you to define policies specifying which fields in a message should be redacted in the key, value, or headers of a record. These policies apply to nested data structures or arrays within messages. For instance, a policy might:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show only the last 4 characters of a field (ShowLast4)&lt;/li&gt;
&lt;li&gt;Show only the email host (ShowEmailHost)&lt;/li&gt;
&lt;li&gt;Return a SHA hash of the contents (SHAHash)&lt;/li&gt;
&lt;li&gt;Fully redact the contents (Full)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kpow provides a data masking sandbox where users can validate policies against test data, ensuring that redaction methods work as expected before deploying them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f905ff529f63456b3dea46_data-masking.png&quot; alt=&quot;Data masking in Kpow&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;data-governance&quot;&gt;Data Governance&lt;/h4&gt;
&lt;p&gt;Maintaining a comprehensive audit log is crucial for ensuring data governance and regulatory compliance. Kpow’s audit log records all queries performed against topics, providing a detailed trail of who accessed the data, what topics were accessed, and when the query occurred. This information is vital for monitoring and enforcing data security policies, detecting unauthorized access, and demonstrating compliance with regulations such as GDPR, HIPAA, or PCI DSS. Within Kpow’s admin UI, navigate to the “Audit Log” page and then to the “Queries” tab to view all queries performed using Kpow.&lt;/p&gt;
&lt;p&gt;Within Kpow’s admin UI you can navigate to the “Audit log” page and then to the “Queries” tab to view all queries that have been performed using Kpow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f905ff529f63456b3dea41_audit.png&quot; alt=&quot;Kpow’s audit log for queries&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-started-today&quot;&gt;Getting started today&lt;/h2&gt;
&lt;p&gt;Kpow’s data inspect feature revolutionizes the way professionals work with Apache Kafka, offering a comprehensive toolkit for querying Kafka topics with ease and efficiency. Whether you’re validating data structures, monitoring message flow, or troubleshooting issues, Kpow provides the tools you need to streamline your workflow and optimize your Kafka-based applications.&lt;/p&gt;
&lt;p&gt;Ready to take your Kafka querying to the next level? Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action. Experience firsthand why Kpow is the number one toolkit for Apache Kafka and unlock new possibilities for managing and optimizing your Kafka clusters.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Jaehyeon Kim</author></item><item><title>Delete Records in Kafka</title><link>https://factorhouse.io/articles/delete-records-in-kafka/</link><guid isPermaLink="true">https://factorhouse.io/articles/delete-records-in-kafka/</guid><description>This article provides a step-by-step guide on the various ways to delete records in Kafka.</description><pubDate>Mon, 15 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h4 id=&quot;this-article-dives-into-the-various-ways-you-can-delete-records-in-kafka&quot;&gt;This article dives into the various ways you can delete records in Kafka&lt;/h4&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Have you ever wondered how to effectively delete records in a Kafka topic? Well, there are actually several ways to do it, each with their own implications and granularity.&lt;/p&gt;
&lt;p&gt;In this article, we’ll explore these different approaches in detail, from the complete deletion of a topic to the more granular erasure of individual records. Understanding these methods is essential for anyone working with Kafka, as it can have significant implications for data retention, storage, and processing. By the end of this article, you’ll have a better understanding of the different methods available for record deletion in Kafka, and how to choose the best approach for your specific use case.&lt;/p&gt;
&lt;h2 id=&quot;about-kpow&quot;&gt;About Kpow&lt;/h2&gt;
&lt;p&gt;This article uses &lt;a href=&quot;https://factorhouse.io/kpow&quot;&gt;&lt;strong&gt;Kpow for Apache Kafka&lt;/strong&gt;&lt;/a&gt; as a companion to demonstrate how you can delete records in Kafka.&lt;/p&gt;
&lt;p&gt;Kpow is a powerful tool that makes it easy to manage and monitor Kafka clusters, and its intuitive user interface simplifies the process of deleting records.&lt;/p&gt;
&lt;p&gt;Start your &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; and get to work deleting records in your own Kafka cluster. Or explore our &lt;a href=&quot;https://demo.kpow.io/&quot;&gt;live multi-cluster demo environment&lt;/a&gt; to see Kpow in action.&lt;/p&gt;
&lt;p&gt;If you need a Kafka cluster to play with, check out our local &lt;a href=&quot;https://github.com/factorhouse/kpow-local&quot;&gt;&lt;strong&gt;Docker Compose environment&lt;/strong&gt;&lt;/a&gt; to spin up Kpow along side a 3-node Kafka cluster on your machine.&lt;/p&gt;
&lt;h2 id=&quot;1-deleting-topics&quot;&gt;1. Deleting Topics&lt;/h2&gt;
&lt;p&gt;The most blunt and impactful way of deleting records on a Kafka cluster is by deleting the topic that contains the records.&lt;/p&gt;
&lt;p&gt;While this can be an effective way to remove all data associated with a topic, it’s important to note that this action is permanent and irreversible. Once a topic is deleted, all data will no longer be available, and any running applications that depend on this topic will likely throw exceptions. If topic auto-create is enabled on the broker, the topic could even get created again with the default topic configuration, potentially causing data loss or other issues.&lt;/p&gt;
&lt;p&gt;Despite these risks, there may be cases where deleting a Kafka topic is necessary, such as when the topic is no longer needed or contains sensitive data that must be removed. Much like dropping a table in a traditional relational database, it’s important to proceed with caution and have a clear understanding of the potential impacts before deleting a topic.&lt;/p&gt;
&lt;p&gt;Deleting topics is simple in Kpow!&lt;/p&gt;
&lt;p&gt;Navigate to Topic -&amp;gt; Details in the UI and select the topic you wish to delete.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa9499_delete-topic.gif&quot; alt=&quot;Delete Topic&quot;&gt;&lt;/p&gt;
&lt;p&gt;The result of deleting topics in Kpow (like all other actions) gets persisted to Kpow’s audit log for &lt;a href=&quot;https://docs.kpow.io/features/data-governance/&quot;&gt;&lt;strong&gt;data governance&lt;/strong&gt;&lt;/a&gt;. Kpow also provides a &lt;a href=&quot;https://docs.factorhouse.io/kpow/integration/webhook&quot;&gt;&lt;strong&gt;Slack webhook integration&lt;/strong&gt;&lt;/a&gt; to notify a channel when the deletion of a topic has been performed.&lt;/p&gt;
&lt;h2 id=&quot;2-truncating-records&quot;&gt;2. Truncating Records&lt;/h2&gt;
&lt;p&gt;Truncating records is another method for deleting data from a Kafka topic, specifically a range of records from a topic partition. Truncation removes all records before a specified offset for a given topic partition. This can be useful when you want to remove a specific range of records without deleting the entire topic.&lt;/p&gt;
&lt;h3 id=&quot;how-topic-partitions-work-in-kafka&quot;&gt;How topic partitions work in Kafka&lt;/h3&gt;
&lt;p&gt;In Kafka, all topic partitions have a start and end offset.&lt;/p&gt;
&lt;p&gt;The start offset is the offset of the very first record on the topic partition. A fresh topic partition will have a start offset of &lt;strong&gt;&lt;code&gt;0&lt;/code&gt;&lt;/strong&gt;. However, because of topic retention, cleanup policies, or even truncation, the start offset could be any value over time.&lt;/p&gt;
&lt;p&gt;And similarly, the end offset is always the last record on a topic partition. The end offset is forever growing as producers write more records to a topic.&lt;/p&gt;
&lt;p&gt;One thing to note: producing a single record may not result in a simple increment of the end offset. For example, transactional producers write additional metadata records when committing.&lt;/p&gt;
&lt;p&gt;Viewing the start and end offsets inside Kpow is easy! Simply navigate to the topic partitions table in Topic -&amp;gt; Details and select the start and end offset columns.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa9486_kpow-topic-parts-start-end.png&quot; alt=&quot;Topic Partitions Table in Kpow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;an-example-of-truncation&quot;&gt;An example of truncation&lt;/h3&gt;
&lt;p&gt;Consider a topic partition with 6 records. The start offset is &lt;strong&gt;&lt;code&gt;0&lt;/code&gt;&lt;/strong&gt; and the end offset is &lt;strong&gt;&lt;code&gt;5&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa949c_truncate-topic.svg&quot; alt=&quot;Truncate In Action&quot;&gt;&lt;/p&gt;
&lt;p&gt;If we make a request to truncate a topic partition before offset &lt;strong&gt;&lt;code&gt;3&lt;/code&gt;&lt;/strong&gt; , all records highlighted in gray will be deleted.&lt;/p&gt;
&lt;p&gt;After we have performed this action the new start offset will be &lt;strong&gt;&lt;code&gt;3&lt;/code&gt;&lt;/strong&gt; and the end offset will remain as &lt;strong&gt;&lt;code&gt;5&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;truncating-records-in-kpow&quot;&gt;Truncating records in Kpow&lt;/h3&gt;
&lt;p&gt;Kpow provides a convenient way to truncate topics with its intuitive UI.&lt;/p&gt;
&lt;p&gt;To truncate a topic in Kpow, simply follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to the topic you want to truncate in the UI.&lt;/li&gt;
&lt;li&gt;Select the partitions you want to truncate.&lt;/li&gt;
&lt;li&gt;Choose to truncate by either the last observed end offset or by group offset.&lt;/li&gt;
&lt;li&gt;Click “Truncate” to delete the specified range of records from the topic.&lt;/li&gt;
&lt;li&gt;By default, Kpow populates the last observed end offset of each partition in the form. This will delete all records up to and including the specified offset.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Alternatively, you can choose to truncate by group offset, which deletes all records a consumer group has consumed. This has the advantage of not impacting the correctness/behavior of the consumer group, by only deleting records it has read.&lt;/p&gt;
&lt;p&gt;It’s important to note that truncating a topic is a destructive action and requires careful consideration. If multiple consumers are reading from the topic, truncating by group offset could impact the other consumers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa948e_truncate.gif&quot; alt=&quot;Truncate in Kpow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;implications-of-truncating-a-topic&quot;&gt;Implications of truncating a topic&lt;/h3&gt;
&lt;p&gt;Truncating a topic in Kafka is a less intrusive way of deleting records than deleting the topic entirely. This is because the topic configuration, including the number of partitions and replicas, remains unchanged. Additionally, you have more granular control over which records get deleted.&lt;/p&gt;
&lt;p&gt;However, it’s important to note that truncating a topic is a destructive action that requires careful consideration. In particular, truncating a topic can cause data loss and may impact the behavior of any consumers reading from the affected partitions.&lt;/p&gt;
&lt;p&gt;As a best practice, it’s generally recommended to rely on the semantics of how you configure a topic to manage topic growth, rather than resorting to truncation. For example, you can use the &lt;strong&gt;&lt;code&gt;retention.ms&lt;/code&gt;&lt;/strong&gt; configuration parameter to automatically age out data after a certain period of time, or configure a cleanup policy to remove old or irrelevant data. &lt;a href=&quot;https://medium.com/@sunny_81705/kafka-log-retention-and-cleanup-policies-c8d9cb7e09f8&quot;&gt;&lt;strong&gt;This blog post&lt;/strong&gt;&lt;/a&gt; covers how these retention policies work in Kafka. How these get configured will depend on the use case of your topic.&lt;/p&gt;
&lt;p&gt;That said, there are still valid reasons to truncate a topic on a running Kafka cluster. For instance, you may want to reset a topic to a specific state for testing or debugging purposes, or you may have encountered a production issue that requires you to delete a range of records from a topic. In these cases, truncation can be a useful tool.&lt;/p&gt;
&lt;p&gt;If you do decide to truncate a topic, it’s important to be aware of the potential impacts on your Kafka cluster and consumers. For example, truncating a topic may cause consumers to experience data gaps or inconsistencies. As a best practice, you should always test truncation in a non-production environment before running it in a production context.&lt;/p&gt;
&lt;h2 id=&quot;3-tombstoning-records&quot;&gt;3. Tombstoning Records&lt;/h2&gt;
&lt;p&gt;The final and most granular way of a deleting record in Kafka is via tombstoning. Tombstoning deletes an individual record based on its key.&lt;/p&gt;
&lt;h3 id=&quot;how-tombstoning-works-in-kafka&quot;&gt;How tombstoning works in Kafka&lt;/h3&gt;
&lt;p&gt;Tombstoning works by producing a record with a &lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt; value and the key of the record that needs to be deleted to a topic. &lt;strong&gt;Note&lt;/strong&gt; : null in this case means a value of 0 bytes. For example, producing the value &lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt; with a JSON serializer will not have the same effect.&lt;/p&gt;
&lt;p&gt;Tombstoning allows you to delete individual records from a topic without affecting the rest of the data in the topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; tombstoning will only work when the topic has been configured with a &lt;strong&gt;&lt;code&gt;compact.policy&lt;/code&gt;&lt;/strong&gt; of &lt;strong&gt;&lt;code&gt;compact&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;compact,delete&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;compacted-topics&quot;&gt;Compacted topics&lt;/h4&gt;
&lt;p&gt;Compacted topics in Kafka ensure that only the latest record per message key is retained within the log of data for a single topic partition. This policy is useful for implementing key/value stores or aggregated views where only the most recent state is needed.&lt;/p&gt;
&lt;p&gt;For example, a KTable that holds the latest count of Covid-19 cases by country, where each record is keyed by the country, would benefit from a compacted topic.&lt;/p&gt;
&lt;p&gt;It is important to note that compaction does not happen automatically and how often it happens depends on your topic and broker configuration. Therefore, deletion does not occur automatically after a tombstone record is produced.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@damienthomlutz/deleting-records-in-kafka-aka-tombstones-651114655a16&quot;&gt;&lt;strong&gt;This blog post&lt;/strong&gt;&lt;/a&gt; goes into finer details about the different broker/topic configuration that can have an impact on when compaction happens.&lt;/p&gt;
&lt;h3 id=&quot;producing-tombstone-messages-in-kpow&quot;&gt;Producing tombstone messages in Kpow&lt;/h3&gt;
&lt;p&gt;First, we can ensure that compaction has been enabled on our topic by navigating to the Topic Configuration table and selecting our topic and the config value &lt;strong&gt;&lt;code&gt;cleanup.policy&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa9492_config-compaction.png&quot; alt=&quot;Topic Configuration&quot;&gt;&lt;/p&gt;
&lt;p&gt;If &lt;strong&gt;&lt;code&gt;cleanup.policy&lt;/code&gt;&lt;/strong&gt; hasn’t been correctly set, we can click the pencil icon to edit the topic configuration and set it to &lt;strong&gt;&lt;code&gt;compact,delete&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Next, navigate to Kpow’s Data Produce UI and select &lt;strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;/strong&gt; for the value serializer while specifying the key you wish to delete.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa948b_topic-produce-tombstone.png&quot; alt=&quot;Topic Produce Tombstone&quot;&gt;&lt;/p&gt;
&lt;p&gt;Done! You have successfully produced a tombstone message!&lt;/p&gt;
&lt;h3 id=&quot;querying-for-data-to-be-deleted&quot;&gt;Querying for data to be deleted&lt;/h3&gt;
&lt;p&gt;We can use Kpow to query for data we want to tombstone on a topic.&lt;/p&gt;
&lt;p&gt;For example. Consider a topic that contains the following data:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; &quot;name&quot;: &quot;John Smith&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; &quot;score&quot;: 10,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; &quot;expires&quot;: &quot;2022-10-10&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s say we want to query for all records that have expired, we could write a &lt;a href=&quot;https://docs.kpow.io/features/data-inspect/#filtering&quot;&gt;&lt;strong&gt;kJQ&lt;/strong&gt;&lt;/a&gt; query like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.value.expires | from-date &amp;#x3C; now&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;kJQ is Kpow’s powerful query language for searching data on a Kafka topic. It is our implementation of the &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;&lt;strong&gt;jq&lt;/strong&gt;&lt;/a&gt; language with added features built specifically for Kafka.&lt;/p&gt;
&lt;p&gt;The above query parses the &lt;strong&gt;&lt;code&gt;expires&lt;/code&gt;&lt;/strong&gt; field as an ISO 8601 date time and checks if its before the current date time (now). &lt;strong&gt;&lt;code&gt;now&lt;/code&gt;&lt;/strong&gt; will get resolved as the current date during query execution time.&lt;/p&gt;
&lt;p&gt;After executing this query in Kpow, we can see a list of results that match our filtered query. These are the expired records!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90702208acfba5efa9495_tombstone-query.png&quot; alt=&quot;Tombstone Query&quot;&gt;&lt;/p&gt;
&lt;p&gt;We can now click the ‘Produce results’ button and produce these records back to the topic as tombstones, by selecting the value serializer as &lt;strong&gt;&lt;code&gt;None&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Done! We have managed to delete a collection of records based on a query filter.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article we have demonstrated the various ways you can delete records in Kafka using Kpow.&lt;/p&gt;
&lt;p&gt;You should now have a better understanding of deletion, understanding the different implications between each method, and when they might be applicable to use.&lt;/p&gt;
&lt;p&gt;Get started with a &lt;a href=&quot;https://factorhouse.io/products/kpow&quot;&gt;free 30-day trial&lt;/a&gt; of Kpow today!&lt;/p&gt;
</content:encoded><category>How-to</category><author>Thomas Crowley</author></item><item><title>Manage Kafka Visibility with Multi-Tenancy</title><link>https://factorhouse.io/articles/manage-kafka-visibility-with-multi-tenancy/</link><guid isPermaLink="true">https://factorhouse.io/articles/manage-kafka-visibility-with-multi-tenancy/</guid><description>This article teaches you how to configure Kpow to restrict visibility of Kafka resources with Multi-Tenancy.</description><pubDate>Mon, 16 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article teaches you how to configure Kpow to restrict visibility of Kafka resources with Multi-Tenancy.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kpow provides sophisticated &lt;a href=&quot;https://docs.kpow.io/authorization/role-based-access-control&quot;&gt;&lt;strong&gt;Role Based Access Control&lt;/strong&gt;&lt;/a&gt; to allow, deny, or stage user actions for any Kafka resource, to a group or topic level. However, for some of our users controlling the actions that a user can take wasn’t quite enough.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“I have hundreds of topics and groups, showing users all of them is confusing. Can I restrict visibility of resources with RBAC?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The best part of working on Kpow is understanding the needs of engineering teams who use Apache Kafka. On the face of it using RBAC to restrict user visibility as well control of resources is reasonable, but when we considered the broader idea we understood this is a bigger problem.&lt;/p&gt;
&lt;h2 id=&quot;introducing-multi-tenancy&quot;&gt;Introducing Multi-Tenancy&lt;/h2&gt;
&lt;p&gt;Kpow &lt;a href=&quot;https://docs.kpow.io/authorization/tenants&quot;&gt;&lt;strong&gt;Multi-Tenancy&lt;/strong&gt;&lt;/a&gt; allows you to assign user roles to one or more &lt;strong&gt;tenants&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each tenant explicitly includes or excludes resources such as Kafka Clusters, Groups, Topics, Schema Registries and Connect Clusters.&lt;/p&gt;
&lt;p&gt;A user role may be assigned multiple tenants, and a user with multiple tenants has the ability to easily switch between them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90783000c919504b9644d_tenant-select.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;When operating within a tenant a user can only see resources included by that tenant or create resources that would be valid within that tenant.&lt;/p&gt;
&lt;p&gt;Importantly, &lt;em&gt;users will see a fully consistent synthetic cluster-view of their aggregated resources&lt;/em&gt;. The overall user experience is simply of a restricted set of Kafka resources as if they were truly the only resources in the system.&lt;/p&gt;
&lt;p&gt;Now our user with hundreds of groups and topics can configure views for different business units and provide a simplified Kafka experience to their users.&lt;/p&gt;
&lt;h3 id=&quot;tenants-in-action&quot;&gt;Tenants In Action&lt;/h3&gt;
&lt;p&gt;Let’s start at the end, below you can see the Broker UI of two different tenants operating in the one Kpow instance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Global&lt;/strong&gt; tenant is configured to contain all resources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transaction&lt;/strong&gt; tenant is configured to contain only topics starting with &lt;strong&gt;&lt;code&gt;tx_*&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;global-tenant-ui&quot;&gt;Global Tenant UI&lt;/h4&gt;
&lt;p&gt;We can see 233 topics in the global tenant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90783000c919504b96450_tenant-global.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;transaction-tenant-ui&quot;&gt;Transaction Tenant UI&lt;/h4&gt;
&lt;p&gt;The transaction tenant only shows 200 topics, and they are much more uniform.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90783000c919504b96448_tenant-tx.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;A user can switch between these two tenants if they have roles with each tenant assigned. Kpow continues to observe and control all attached Kafka resources, but provides a consistent view of synthetic clusters constructed of only the groups and topics included in each tenant. Aggregated metrics like write/s and total disk space can be seen in either view with different figures.&lt;/p&gt;
&lt;h3 id=&quot;uses-of-multi-tenancy&quot;&gt;Uses of Multi-Tenancy&lt;/h3&gt;
&lt;p&gt;The primary intended use of Multi-Tenancy is for you to provide restricted views of Kafka resources to users from different teams in your organization.&lt;/p&gt;
&lt;p&gt;However with the growth of Managed Kafka Services you may also want to configure basic tenants that exclude topics and groups of no regular interest.&lt;/p&gt;
&lt;p&gt;Kpow stores all information regarding your Kafka resources in internal topics within your cluster, including an audit log of user actions. Kpow is also constructed of two Kafka Streams applications that run in unison to build the telemetry presented back to you.&lt;/p&gt;
&lt;p&gt;A common user request has been to hide these internal topics and groups in the general UI as they’re not of interest to our end users. Previously this had been a complicated task of in-place exclusion in the front-end, but aggregated metrics were hard to achieve.&lt;/p&gt;
&lt;p&gt;If you have no tenants configured Kpow automatically provides two. A &lt;strong&gt;Global&lt;/strong&gt; tenant that shows all attached Kafka resources and a &lt;strong&gt;Kpow Hidden&lt;/strong&gt; tenant that hides Kpow resources and the consumer offsets topic.&lt;/p&gt;
&lt;p&gt;You may want to provide tenants for specific business units, or you might just want to exclude internal topics from your cloud or managed service provider, or both!&lt;/p&gt;
&lt;h3 id=&quot;get-started&quot;&gt;Get Started&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.kpow.io/authorization/tenants&quot;&gt;&lt;strong&gt;Multi-Tenancy&lt;/strong&gt;&lt;/a&gt; is related to &lt;a href=&quot;https://docs.kpow.io/authorization/role-based-access-control&quot;&gt;&lt;strong&gt;Role Based Access Control&lt;/strong&gt;&lt;/a&gt; and both require &lt;a href=&quot;https://docs.kpow.io/authentication/overview&quot;&gt;&lt;strong&gt;User Authentication&lt;/strong&gt;&lt;/a&gt; which is not available to trial users.&lt;/p&gt;
&lt;p&gt;If you are on trial and would like to explore any of these features please request a license upgrade from &lt;a href=&quot;mailto:sales@factorhouse.io&quot;&gt;&lt;strong&gt;&lt;a href=&quot;mailto:sales@factorhouse.io&quot;&gt;sales@factorhouse.io&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;tenants&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Global&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;All configured Kafka resources.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kpow Hidden&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;All configured Kafka resources except internal Kpow resources and __consumer_offsets.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt; exclude&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;__oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;__consumer_offsets&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;          -&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;*&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within your &lt;a href=&quot;https://docs.kpow.io/authorization/role-based-access-control&quot;&gt;&lt;strong&gt;RBAC yaml configuration file&lt;/strong&gt;&lt;/a&gt; you can specify a top-level &lt;strong&gt;tenants&lt;/strong&gt; key:&lt;/p&gt;
&lt;p&gt;The following example configuration matches the &lt;strong&gt;default tenants&lt;/strong&gt; that Kpow provides if you have none configured.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;tenants&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Global&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;All configured Kafka resources.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;Kpow Hidden&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;All configured Kafka resources except internal Kpow resources and __consumer_offsets.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    resources&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#22863A&quot;&gt;exclude&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;__oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;__consumer_offsets&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;          - [ &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;oprtr*&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A&quot;&gt;    roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more information about Multi-Tenancy see our &lt;a href=&quot;https://docs.kpow.io/authorization/tenants&quot;&gt;&lt;strong&gt;online documentation&lt;/strong&gt;&lt;/a&gt;, for help contact &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;&lt;strong&gt;&lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Derek Troy-West</author></item><item><title>Manage Temporary Access to Kafka Resources</title><link>https://factorhouse.io/articles/manage-temporary-access-to-kafka-resources/</link><guid isPermaLink="true">https://factorhouse.io/articles/manage-temporary-access-to-kafka-resources/</guid><description>Temporary policies allow Admins the ability to assign access control policies for a fixed duration. This blog post introduces temporary policies with an all-to-common real-world scenario.</description><pubDate>Wed, 07 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article introduces Kpow for Apache Kafka®’s new Temporary Policies feature.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;introducing-temporary-policies&quot;&gt;Introducing Temporary Policies&lt;/h2&gt;
&lt;p&gt;Introduced to the Kpow Kafka Management and Monitoring toolkit in v79 is the ability to &lt;strong&gt;Stage Mutations&lt;/strong&gt; , create &lt;strong&gt;Temporary Role Based Access Control Policies&lt;/strong&gt; (temporary policies), and a suite of &lt;a href=&quot;https://factorhouse.io/blog/releases/79/&quot;&gt;&lt;strong&gt;new admin features&lt;/strong&gt;&lt;/a&gt; giving greater control over Kpow to Admin Users.&lt;/p&gt;
&lt;p&gt;This blog post introduces temporary policies through the lense of a common real-world scenario.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Temporary policies allow Admins the ability to assign access control policies for a fixed duration.&lt;/strong&gt; A common use-case would be providing a user TOPIC_INSPECT access to read data from a topic for an hour while resolving an issue in a Production environment.&lt;/p&gt;
&lt;h3 id=&quot;temporary-policies-use-case&quot;&gt;Temporary Policies Use Case&lt;/h3&gt;
&lt;p&gt;You wake up one morning to a dreaded sight: a poison message has taken down one of your services.&lt;/p&gt;
&lt;p&gt;Your team decides the simplest solution is to skip the message by incrementing your consumer group’s offset for the topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Now here’s the problem.&lt;/strong&gt; Access to production is limited, and for such a simple action (incrementing the offset), a team member generally must jump through the hoops of configuring the VPN, connecting to the jumpbox, and making sure they execute the right combination of bash commands against the Kafka cluster.&lt;/p&gt;
&lt;p&gt;Often these operations are unnecessarily time-consuming, brittle, and frustrating in a time-critical moment when you need to restore production access. Furthermore, the jumpbox generally has full access to the Kafka cluster, and there is no audit log recording the actions being committed.&lt;/p&gt;
&lt;p&gt;In combination with Kpow’s existing Role-Based Access Controls and powerful mutation actions, Temporary Policies improve this experience by giving teams the tools they need to easily effect change in a secured environment, like production, when things go wrong.&lt;/p&gt;
&lt;h3 id=&quot;configuring-role-based-access-control&quot;&gt;Configuring Role-Based Access Control&lt;/h3&gt;
&lt;p&gt;In this example, two roles are coming from our Identity provider: &lt;strong&gt;&lt;code&gt;devs&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;owners&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We will assign anyone with the role &lt;strong&gt;&lt;code&gt;owners&lt;/code&gt;&lt;/strong&gt; admin access, and give them &lt;strong&gt;&lt;code&gt;GROUP_EDIT&lt;/code&gt;&lt;/strong&gt; access to the production cluster.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;devs&lt;/code&gt;&lt;/strong&gt; role will be &lt;strong&gt;implicitly denied&lt;/strong&gt; from undertaking any action against the cluster, but are authorized for read-only access to view the production cluster in Kpow.&lt;/p&gt;
&lt;p&gt;Our example RBAC yaml file might look something like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;admin_roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;owners&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;authorized_roles&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;owners&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;devs&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;policies&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;  -&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    actions&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#005CC5&quot;&gt; GROUP_EDIT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    effect&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: Allow&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    resource&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt; &quot;*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1&quot;&gt;    role&lt;/span&gt;&lt;span style=&quot;color:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62&quot;&gt;&quot;owners&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration prevents regular developers from making changes against the production cluster.&lt;/p&gt;
&lt;h3 id=&quot;the-poison-pill&quot;&gt;The Poison Pill&lt;/h3&gt;
&lt;p&gt;Today is the day when your team has to fix the consumer group on the production cluster.&lt;/p&gt;
&lt;p&gt;Everyone has been briefed on the plan, and it has been decided that the team lead will temporarily grant the &lt;strong&gt;&lt;code&gt;devs&lt;/code&gt;&lt;/strong&gt; role &lt;strong&gt;&lt;code&gt;Allow&lt;/code&gt;&lt;/strong&gt; access for &lt;strong&gt;&lt;code&gt;GROUP_EDIT&lt;/code&gt;&lt;/strong&gt;. This will enable one of the developers on the team to make the required change to the production cluster.&lt;/p&gt;
&lt;p&gt;This has been done through the &lt;strong&gt;Temporary Policies&lt;/strong&gt; section of Kpow’s settings UI:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90bf585841bbd5c9bf933_policies-ui.png&quot; alt=&quot;Temporary policies UI in Kpow&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once a temporary policy has been created, team members can be notified via Slack with the Kpow Slack integration.&lt;/p&gt;
&lt;h3 id=&quot;incrementing-the-offset&quot;&gt;Incrementing the offset&lt;/h3&gt;
&lt;p&gt;A team member has been tasked with the job of incrementing the offset of the consumer group for the problematic topic.&lt;/p&gt;
&lt;p&gt;The developer looks to the application logs and notices that it is partition 3 of topic &lt;strong&gt;&lt;code&gt;tx_trade1&lt;/code&gt;&lt;/strong&gt; that contains the poison message.&lt;/p&gt;
&lt;p&gt;The erroring consumer group is named &lt;strong&gt;&lt;code&gt;trade_b2&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The developer then opens Kpow, navigates to the “Workflows” tab, and selects the consumer group.&lt;/p&gt;
&lt;p&gt;From within the consumer group view, the dev clicks on the partition and selects “Skip Offset”.&lt;/p&gt;
&lt;p&gt;This action will schedule the mutation, and once someone on the team scales down the &lt;strong&gt;&lt;code&gt;trade_b2&lt;/code&gt;&lt;/strong&gt; service, the offset will be incremented.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90bf585841bbd5c9bf942_skip-offset.gif&quot; alt=&quot;Skipping group offsets in Kpow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;post-mortem&quot;&gt;Post-Mortem&lt;/h3&gt;
&lt;p&gt;Kpow also provides valuable information and insights for teams to use after a production incident when you are completing your incident post-mortem.&lt;/p&gt;
&lt;p&gt;Kpow has an Audit Log for Data Governance, and all the actions undertaken to resolve any production incident are persisted in Kpow’s audit log topic. Meaning you can use the Audit Log to see the recorded history of all actions taken to restore the production service.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90bf585841bbd5c9bf939_audit-log.png&quot; alt=&quot;Kpow’s audit log&quot;&gt;&lt;/p&gt;
&lt;p&gt;Inspecting the audit log message reveals the offset that was skipped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90bf585841bbd5c9bf936_audit-log-item.png&quot; alt=&quot;Audit log message&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can use Kpow’s data inspect functionality to view the poison message to help investigate why that message took down the consumer group.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90bf585841bbd5c9bf93d_data-inspect.png&quot; alt=&quot;Kpow’s data inspect functionality&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can find further information on setting up, viewing and managing temporary policies &lt;a href=&quot;https://docs.kpow.io/authorization/administration/temporary-policies&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;further-readingreferences&quot;&gt;Further reading/references&lt;/h3&gt;
&lt;p&gt;Explore our documentation to learn more about the Kpow’s features mentioned in this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.kpow.io/authorization/role-based-access-control&quot;&gt;&lt;strong&gt;Role-Based Access Control&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.kpow.io/authorization/administration/temporary-policies&quot;&gt;&lt;strong&gt;Temporary Policies&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.kpow.io/mutations/group-actions&quot;&gt;&lt;strong&gt;Group Actions&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might also be interested in the following articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://factorhouse.io/blog/how-to/kafka-alerting-with-kpow-prometheus-and-alertmanager/&quot;&gt;&lt;strong&gt;Kafka Alerting with Kpow, Prometheus and Alertmanager&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Manage, Monitor and Learn Apache Kafka with &lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;Kpow&lt;/a&gt; by &lt;a href=&quot;https://factorhouse.io/&quot;&gt;Factor House&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We know how easy Apache Kafka® can be with the right tools. We built Kpow to make the developer experience with Kafka simple and enjoyable, and to save businesses time and money while growing their Kafka expertise. A single Docker container or JAR file that installs in minutes, Kpow’s unique Kafka UI gives you instant visibility of your clusters and immediate access to your data.&lt;/p&gt;
&lt;p&gt;Kpow is compatible with Apache Kafka+1.0, Red Hat AMQ Streams, Amazon MSK, Instaclustr, Aiven, Vectorized, Azure Event Hubs, Confluent Platform, and Confluent Cloud.&lt;/p&gt;
&lt;p&gt;Start with a &lt;a href=&quot;https://factorhouse.io/kpow/get-started/&quot;&gt;&lt;strong&gt;free 30-day trial&lt;/strong&gt;&lt;/a&gt; and solve your Kafka issues within minutes.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Thomas Crowley</author></item><item><title>Deploy Clojure Projects to Maven Central with Leiningen</title><link>https://factorhouse.io/articles/deploy-clojure-projects-to-maven-central/</link><guid isPermaLink="true">https://factorhouse.io/articles/deploy-clojure-projects-to-maven-central/</guid><description>This article provides a step-by-step guide to how we deploy the Kpow Kafka Streams Monitoring Agent to Maven Central with Leiningen.</description><pubDate>Wed, 30 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article dives into deploying Clojure projects to Maven.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;maven-central-and-clojure&quot;&gt;Maven Central and Clojure&lt;/h2&gt;
&lt;p&gt;We have just released our first open-source consumable: &lt;a href=&quot;https://github.com/factorhouse/kpow-streams-agent&quot;&gt;&lt;strong&gt;kpow-streams-agent&lt;/strong&gt;&lt;/a&gt;, a monitoring tool for &lt;a href=&quot;https://kafka.apache.org/documentation/streams/&quot;&gt;&lt;strong&gt;Kafka Streams&lt;/strong&gt;&lt;/a&gt;, to &lt;a href=&quot;https://search.maven.org/artifact/io.operatr/kpow-streams-agent&quot;&gt;&lt;strong&gt;Maven Central&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Kpow Streams Agent integrates your Kafka Streams topologies with Kpow, offering near-realtime monitoring and visualisation of your streaming compute:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90dd3dc82cbbeda7558dd_streams-ui.png&quot; alt=&quot;Kpow for Apache Kafka Streams UI&quot;&gt;&lt;/p&gt;
&lt;p&gt;We intend for this software to be used by the wider JVM ecosystem (eg, Java, Kotlin, Scala), so would like our library to be available on Maven Central.&lt;/p&gt;
&lt;p&gt;There weren’t many resources online documenting anyone’s experience deploying Clojure-centric software to Maven Central and the few resources that I came across were out of date with the current requirements (as of June 2021).&lt;/p&gt;
&lt;p&gt;This blog post documents the steps needed to make your Clojure code available to a wider audience.&lt;/p&gt;
&lt;h3 id=&quot;create-a-sonatype-account&quot;&gt;Create a Sonatype account&lt;/h3&gt;
&lt;p&gt;The entire process for claiming your own namespace on Maven Central starts with creating a JIRA ticket. This seemed a bit archaic to us, coming from Clojars, NPM, and Crates.io backgrounds. Certainly, with these modern package managers, the integration between language/ecosystem/registry seems a lot more streamlined, thus publishing software much easier.&lt;/p&gt;
&lt;p&gt;We weren’t sure if this would be an automated process or if we would have to wait for a human to manually approve our request. We were relieved that it was indeed somewhat automated, with a bot automatically approving each step.&lt;/p&gt;
&lt;h3 id=&quot;sign-up-to-jira&quot;&gt;Sign up to JIRA&lt;/h3&gt;
&lt;p&gt;The first step is to create an account on the &lt;a href=&quot;https://issues.sonatype.org/&quot;&gt;&lt;strong&gt;Sonatype JIRA&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The credentials you provide here will also be the same credentials you use to deploy, so keep that in mind as you proceed.&lt;/p&gt;
&lt;h3 id=&quot;create-a-new-ticket&quot;&gt;Create a new ticket&lt;/h3&gt;
&lt;p&gt;Once you have signed up for the JIRA, create a &lt;a href=&quot;https://issues.sonatype.org/secure/CreateIssue!default.jspa&quot;&gt;&lt;strong&gt;new ticket&lt;/strong&gt;&lt;/a&gt; and choose the issue type “New Project”.&lt;/p&gt;
&lt;p&gt;This is where you claim your &lt;strong&gt;&lt;code&gt;group.id&lt;/code&gt;&lt;/strong&gt; on Maven Central. This must be related to a website domain you own. For us, we claimed &lt;strong&gt;&lt;code&gt;io.operatr&lt;/code&gt;&lt;/strong&gt; as our companies domain is &lt;a href=&quot;https://factorhouse.io/&quot;&gt;&lt;strong&gt;operatr.io&lt;/strong&gt;&lt;/a&gt;. You will need to prove ownership of your domain for the next section.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90dd3dc82cbbeda7558e4_jira-ticket.png&quot; alt=&quot;Creating a Sonatype JIRA ticket&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;add-a-txt-entry-to-your-domain&quot;&gt;Add a TXT entry to your domain&lt;/h3&gt;
&lt;p&gt;Once you have submitted your JIRA ticket, a bot should automatically reply to your issue within a few minutes asking for verification of your domain. The simplest way to verify your domain is to create a TXT entry.&lt;/p&gt;
&lt;p&gt;For example, if you use Cloudflare to manage your DNS records you could follow &lt;a href=&quot;https://support.knowbe4.com/hc/en-us/articles/115015835387-How-Can-I-Add-a-TXT-Record-to-My-DNS-Records-&quot;&gt;&lt;strong&gt;these steps&lt;/strong&gt;&lt;/a&gt; to add a TXT entry to your domain.&lt;/p&gt;
&lt;p&gt;You will need to create a TXT entry containing the Jira issue ID of your ticket (for example &lt;strong&gt;&lt;code&gt;OSSRH-70400&lt;/code&gt;&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;Once you have added the TXT entry to your domain, the bot should automatically reply and confirm that your group.id has been prepared&lt;/p&gt;
&lt;h3 id=&quot;deploy-requirements&quot;&gt;Deploy Requirements&lt;/h3&gt;
&lt;p&gt;You will now be granted the ability to deploy snapshot and release artifacts to &lt;strong&gt;&lt;code&gt;s01.oss.sonatype.org&lt;/code&gt;&lt;/strong&gt; for the &lt;strong&gt;&lt;code&gt;group.id&lt;/code&gt;&lt;/strong&gt; you have just registered.&lt;/p&gt;
&lt;p&gt;All artifacts you deploy here are staged and can only be promoted to Maven Central &lt;strong&gt;if&lt;/strong&gt; they meet the requirements.&lt;/p&gt;
&lt;p&gt;This section will document how you can configure your &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; to meet the &lt;a href=&quot;https://central.sonatype.org/publish/requirements/&quot;&gt;&lt;strong&gt;Sonatype requirements&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;gpg-keys&quot;&gt;GPG Keys&lt;/h3&gt;
&lt;p&gt;Firstly, we want to create a GPG key to sign our release. Lein’s &lt;a href=&quot;https://github.com/technomancy/leiningen/blob/stable/doc/GPG.md&quot;&gt;&lt;strong&gt;GPG Guide&lt;/strong&gt;&lt;/a&gt; is a good starting place on how you can do that.&lt;/p&gt;
&lt;p&gt;Once you have created your GPG key you will need to upload your public key to a keyserver, such as &lt;a href=&quot;https://keyserver.ubuntu.com/&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://keyserver.ubuntu.com/&quot;&gt;https://keyserver.ubuntu.com/&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In order to do this, you can export your GPG public key with the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg --armor --export $MY_EMAIL&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;credentials&quot;&gt;Credentials&lt;/h3&gt;
&lt;p&gt;Next, you will need to update your &lt;strong&gt;&lt;code&gt;~/.lein/credentials.clj&lt;/code&gt;&lt;/strong&gt; file to include your Sonatype credentials:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{#&quot;https://s01.oss.sonatype.org/.*&quot; {:username &quot;JIRA_USERNAME&quot; :password &quot;JIRA_PASSWORD&quot;}}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have created &lt;strong&gt;&lt;code&gt;credentials.clj&lt;/code&gt;&lt;/strong&gt; you will need to encrypt it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg --default-recipient-self -e \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ~/.lein/credentials.clj &gt; ~/.lein/credentials.clj.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you will need to setup the correct &lt;strong&gt;&lt;code&gt;:deploy-repositories&lt;/code&gt;&lt;/strong&gt; within your project’s &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:deploy-repositories [[&quot;releases&quot; {:url   &quot;https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                   :creds :gpg}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                       &quot;snapshots&quot; {:url   &quot;https://s01.oss.sonatype.org/content/repositories/snapshots/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    :creds :gpg}]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;source-and-javadoc-jars&quot;&gt;Source and Javadoc Jars&lt;/h3&gt;
&lt;p&gt;It is a requirement to include both a &lt;strong&gt;&lt;code&gt;-sources.jar&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;-javadoc.jar&lt;/code&gt;&lt;/strong&gt; jar as part of your deployment.&lt;/p&gt;
&lt;p&gt;These requirements are tailored more towards Java codebases than Clojure:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;If, for some reason (for example, license issue or it’s a Scala project), you can not provide -sources.jar or -javadoc.jar , please make fake -sources.jar or -javadoc.jar with simple README inside to pass the checking&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can create &lt;strong&gt;&lt;code&gt;-sources.jar&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;-javadoc.jar&lt;/code&gt;&lt;/strong&gt; jars by using a &lt;strong&gt;&lt;code&gt;:classifiers&lt;/code&gt;&lt;/strong&gt; key in lein:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:classifiers [[&quot;sources&quot; {:source-paths      ^:replace []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                          :java-source-paths ^:replace [&quot;src/java&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                          :resource-paths    ^:replace []}]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              [&quot;javadoc&quot; {:source-paths      ^:replace []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                          :java-source-paths ^:replace []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                          :resource-paths    ^:replace [&quot;javadoc&quot;]}]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This specific example will bundle Java source code in the &lt;strong&gt;&lt;code&gt;sources&lt;/code&gt;&lt;/strong&gt; jar, and Java docs in the &lt;strong&gt;&lt;code&gt;javadoc&lt;/code&gt;&lt;/strong&gt; jar.&lt;/p&gt;
&lt;p&gt;If you intend to create “fake” source jars, you could leave the source paths and resource paths empty.&lt;/p&gt;
&lt;h3 id=&quot;project-details&quot;&gt;Project Details&lt;/h3&gt;
&lt;p&gt;In order to meet the &lt;a href=&quot;https://central.sonatype.org/publish/requirements/#project-name-description-and-url&quot;&gt;&lt;strong&gt;Project name, description and URL&lt;/strong&gt;&lt;/a&gt; requirements, you will need to correctly populate the following keys in &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:description &quot;A Clojure project deployed to Maven&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:url &quot;https://github.com/org/repo&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;license-information&quot;&gt;License Information&lt;/h3&gt;
&lt;p&gt;In order to meet the &lt;a href=&quot;https://central.sonatype.org/publish/requirements/#license-information&quot;&gt;&lt;strong&gt;License information&lt;/strong&gt;&lt;/a&gt; requirement you will need to correctly populate the &lt;strong&gt;&lt;code&gt;:license&lt;/code&gt;&lt;/strong&gt; key in &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:license {:name         &quot;Apache-2.0 License&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          :url          &quot;https://www.apache.org/licenses/LICENSE-2.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          :distribution :repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          :comments     &quot;same as Kafka&quot;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;developer-information&quot;&gt;Developer Information&lt;/h3&gt;
&lt;p&gt;In order to meet the &lt;a href=&quot;https://central.sonatype.org/publish/requirements/#developer-information&quot;&gt;&lt;strong&gt;Developer Information&lt;/strong&gt;&lt;/a&gt; requirement you will need to correctly structure your &lt;strong&gt;&lt;code&gt;:pom-additions&lt;/code&gt;&lt;/strong&gt; key in &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:pom-addition ([:developers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                [:developer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                 [:id &quot;johnsmith&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                 [:name &quot;John Smith&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                 [:url &quot;https://mycorp.org&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                 [:roles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  [:role &quot;developer&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  [:role &quot;maintainer&quot;]]]])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;scm-information&quot;&gt;SCM Information&lt;/h3&gt;
&lt;p&gt;In order to meet the &lt;a href=&quot;https://central.sonatype.org/publish/requirements/#scm-information&quot;&gt;&lt;strong&gt;SCM Information&lt;/strong&gt;&lt;/a&gt; requirement, you will need to correctly populate the &lt;strong&gt;&lt;code&gt;:scm&lt;/code&gt;&lt;/strong&gt; key in &lt;strong&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;/strong&gt; like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:scm {:name &quot;git&quot; :url &quot;https://github.com/org/repo&quot;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;deploying-to-central&quot;&gt;Deploying to Central&lt;/h2&gt;
&lt;p&gt;If you have followed all of the steps from the previous section, you should have a lein project that meets all Sonatype requirements and is ready to be deployed!&lt;/p&gt;
&lt;p&gt;You can do this via a regular:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lein deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have deployed your artifacts, the next step is to log in to the Nexus Repository Manager at &lt;a href=&quot;https://s01.oss.sonatype.org/#welcome&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://s01.oss.sonatype.org/&quot;&gt;https://s01.oss.sonatype.org/&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;. Again, your JIRA credentials from before are used to log in.&lt;/p&gt;
&lt;p&gt;Once inside the, navigate to “Staging Repositories” - you should see an entry labeled &lt;strong&gt;&lt;code&gt;XXX-1000&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Click on this item and verify that the contents you wish to deploy to Central are present.&lt;/p&gt;
&lt;p&gt;If everything looks good, click the “Close” button. This will trigger the requirements check&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f90dd3dc82cbbeda7558e0_nexus.png&quot; alt=&quot;The Nexus Repository Manager&quot;&gt;&lt;/p&gt;
&lt;p&gt;If the requirements check passes, you will be able to press the “Release” button. Once you press this button, your release will shortly be synced with Maven Central!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : It can take up to 4 hours for the sync with &lt;a href=&quot;https://search.maven.org/&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://search.maven.org/&quot;&gt;https://search.maven.org/&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;further-reading-and-references&quot;&gt;Further Reading and References&lt;/h3&gt;
&lt;p&gt;The following resources might be useful for more information about deployments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md&quot;&gt;&lt;strong&gt;Lein deployment docs&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/factorhouse/kpow-streams-agent/blob/main/project.clj&quot;&gt;&lt;strong&gt;Sample project.clj&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://central.sonatype.org/publish/requirements/&quot;&gt;&lt;strong&gt;Sonatype requirements&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://central.sonatype.org/publish/release/&quot;&gt;&lt;strong&gt;Releasing Sonatype artifacts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Manage, Monitor and Learn Apache Kafka with &lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;Kpow&lt;/a&gt; by &lt;a href=&quot;https://factorhouse.io/&quot;&gt;Factor House&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We know how easy Apache Kafka® can be with the right tools. We built Kpow to make the developer experience with Kafka simple and enjoyable, and to save businesses time and money while growing their Kafka expertise. A single Docker container or JAR file that installs in minutes, Kpow’s unique Kafka UI gives you instant visibility of your clusters and immediate access to your data.&lt;/p&gt;
&lt;p&gt;Kpow is compatible with Apache Kafka+1.0, Red Hat AMQ Streams, Amazon MSK, Instaclustr, Aiven, Vectorized, Azure Event Hubs, Confluent Platform, and Confluent Cloud.&lt;/p&gt;
&lt;p&gt;Start with a &lt;a href=&quot;https://factorhouse.io/kpow/get-started/&quot;&gt;&lt;strong&gt;free 30-day trial&lt;/strong&gt;&lt;/a&gt; and solve your Kafka issues within minutes.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Thomas Crowley</author></item><item><title>Run Kpow in Kubernetes with Helm</title><link>https://factorhouse.io/articles/run-kpow-in-kubernetes-with-helm/</link><guid isPermaLink="true">https://factorhouse.io/articles/run-kpow-in-kubernetes-with-helm/</guid><description>This article covers running Kpow in Kubernetes using the Kpow Helm Chart. Introduction Kpow is the all-in-one toolkit to manage, monitor, and learn about your Kafka resources. Helm is the package manager for Kubernetes. Helm deploys charts, which you can think of as a packaged application. We publish...</description><pubDate>Tue, 01 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article covers running Kpow in Kubernetes using the &lt;a href=&quot;https://github.com/factorhouse/kpow-helm-charts&quot;&gt;Kpow Helm Chart&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 24-01-2023&lt;/strong&gt; : The process for installing Kpow with Helm has changed since this article was originally published.&lt;/p&gt;
&lt;p&gt;You are advised to follow the instructions described in the &lt;a href=&quot;https://github.com/factorhouse/kpow-helm-charts&quot;&gt;&lt;strong&gt;Kpow Helm Charts Readme&lt;/strong&gt;&lt;/a&gt; on Github.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The article is left intact below for archive purposes only&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;&lt;strong&gt;Kpow&lt;/strong&gt;&lt;/a&gt; is the all-in-one toolkit to manage, monitor, and learn about your Kafka resources.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://helm.sh/&quot;&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/a&gt; is the package manager for Kubernetes. Helm deploys charts, which you can think of as a packaged application.&lt;/p&gt;
&lt;p&gt;We publish a &lt;a href=&quot;https://github.com/factorhouse/kpow-helm-charts&quot;&gt;&lt;strong&gt;Helm chart for Kpow&lt;/strong&gt;&lt;/a&gt; in our &lt;a href=&quot;https://charts.kpow.io/&quot;&gt;&lt;strong&gt;Helm Chart Repository&lt;/strong&gt;&lt;/a&gt;. You can view the details of both in &lt;a href=&quot;https://artifacthub.io/packages/search?repo=kpow&quot;&gt;&lt;strong&gt;ArtifactHUB&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before we can install Kpow we need to obtain a trial license, configure our local environment, and connect to Kubernetes.&lt;/p&gt;
&lt;h3 id=&quot;get-a-license&quot;&gt;Get a License&lt;/h3&gt;
&lt;p&gt;You require a license to run Kpow, sign-up for a &lt;a href=&quot;https://factorhouse.io/kpow/get-started&quot;&gt;&lt;strong&gt;free 30-day trial&lt;/strong&gt;&lt;/a&gt; today.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://docs.kpow.io/installation/aws-marketplace&quot;&gt;&lt;strong&gt;Kpow on the AWS Marketplace&lt;/strong&gt;&lt;/a&gt; to have Kpow billed automatically to your AWS account, no license required.&lt;/p&gt;
&lt;h3 id=&quot;configure-your-environment-environment&quot;&gt;Configure Your Environment Environment&lt;/h3&gt;
&lt;p&gt;You will need to install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html&quot;&gt;&lt;strong&gt;AWS CLI&lt;/strong&gt;&lt;/a&gt; v1.18+&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/&quot;&gt;&lt;strong&gt;Kubectl&lt;/strong&gt;&lt;/a&gt; v1.16+&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://helm.sh/&quot;&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/a&gt; v3.0.0+&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;connect-to-kubernetes&quot;&gt;Connect to Kubernetes&lt;/h3&gt;
&lt;p&gt;Installing Kpow with Helm requires a Kubernetes environment, in this quick-start guide we use &lt;a href=&quot;https://aws.amazon.com/eks/&quot;&gt;&lt;strong&gt;Amazon EKS&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h5 id=&quot;update-your-eks-cluster-configuration&quot;&gt;Update your EKS Cluster Configuration&lt;/h5&gt;
&lt;p&gt;Use the AWS CLI to update your current EKS cluster configuration.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws eks --region &amp;#x3C;your-aws-region&gt; update-kubeconfig --name &amp;#x3C;your-eks-cluster-name&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Updated context arn:aws:eks:&amp;#x3C;your-aws-region&gt;:123123123:cluster/&amp;#x3C;your-eks-cluster-name&gt; in /your/.kube/config&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;confirm-eks-cluster-availability&quot;&gt;Confirm EKS Cluster Availability&lt;/h5&gt;
&lt;p&gt;Use &lt;strong&gt;kubectl&lt;/strong&gt; to check the availability of your configured EKS cluster.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kubectl get svc &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kubernetes   ClusterIP   12.345.6.7   &amp;#x3C;none&gt;        443/TCP   28h&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we’re configured and connected, we’re ready to install Kpow!&lt;/p&gt;
&lt;h3 id=&quot;install-kpow-with-helm&quot;&gt;Install Kpow with Helm&lt;/h3&gt;
&lt;p&gt;Kpow can be installed in Kubernetes with Helm in these simple steps.&lt;/p&gt;
&lt;h3 id=&quot;configure-the-kpow-helm-repository&quot;&gt;Configure the Kpow Helm Repository&lt;/h3&gt;
&lt;p&gt;Register the Kpow Helm repository, then update your Helm repo configuration to make sure you install the latest version of Kpow.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;helm repo add kpow https://charts.kpow.io &amp;#x26;&amp;#x26; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;helm repo update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;get-the-kpow-helm-chart&quot;&gt;Get the Kpow Helm Chart&lt;/h3&gt;
&lt;p&gt;We pull the Kpow Helm chart to a local directory so that we can make configuration changes before installing Kpow.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;helm pull kpow/kpow --untar --untardir .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;update-kpow-configuration&quot;&gt;Update Kpow Configuration&lt;/h3&gt;
&lt;p&gt;The minimum information required by Kpow to operate is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;License Details (sign-up for a &lt;a href=&quot;https://factorhouse.io/kpow/get-started&quot;&gt;&lt;strong&gt;free 30-day trial&lt;/strong&gt;&lt;/a&gt; today)&lt;/li&gt;
&lt;li&gt;Kafka Bootstrap URL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kpow is configured by a ConfigMap containing all of the Environment Variables described in our &lt;a href=&quot;https://docs.kpow.io/&quot;&gt;&lt;strong&gt;documentation&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Edit the ConfigMap and make the changes required for your environment.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vi ./kpow/templates/kpow-config.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;start-kpow&quot;&gt;Start Kpow&lt;/h3&gt;
&lt;p&gt;You are now ready to launch a Kpow instance, in this example we will create and launch in the &lt;strong&gt;operatr-io&lt;/strong&gt; namespace.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;helm install --namespace operatr-io --create-namespace my-kpow ./kpow &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;access-the-kpow-ui&quot;&gt;Access the Kpow UI&lt;/h3&gt;
&lt;p&gt;Now that your instance is running, you can access the UI by running the commands included in the output of the previous command.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export POD_NAME=$(kubectl get pods --namespace operatr-io -l &quot;app.kubernetes.io/name=kpow,app.kubernetes.io/instance=my-kpow&quot; -o jsonpath=&quot;{.items[0].metadata.name}&quot;) &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;echo &quot;Visit http://127.0.0.1:3000 to use your application&quot; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kubectl --namespace operatr-io port-forward $POD_NAME 3000:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Kpow UI is now available on &lt;a href=&quot;http://127.0.0.1:3000/&quot;&gt;&lt;strong&gt;&lt;a href=&quot;http://127.0.0.1:3000&quot;&gt;http://127.0.0.1:3000&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;manage-the-kpow-instance&quot;&gt;Manage the Kpow Instance&lt;/h3&gt;
&lt;p&gt;If you encounter errors installing Kpow or accessing the Kpow UI you can view the installed pods and their logs.&lt;/p&gt;
&lt;h3 id=&quot;view-the-kpow-pod&quot;&gt;View the Kpow Pod&lt;/h3&gt;
&lt;p&gt;Use &lt;strong&gt;kubectl&lt;/strong&gt; to list pods in the &lt;strong&gt;operatr-io&lt;/strong&gt; namespace.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kubectl describe pods --namespace operatr-io&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;view-the-kpow-pod-logs&quot;&gt;View the Kpow Pod Logs&lt;/h3&gt;
&lt;p&gt;Using the &lt;strong&gt;name&lt;/strong&gt; of the pod from the previous command output, use &lt;strong&gt;kubectl&lt;/strong&gt; to view the pod logs&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;kubectl logs --namespace operatr-io my-kpow-9988df6b6-vvf8z &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;delete-kpow&quot;&gt;Delete Kpow&lt;/h3&gt;
&lt;p&gt;Removing the Kpow instance is simple with Helm.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;helm delete --namespace operatr-io my-kpow&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next Steps&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you have any issues or would like to walk through your Kubernetes use cases with us, contact &lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;&lt;strong&gt;&lt;a href=&quot;mailto:support@factorhouse.io&quot;&gt;support@factorhouse.io&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Visit &lt;a href=&quot;https://docs.kpow.io/&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.kpow.io&quot;&gt;https://docs.kpow.io&lt;/a&gt;&lt;/strong&gt;&lt;/a&gt; for the full list of configuration options and Kpow features available to you.&lt;/li&gt;
&lt;li&gt;Check out our &lt;a href=&quot;https://docs.kpow.io/installation/aws-marketplace&quot;&gt;&lt;strong&gt;AWS Marketplace guide&lt;/strong&gt;&lt;/a&gt; for details of running Kpow in EKS billed automatically to your AWS account.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>How-to</category><author>Derek Troy-West</author></item><item><title>Kafka Alerting with Kpow, Prometheus and Alertmanager</title><link>https://factorhouse.io/articles/kafka-alerting-with-kpow-prometheus-and-alertmanager/</link><guid isPermaLink="true">https://factorhouse.io/articles/kafka-alerting-with-kpow-prometheus-and-alertmanager/</guid><description>This article covers setting up alerting with Kpow using Prometheus and Alertmanager. Introduction Kpow was built from our own need to monitor Kafka clusters and related resources (eg, Streams, Connect and Schema Registries). Through Kpow&apos;s user interface we can detect and even predict potential problems...</description><pubDate>Mon, 24 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;This article covers setting up alerting with Kpow using Prometheus and Alertmanager.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Kpow was built from our own need to monitor Kafka clusters and related resources (eg, Streams, Connect and Schema Registries).&lt;/p&gt;
&lt;p&gt;Through Kpow’s user interface we can detect and even predict potential problems with Kafka such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replicas that have gone out of sync&lt;/li&gt;
&lt;li&gt;Consumer group assignments that are lagging above a certain threshold&lt;/li&gt;
&lt;li&gt;Topic growth that will exceed a quota&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;How can we alert teams as soon as these problems occur?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kpow does not provide its own alerting functionality but instead integrates with Prometheus for a modern alerting solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why don’t we natively support alerting?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We believe a dedicated product like Prometheus is better suited for alerting rather than an individual product in most cases. Most organizations have alerting needs beyond Kafka, and having alerting managed from a centralized service, such as Prometheus makes more sense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t use Prometheus?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Fear not, almost every major observability tool on the market today supports Prometheus metrics. For example, &lt;a href=&quot;https://grafana.com/blog/2020/09/15/introducing-prometheus-style-alerting-for-grafana-cloud/&quot;&gt;&lt;strong&gt;Grafana Cloud&lt;/strong&gt;&lt;/a&gt; supports Prometheus alerts out of the box.&lt;/p&gt;
&lt;p&gt;This article will demonstrate how to set up Kpow with Prometheus + AlertManager, alongside example configuration to help you start defining your alerts when things go wrong with your Kafka cluster.&lt;/p&gt;
&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;
&lt;p&gt;Here is the basic architecture of alerting with Prometheus:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.prod.website-files.com/689f8aab1977008c61538b01/68f923807a38e584d0be7191_kpow-prometheus-architecture.png&quot; alt=&quot;Kpow and Prometheus Architecture Diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Alerts are defined in Prometheus configuration. Prometheus pulls metrics from all client applications (including Kpow). If any condition is met, Prometheus pushes the alert to the AlertManager service, which manages the alerts through its pipeline of &lt;a href=&quot;https://prometheus.io/docs/alerting/latest/alertmanager/&quot;&gt;&lt;strong&gt;silencing, inhibition, grouping and sending out notifications&lt;/strong&gt;&lt;/a&gt;. Essentially what that means is that AlertManager takes care of deduplicating, grouping and routing of alerts to the correct integration such as Slack, email or Opsgenie.&lt;/p&gt;
&lt;h3 id=&quot;kpow-metrics&quot;&gt;Kpow Metrics&lt;/h3&gt;
&lt;p&gt;The unique thing about Kpow as a product is that we calculate our own telemetry about your Kafka Cluster and related resources.&lt;/p&gt;
&lt;p&gt;This has a ton of advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No dependency on Kafka’s own JMX metrics - This allows frictionless installation and configuration.&lt;/li&gt;
&lt;li&gt;From our observations of your Kafka cluster we calculate a wide range of Kafka metrics, including group and topic offset deltas.&lt;/li&gt;
&lt;li&gt;This same pattern applies to other supported resources such as Kafka Connect, Kafka Streams and Schema Registry metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;environment-setup&quot;&gt;Environment Setup&lt;/h3&gt;
&lt;p&gt;We provide a &lt;strong&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/strong&gt; configuration that starts up Kpow, a 3-node Kafka cluster and Prometheus + AlertManager. This can be found in the &lt;a href=&quot;https://github.com/factorhouse/kpow-local&quot;&gt;&lt;strong&gt;kpow-local&lt;/strong&gt;&lt;/a&gt; repositry on GitHub. Instructions on how to start a 30-day trial can be found in the repository if you are new to Kpow.&lt;/p&gt;
&lt;p&gt;git clone &lt;a href=&quot;https://github.com/factorhouse/kpow-local.gitcd&quot;&gt;https://github.com/factorhouse/kpow-local.gitcd&lt;/a&gt; kpow-localvi local.env # add your LICENSE details, see kpow-local README.mddocker-compose up&lt;/p&gt;
&lt;p&gt;Once the Docker Compose environment is running:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alertmanager’s web UI will be reachable on port &lt;strong&gt;&lt;code&gt;9001&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Prometheus’ web UI will be reachable on port &lt;strong&gt;&lt;code&gt;9090&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Kpow’s web UI will be reachable on port &lt;strong&gt;&lt;code&gt;3000&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The remainder of this tutorial will be based off the Docker Compose environment.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-configuration&quot;&gt;Prometheus Configuration&lt;/h3&gt;
&lt;p&gt;A single instance of Kpow can observe and monitor &lt;a href=&quot;https://docs.kpow.io/config/multi-cluster&quot;&gt;&lt;strong&gt;multiple Kafka clusters&lt;/strong&gt;&lt;/a&gt; and related resources! This makes Kpow a great aggregator for your entire Kafka deployment across multiple environments as a single Prometheus endpoint served by Kpow can provide metrics about all your Kafka resources.&lt;/p&gt;
&lt;p&gt;When Kpow starts up, it logs the various Prometheus endpoints available:&lt;/p&gt;
&lt;p&gt;--* Prometheus Egress:  * GET /metrics/v1 - All metrics  * GET /offsets/v1 - All topic offsets  * GET /offsets/v1/topic/[topic-name] - All topic offsets for specific topic, all clusters.  * GET /streams/v1 - All Kafka Streams metrics  * GET /streams/v1/group/[group-name] - All Kafka Streams metrics for specific group, all clusters  * GET /metrics/v1/cluster/sb2i_wfxSa-LaD0srBaMiA - Metrics for cluster Dev01  * GET /offsets/v1/cluster/sb2i_wfxSa-LaD0srBaMiA - Offsets for cluster Dev01  * GET /streams/v1/cluster/sb2i_wfxSa-LaD0srBaMiA - Kafka Streams metrics for cluster Dev01  * GET /metrics/v1/connect/sb2i_wfxSa-LaD0srBaMiA - Metrics for connect instance sb2i_wfxSa-LaD0srBaMiA (cluster sb2i_wfxSa-LaD0srBaMiA)  * GET /metrics/v1/cluster/lkc-jyojm - Metrics for cluster Uat01  * GET /offsets/v1/cluster/lkc-jyojm - Offsets for cluster Uat01  * GET /streams/v1/cluster/lkc-jyojm - Kafka Streams metrics for cluster Uat01  * GET /metrics/v1/schema/a2f06a916672d71d675f - Metrics for schema registry instance a2f06a916672d71d675f (cluster lkc-jyojm)  * GET /metrics/v1/cluster/CuxsifYVRhSRX6iLTbANWQ - Metrics for cluster Prod1  * GET /offsets/v1/cluster/CuxsifYVRhSRX6iLTbANWQ - Offsets for cluster Prod1  * GET /streams/v1/cluster/CuxsifYVRhSRX6iLTbANWQ - Kafka Streams metrics for cluster Prod1&lt;/p&gt;
&lt;p&gt;This allows Prometheus to only consume a subset of metrics (eg, metrics about a specific consumer group or resource).&lt;/p&gt;
&lt;p&gt;To have Prometheus pull all metrics, add this entry to your &lt;strong&gt;&lt;code&gt;scrape_configs&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;scrape_configs:  - job_name: ‘kpow’    metrics_path: ‘/metrics/v1’    static_configs:      - targets: [‘http://kpow:3000’]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : you will need to provide a reachable &lt;strong&gt;&lt;code&gt;target&lt;/code&gt;&lt;/strong&gt;. In this example Kpow is reachable at &lt;strong&gt;&lt;code&gt;http://kpow:3000&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Within your prometheus config, you will need to specify a location to your rules.yml file:&lt;/p&gt;
&lt;p&gt;rule_files:  - kpow-rules.yml&lt;/p&gt;
&lt;p&gt;Our &lt;strong&gt;&lt;code&gt;kpow-rules.yml&lt;/code&gt;&lt;/strong&gt; file looks something like:&lt;/p&gt;
&lt;p&gt;groups:- name: Kafka  rules:  # Example rules in section below&lt;/p&gt;
&lt;p&gt;We have a single alert group called &lt;strong&gt;&lt;code&gt;Kafka&lt;/code&gt;&lt;/strong&gt;. The collection of rules are explained in the next section.&lt;/p&gt;
&lt;p&gt;The sample &lt;strong&gt;&lt;code&gt;kpow-rules.yml&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;alertmanager.yml&lt;/code&gt;&lt;/strong&gt; config can be found &lt;a href=&quot;https://github.com/factorhouse/kpow-local/tree/master/resources&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;. In this example alertmanager will be sending all fired alerts to a Slack WebHook.&lt;/p&gt;
&lt;h3 id=&quot;kpow-metric-structure&quot;&gt;Kpow Metric Structure&lt;/h3&gt;
&lt;p&gt;A glossary of available Prometheus metrics from Kpow can be found &lt;a href=&quot;https://docs.kpow.io/features/prometheus/metrics-glossary&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All Kpow metrics follow a similar labelling convention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;domain&lt;/code&gt;&lt;/strong&gt; - the category of metric (for example &lt;strong&gt;&lt;code&gt;cluster&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;streams&lt;/code&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;id&lt;/code&gt;&lt;/strong&gt; - the unique identifier of the category (for example Kafka Cluster ID)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;target&lt;/code&gt;&lt;/strong&gt; - the identifier of the metric (for example consumer group, topic name etc)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;env&lt;/code&gt;&lt;/strong&gt; - an optional label to identify the &lt;strong&gt;&lt;code&gt;domain&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the metric:&lt;/p&gt;
&lt;p&gt;group_state{domain=“cluster”,id=“6Qw4099nSuuILkCkWC_aNw”,target=“tx_partner_group4”,env=“Trade_Book__Staging_”,} 4.0 1619060220000&lt;/p&gt;
&lt;p&gt;Relates to a Kafka Cluster (with id &lt;strong&gt;&lt;code&gt;6Qw4099nSuuILkCkWC_aNw&lt;/code&gt;&lt;/strong&gt; and label &lt;strong&gt;&lt;code&gt;Trade Book Staging&lt;/code&gt;&lt;/strong&gt;) for consumer group &lt;strong&gt;&lt;code&gt;tx_partner_group4&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;prometheus-rules&quot;&gt;Prometheus Rules&lt;/h3&gt;
&lt;p&gt;The remainder of this section will provide example Prometheus rules for common alerting scenarios.&lt;/p&gt;
&lt;h3 id=&quot;alerting-when-a-consumer-group-is-unhealthy&quot;&gt;Alerting when a Consumer Group is unhealthy&lt;/h3&gt;
&lt;p&gt;- alert: UnhealthyConsumer  expr: group_state == 0 or group_state == 1 or group_state == 2  for: 5m  annotations:    summary: “Consumer {{ $labels.target }} is unhealthy”    description:  “The Consumer Group {{ $labels.target }} has gone into {{ $labels.state }} for cluster {{ $labels.id }}”&lt;/p&gt;
&lt;p&gt;Here, the &lt;strong&gt;&lt;code&gt;group_state&lt;/code&gt;&lt;/strong&gt; metric from Kpow is exposed as a gauge and the value represents the ordinal value of the &lt;a href=&quot;https://kafka.apache.org/27/javadoc/org/apache/kafka/common/ConsumerGroupState.html&quot;&gt;&lt;strong&gt;ConsumerGroupState&lt;/strong&gt;&lt;/a&gt; enum. The &lt;strong&gt;&lt;code&gt;expr&lt;/code&gt;&lt;/strong&gt; is testing whether &lt;strong&gt;&lt;code&gt;group_state&lt;/code&gt;&lt;/strong&gt; enters state &lt;strong&gt;&lt;code&gt;DEAD&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;EMPTY&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;UNKNOWN&lt;/code&gt;&lt;/strong&gt; for &lt;strong&gt;all consumer groups&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;for&lt;/code&gt;&lt;/strong&gt; clause causes Prometheus to wait for a certain duration between first encountering a new expression output vector element and counting an alert as firing for this element. In this case 5 minutes.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;annotations&lt;/code&gt;&lt;/strong&gt; section then provides a human readable alert description which describes which consumer group has entered an unhealthy state. Group state has a &lt;strong&gt;&lt;code&gt;state&lt;/code&gt;&lt;/strong&gt; label that contains the human-readable value of the state (eg, &lt;strong&gt;&lt;code&gt;STABLE&lt;/code&gt;&lt;/strong&gt;).&lt;/p&gt;
&lt;h3 id=&quot;alerting-when-a-kafka-connect-task-is-unhealthy&quot;&gt;Alerting when a Kafka Connect task is unhealthy&lt;/h3&gt;
&lt;p&gt;Similar to our consumer group configuration, we can alert when we detect a connector task has gone into an &lt;strong&gt;&lt;code&gt;ERROR&lt;/code&gt;&lt;/strong&gt; state.&lt;/p&gt;
&lt;p&gt;- alert: UnhealthyConnectorTask  expr: connect_connector_task_state != 1  for: 5m  annotations:    summary: “Connect task {{ $labels.target }} is unhealthy”    description:  “The Connector task {{ $labels.target }} has gone into {{ $labels.target }} for cluster {{ $labels.id }}”- alert: UnhealthyConnector  expr: connect_connector_state != 1  for: 5m  annotations:    summary: “Connector {{ $labels.target }} is unhealthy”    description:  “The Connector {{ $labels.target }} has gone into {{ $labels.target }} for cluster {{ $labels.id }}”&lt;/p&gt;
&lt;p&gt;Here we have configured two alerts: one if an individual connector task goes enters an error state, and one if the connector itself enters an error state. The value of &lt;strong&gt;&lt;code&gt;1&lt;/code&gt;&lt;/strong&gt; represents the &lt;strong&gt;&lt;code&gt;RUNNING&lt;/code&gt;&lt;/strong&gt; state.&lt;/p&gt;
&lt;h3 id=&quot;alerting-when-a-consumer-group-is-lagging-above-a-threshold&quot;&gt;Alerting when a consumer group is lagging above a threshold&lt;/h3&gt;
&lt;p&gt;In this example Prometheus will fire an alert if any consumer groups lag exceeds 5000 messages for more than 5 minutes.&lt;/p&gt;
&lt;p&gt;We can configure a similar alert for &lt;strong&gt;&lt;code&gt;host_offset_lag&lt;/code&gt;&lt;/strong&gt; to monitor individual lagging hosts, or even &lt;strong&gt;&lt;code&gt;broker_offset_lag&lt;/code&gt;&lt;/strong&gt; for lagging behind brokers.&lt;/p&gt;
&lt;p&gt;- alert: LaggingConsumerGroup  expr: group_offset_lag &amp;gt; 5000  for: 5m  annotations:    summary: “Consumer group {{ $labels.target }} is lagging”    description:  “Consumer group {{ $labels.target }} is lagging for cluster {{ $labels.id }}”&lt;/p&gt;
&lt;h3 id=&quot;alerting-when-the-kpow-instance-is-down&quot;&gt;Alerting when the Kpow instance is down&lt;/h3&gt;
&lt;p&gt;- alert: KpowDown  expr: up == 0 and {job=“kpow”}  for: 1m  annotations:    summary: “Kpow is down”    description:  “Kpow instance {{ $labels.target }} has been down for more than 1 minute.”&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This article demonstrates how you can build out a modern alerting system with Kpow and Prometheus.&lt;/p&gt;
&lt;p&gt;Source code for configuration, including a demo docker-compose.yml of the setup can be found &lt;a href=&quot;https://github.com/factorhouse/kpow-local&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Prometheus metrics are the de-facto industry standard, meaning similar integrations are possible with services such as &lt;a href=&quot;https://grafana.com/blog/2020/09/15/introducing-prometheus-style-alerting-for-grafana-cloud/&quot;&gt;&lt;strong&gt;Grafana Cloud&lt;/strong&gt;&lt;/a&gt; or &lt;a href=&quot;https://docs.newrelic.com/docs/integrations/prometheus-integrations/&quot;&gt;&lt;strong&gt;New Relic&lt;/strong&gt;&lt;/a&gt;. All of these services provide an equally compelling solution to alerting.&lt;/p&gt;
&lt;p&gt;What’s even more exciting for us is &lt;a href=&quot;https://aws.amazon.com/prometheus/&quot;&gt;&lt;strong&gt;Amazon’s Managed Service for Prometheus&lt;/strong&gt;&lt;/a&gt; which is currently in feature preview. This service looks to make Prometheus monitoring of containerized applications at scale much easier.&lt;/p&gt;
&lt;p&gt;While Prometheus metrics are what we expose for data egress with Kpow, please &lt;a href=&quot;https://factorhouse.io/contact/&quot;&gt;&lt;strong&gt;get in touch&lt;/strong&gt;&lt;/a&gt; if you would like alternative metric egress formats in Kpow such as WebHooks or even a JMX connection - we’d love to know your use case!&lt;/p&gt;
&lt;h3 id=&quot;further-readingreferences&quot;&gt;Further reading/references&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://grafana.com/blog/2020/02/25/step-by-step-guide-to-setting-up-prometheus-alertmanager-with-slack-pagerduty-and-gmail/#:~:text=Setting%20up%20alerts%20with%20Prometheus,the%20alerts%20specified%20in%20Prometheus.&quot;&gt;&lt;strong&gt;Step-by-step guide to setting up Prometheus Alertmanager with Slack, PagerDuty, and Gmail&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/devops-dudes/prometheus-alerting-with-alertmanager-e1bbba8e6a8e&quot;&gt;&lt;strong&gt;Prometheus Alerting with AlertManager&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Manage, Monitor and Learn Apache Kafka with &lt;a href=&quot;https://factorhouse.io/kpow/&quot;&gt;Kpow&lt;/a&gt; by &lt;a href=&quot;https://factorhouse.io/&quot;&gt;Factor House&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We know how easy Apache Kafka® can be with the right tools. We built Kpow to make the developer experience with Kafka simple and enjoyable, and to save businesses time and money while growing their Kafka expertise. A single Docker container or JAR file that installs in minutes, Kpow’s unique Kafka UI gives you instant visibility of your clusters and immediate access to your data.&lt;/p&gt;
&lt;p&gt;Kpow is compatible with Apache Kafka+1.0, Red Hat AMQ Streams, Amazon MSK, Instaclustr, Aiven, Vectorized, Azure Event Hubs, Confluent Platform, and Confluent Cloud.&lt;/p&gt;
&lt;p&gt;Start with a &lt;a href=&quot;https://factorhouse.io/kpow/get-started/&quot;&gt;&lt;strong&gt;free 30-day trial&lt;/strong&gt;&lt;/a&gt; and solve your Kafka issues within minutes.&lt;/p&gt;
</content:encoded><category>How-to</category><author>Thomas Crowley</author></item></channel></rss>