<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Conduktor</title>
    <description>The latest articles on DEV Community by Conduktor (conduktor).</description>
    <link>https://dev.to/conduktor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F7353%2Fe70b4d17-904a-4b56-9963-4dd2ac5aa071.png</url>
      <title>DEV Community: Conduktor</title>
      <link>https://dev.to/conduktor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/conduktor"/>
    <language>en</language>
    <item>
      <title>Every Application Could Delete Our Schemas.</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 13 Jul 2026 20:49:55 +0000</pubDate>
      <link>https://dev.to/conduktor/your-kafka-schema-registry-is-wide-open-1fl6</link>
      <guid>https://dev.to/conduktor/your-kafka-schema-registry-is-wide-open-1fl6</guid>
      <description>&lt;p&gt;Quick question about your Kafka setup: right now, which applications are allowed to change or delete which schemas?&lt;/p&gt;

&lt;p&gt;Sometimes, it's everyone (because the Schema Registry (SR) is not protected). Sometimes, it's an nginx/traefik in front of their SR to blacklist some routes (like global compatibility) and call it done. Sometimes, it's just to add basic authentication at least. Rarely, it's about authorization.&lt;/p&gt;

&lt;p&gt;Authorization is about "what are you allowed to change/break?"&lt;/p&gt;

&lt;p&gt;Let me show you where the gap actually is, because "just put a proxy in front of it" is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication isn't authorization
&lt;/h2&gt;

&lt;p&gt;A schema registry exists to solve a coordination problem. Producers and consumers ship on their own schedules, so they need a shared, versioned contract for the shape of each message. Producers register a schema, consumers resolve it by ID, and the registry enforces compatibility as things evolve so one side's change doesn't blow up the other.&lt;/p&gt;

&lt;p&gt;That makes it a very high-value target. &lt;em&gt;Everything&lt;/em&gt; producing or consuming structured data depends on it. So "who can change it" should be a first-class question and in the free Confluent Schema Registry, the answer is "anyone."&lt;/p&gt;

&lt;p&gt;The default listener is plain HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# Schema Registry default — plaintext, every interface
&lt;/span&gt;&lt;span class="py"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://0.0.0.0:8081&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per-subject access control isn't in the free version. It's a commercial add-on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Until either ACLs or Role-Based Access Control is also enabled for Schema Registry, any user can create, alter, and delete Schema Registry subjects."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create/alter/delete arê the controls you actually want is per-subject: &lt;em&gt;the payments team can write `payments-&lt;/em&gt;` schemas and nothing else.*&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not just Confluent
&lt;/h2&gt;

&lt;p&gt;The free registry is the most obvious offender, but it's not alone. Every option handles per-subject authorization differently, and most of it is off by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Per-subject authz&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Uses your Kafka identity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Confluent SR (free)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;open, plain HTTP&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apicurio&lt;/td&gt;
&lt;td&gt;registry-wide roles only&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Karapace&lt;/td&gt;
&lt;td&gt;yes, regex ACL per subject&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluent RBAC / plugin&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;off, commercial&lt;/td&gt;
&lt;td&gt;Confluent's plane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic HTTP proxy&lt;/td&gt;
&lt;td&gt;coarse, path-prefix only&lt;/td&gt;
&lt;td&gt;you build it&lt;/td&gt;
&lt;td&gt;no — matches the URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Per-subject control exists in a couple of places, but it's either a hand-edited auth file (Karapace) or a paid platform tier (Confluent RBAC). None of the free or open-source options tie the rule to the Kafka identities you already manage, or give you an audit trail that lines up with Kafka.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a generic HTTP proxy is a bad solution
&lt;/h2&gt;

&lt;p&gt;Since the registry speaks HTTP, the natural thinking is to set up an nginx or an API gateway and only allow the routes you want. For basic guardrails on a single registry, that's cheap and reasonable. Subject names even show up in the path, so you can gate writes by prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Allow writes only to payments-* subjects&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;^/subjects/payments-.*/versions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://schema-registry:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine. Then you need to maintain the nginx file, meet the edge cases, and you can only reason about URLs: not schemas, not identities. Here's the one that always gets people:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /schemas/ids/42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That fetches a schema by numeric ID. There's &lt;strong&gt;no subject in the path&lt;/strong&gt; to match on, so your careful &lt;code&gt;payments-*&lt;/code&gt; rule doesn't apply — and that call can return any schema, including another team's. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It authorizes the &lt;strong&gt;network path, not your Kafka principal&lt;/strong&gt;: so it's only as strong as the firewall around port 8081.&lt;/li&gt;
&lt;li&gt;It assumes subjects are named after topics. Switch to &lt;code&gt;RecordNameStrategy&lt;/code&gt; and the subject is a record name, so your prefix rules stop lining up.&lt;/li&gt;
&lt;li&gt;It can't tell a deliberate registration from a client silently auto-registering on first produce:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# One flag away from clients registering schemas you never reviewed
&lt;/span&gt;&lt;span class="py"&gt;auto.register.schemas&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to go down this path will create an abomination you'll need to maintain forever with your own model of subjects and owners inside your proxy. That's a lot of infrastructure to own just to answer "who can touch payments schemas."&lt;/p&gt;

&lt;h2&gt;
  
  
  What is "closed by default"?
&lt;/h2&gt;

&lt;p&gt;Per-subject authorization should be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;By subject, prefix, and wildcard&lt;/strong&gt;, not by URL path that happens to contain a subject sometimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tied to the Kafka identities you already use&lt;/strong&gt;, the same principals you manage for topic ACLs, not a second, parallel access system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logged end to end&lt;/strong&gt;, every operation and every denial, in a trail that lines up with the rest of your Kafka audit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The way to get all three without a platform license or bespoke plumbing is a &lt;em&gt;schema-aware&lt;/em&gt; proxy, something that understands subjects and requests rather than URLs. That's the category &lt;a href="https://www.conduktor.io/schema-registry-proxy" rel="noopener noreferrer"&gt;Conduktor's Schema Registry Proxy&lt;/a&gt; sits in: it fronts the registry you already run, checks read and write permissions per subject, logs every call, and doesn't need any producer or consumer changes. You just point &lt;code&gt;schema.registry.url&lt;/code&gt; at the proxy and keep your current registry, Confluent or open source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clients don't change — they just talk to the proxy
&lt;/span&gt;&lt;span class="py"&gt;schema.registry.url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://schema-registry-proxy:8081&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have a protected schema registry, reads and writes, that can even be linked to a self-service framework where ownership is a first-class citizen.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>devops</category>
      <category>architecture</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Every Kafka Cluster Eventually Hits This Networking Problem</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 06 Jul 2026 17:42:50 +0000</pubDate>
      <link>https://dev.to/conduktor/kafka-addressing-vpc-peering-5f3j</link>
      <guid>https://dev.to/conduktor/kafka-addressing-vpc-peering-5f3j</guid>
      <description>&lt;p&gt;If you've ever tried to connect a Kafka client that lives in a different VPC than the cluster, you've probably hit this issue where there's a route between the two networks, telnet works, and yet the client still can't consume a single record. It feels like a networking bug. It isn't.&lt;/p&gt;

&lt;p&gt;The issue is that &lt;strong&gt;reaching Kafka across VPCs is an addressing problem, not a networking one.&lt;/strong&gt; Once that clicks, why peering, transit gateways, and per-broker load balancers don't work will make sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What your client actually does when it connects
&lt;/h2&gt;

&lt;p&gt;Most services are easy to reach across a networking boundary. Put a load balancer in front, give it one address, point clients at it. Done.&lt;/p&gt;

&lt;p&gt;Kafka doesn't work like that. A client connects in two stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap.&lt;/strong&gt; The client talks to any broker and asks one question: who's in this cluster?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct connection.&lt;/strong&gt; The broker answers with metadata — a list of &lt;em&gt;every&lt;/em&gt; broker, each named by its own &lt;code&gt;advertised.listeners&lt;/code&gt; address. The client then opens direct connections to specific brokers: the leader for each partition it reads or writes (or the nearest replica, if you're using follower fetching, KIP-392, Kafka 2.4+).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That second stage is the most important to remember. One shared address in front of the cluster is not working, because the client has to resolve and route to &lt;em&gt;each broker's advertised address, exactly as the cluster hands it back.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So if a broker advertises &lt;code&gt;broker-1.cluster.internal:9092&lt;/code&gt;, a private name that only resolves inside the cluster's VPC, a remote client connects to the bootstrap fine, then fail when it tries to connect directly to one of the brokers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec kafka-consumer-a kcat -b kafka:9092 -L -m 5
# -&amp;gt; Failed to resolve 'kafka:9092'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A network path between two VPCs is not the same as Kafka reachability. The client still has to resolve and route to every broker's advertised address &lt;em&gt;from where it sits&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;"Then just make the broker advertise something reachable". Sure, brokers support multiple listeners, one internal and one external. But every listener is another port to open on every broker, and managed Kafka (MSK, Confluent Cloud, Aiven) won't let you touch them anyway. Listeners don't scale to a dozen independent networks across accounts and clouds. Which is exactly the situation you're in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why peering collapses the moment you have more than two networks
&lt;/h2&gt;

&lt;p&gt;VPC peering is genuinely great for connecting two VPCs. Past two, here be dragons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's point-to-point and non-transitive.&lt;/strong&gt; Peering A and B each to the cluster does &lt;em&gt;not&lt;/em&gt; let A talk to B. Every new client network needs its own peering straight into the cluster's VPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your cluster's VPC becomes an accidental hub.&lt;/strong&gt; Ten client networks means ten peerings, ten route-table entries, and ten security-group conversations on a VPC that was never designed to be a hub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CIDRs have to be coordinated.&lt;/strong&gt; Peered VPCs can't overlap ranges, so every team negotiates address space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Kafka caps it.&lt;/strong&gt; Providers limit how many peerings or PrivateLink attachments you get, and none of them let you rewrite broker listeners.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recurring pain is one line: &lt;strong&gt;every new client forces another change onto the cluster's VPC&lt;/strong&gt;, the one piece of infra you least want to keep editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: let a Kafka-aware proxy rewrite the addresses
&lt;/h2&gt;

&lt;p&gt;The solution: put something in the connection path that actually &lt;em&gt;understands the Kafka protocol&lt;/em&gt; instead of just shuffling packets, and have it rewrite the broker addresses in the metadata response before the client ever sees them.&lt;/p&gt;

&lt;p&gt;That's what &lt;a href="https://www.conduktor.io/gateway" rel="noopener noreferrer"&gt;Conduktor Gateway&lt;/a&gt; does. A broker advertises &lt;code&gt;kafka-internal:9092&lt;/code&gt;; the Gateway rewrites that to an address the client can reach. Because clients connect to specific brokers (the partition leaders), the Gateway maps each broker to its own port so traffic still routes deterministically to the right one behind it.&lt;/p&gt;

&lt;p&gt;From the client's side, the only change is a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# before: pointing straight at a broker it can't actually reach
&lt;/span&gt;&lt;span class="py"&gt;bootstrap.servers&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;broker-1.cluster.internal:9092&lt;/span&gt;

&lt;span class="c"&gt;# after: pointing at the proxy, which hands back addresses it can
&lt;/span&gt;&lt;span class="py"&gt;bootstrap.servers&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;conduktor-gateway.hub:9092&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Credentials don't change. The client presents the same SASL credentials it already has, the Gateway forwards them to the broker, and Kafka ACLs (or Confluent RBAC) still decide what it's allowed to do. The proxy is stateless, it stores no credentials and adds no new auth model.&lt;/p&gt;

&lt;p&gt;The nice architectural payoff: the Gateway lives in its own &lt;strong&gt;hub VPC&lt;/strong&gt;. Peerings or PrivateLink attachments land on the hub, never directly between a client and the cluster. New networks attach to the hub; the cluster keeps a single attachment no matter how many clients reach in, and its listeners are never touched.&lt;/p&gt;

&lt;p&gt;The Gateway isn't a substitute for a route as you still need connectivity between the hub and each VPC. What it removes is the per-client peering into the cluster, the broker reconfiguration, and the requirement that every advertised address be reachable from every client.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But what about a transit gateway? Or an LB per broker?"
&lt;/h2&gt;

&lt;p&gt;Won't work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cloud transit gateway&lt;/strong&gt; is one hub many VPCs attach to for any-to-any IP connectivity. But it operates at L3 so it moves IP packets with zero awareness of Kafka. It doesn't change what brokers advertise, so you're back to sharing private hosted zones across accounts. Overlapping CIDRs still break it without NAT, it's confined to one cloud, and on managed Kafka you still can't touch the listeners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A load balancer per broker&lt;/strong&gt; can't be round-robined, because clients connect to specific leaders. So it's one NLB (or target group, or PrivateLink endpoint) &lt;em&gt;per broker&lt;/em&gt;, plus &lt;code&gt;advertised.listeners&lt;/code&gt; reconfigured: N pieces of plumbing to keep in sync, each with its own port, DNS record, and TLS SAN. Scale or replace a broker and the mapping churns, forcing a rolling restart. And on managed Kafka you usually can't set advertised listeners at all — so it's off the table before you begin.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Works at&lt;/th&gt;
&lt;th&gt;Fixes the advertised-address problem?&lt;/th&gt;
&lt;th&gt;Managed Kafka?&lt;/th&gt;
&lt;th&gt;Cross-cloud?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VPC peering&lt;/td&gt;
&lt;td&gt;L3&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Capped by limits&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transit gateway&lt;/td&gt;
&lt;td&gt;L3&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;On-prem yes, other cloud no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NLB per broker&lt;/td&gt;
&lt;td&gt;L4&lt;/td&gt;
&lt;td&gt;Only if you reconfigure listeners&lt;/td&gt;
&lt;td&gt;Usually not&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka-aware proxy&lt;/td&gt;
&lt;td&gt;Kafka L7&lt;/td&gt;
&lt;td&gt;Yes, automatically&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Reproduce the whole thing on one machine
&lt;/h2&gt;

&lt;p&gt;The free &lt;a href="https://www.conduktor.io/gateway/community-edition" rel="noopener noreferrer"&gt;Gateway Community&lt;/a&gt; quickstart stands up the exact scenario: a Kafka cluster in a private Docker network the clients can't reach, two consumers in two separate "VPC" networks, and the Gateway as the only container joined to all three.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash &amp;lt;(curl -fsSL https://releases.conduktor.io/gateway-community-quickstart)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumer A can't even resolve the broker directly — different network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec kafka-consumer-a kcat -b kafka:9092 -L -m 5
# -&amp;gt; Failed to resolve 'kafka:9092'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same consumer, same SASL credentials, one different bootstrap address — now it reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec kafka-consumer-a kcat -b conduktor-gateway:9092 -t customers -C -e -c 3 \
  -s value=avro -r http://karapace:8081 \
  -X security.protocol=SASL_PLAINTEXT -X sasl.mechanism=PLAIN \
  -X sasl.username=consumer-a -X sasl.password=consumer-a-secret
# -&amp;gt; readable records
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the log line that proves &lt;em&gt;why&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker logs conduktor-gateway 2&amp;gt;&amp;amp;1 | grep "Rewriting METADATA"
# kafka:9092 -&amp;gt; conduktor-gateway:9092
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole trick. The broker advertises an address the client can't reach; the proxy rewrites it to one it can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If cross-VPC Kafka has been a recurring issue on your platform team, try rethinking it: you don't have a networking problem, you have an addressing problem, and a Kafka-aware proxy is the one thing that fixes the address without you touching the cluster.&lt;/p&gt;

&lt;p&gt;If you later want to encrypt fields, enforce schemas, or isolate tenants on that same path, this same proxy will do it too.&lt;/p&gt;

&lt;p&gt;If you want to poke at it, the &lt;a href="https://www.conduktor.io/gateway/community-edition" rel="noopener noreferrer"&gt;Gateway Community quickstart&lt;/a&gt; is free and runs on one machine. Curious how you're solving this today, peering everything everywhere, transit gateways, something else? Let me know.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>datastreaming</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>We Measured Kafka Usage. The Results Surprised Us.</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:32:43 +0000</pubDate>
      <link>https://dev.to/conduktor/kafka-cost-optimization-starts-with-usage-3lfb</link>
      <guid>https://dev.to/conduktor/kafka-cost-optimization-starts-with-usage-3lfb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfvlbwdkw5n53i3qecfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfvlbwdkw5n53i3qecfi.png" alt="Kafka costs are about usage not just infra" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I sit in a lot of Kafka reviews. Vendors, instances, replication, tiered storage, advanced stuff like fetch-from-follower, networking, partitions, best practices etc. Most discussions are driven by tech only, instead of looking at the big picture: how this beautiful infra is being used.&lt;/p&gt;

&lt;p&gt;Unpopular opinion: &lt;strong&gt;most of your Kafka cost is not due to infrastructure, it's due to a usage problem.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the cost actually comes from
&lt;/h2&gt;

&lt;p&gt;Vendor calculators are hard to compare because of so many assumptions. Replication multipliers, disk class, compression ratio, tiered storage (billed at the replicated rate or the actual S3 rate). The price you see is almost never what you pay.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RF=3 multiplies the per-GB price by 3 everywhere.&lt;/strong&gt; And tiered storage is often &lt;em&gt;still&lt;/em&gt; billed at the replicated rate even though only one copy lives in S3. You're paying the RF=3 rate for data Kafka no longer replicates. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-VPC, in-region traffic&lt;/strong&gt; between your account and the vendor's lands on &lt;em&gt;your&lt;/em&gt; cloud bill, roughly 1c/GB each way depending on the path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without fetch-from-follower, most consumer fetches cross AZ boundaries.&lt;/strong&gt; With three balanced AZs, ~2/3 of consumer reads go cross-AZ, because the leader lives in one AZ and the other two reads come from elsewhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compression is often just... off.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With zstd at sane batch sizes, JSON-ish logs and metrics commonly compress 8–10x:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;compression.type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;zstd&lt;/span&gt;
&lt;span class="py"&gt;batch.size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;65536          # 64KB&lt;/span&gt;
&lt;span class="py"&gt;linger.ms&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going from 5x to 10x halves your stored bytes &lt;em&gt;and&lt;/em&gt; halves the replication bytes flowing inside the cluster. You pay for that traffic three times over at RF=3, so the ratio matters.&lt;/p&gt;

&lt;p&gt;And fetch-from-follower, available since Kafka 2.4, is a broker + consumer config away. Same-AZ traffic inside your VPC is free on AWS, so no cross-AZ tax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# broker
&lt;/span&gt;&lt;span class="py"&gt;replica.selector.class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.apache.kafka.common.replica.RackAwareReplicaSelector&lt;/span&gt;
&lt;span class="py"&gt;broker.rack&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;us-east-1a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# consumer — must match the broker's rack value
&lt;/span&gt;&lt;span class="py"&gt;client.rack&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;us-east-1a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do all of it: fetch-from-follower, tiered storage, compression enforcement, partition right-sizing, BYOC to apply your existing cloud discount, single-AZ topics where you can tolerate it. But notice that &lt;em&gt;it's still infrastructure tuning.&lt;/em&gt; Let's go up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost is a stack, not a line item
&lt;/h2&gt;

&lt;p&gt;When you tune anything in Kafka, you think in layers, bottom-up: hardware, JVM, broker config, producer/consumer tuning, topic design, application code. Same for cost:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloud infrastructure&lt;/strong&gt;: instance types, AZ placement, networking, BYOC negotiation. At big contract sizes, negotiated networking discounts can hit 90%, but only if traffic flows through &lt;em&gt;your&lt;/em&gt; account. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broker &amp;amp; protocol tuning&lt;/strong&gt;: compression, retention, RF, fetch-from-follower, tiered storage, partition count. Easy, they're config changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture&lt;/strong&gt;: diskless topics, Iceberg topics, single-AZ topics, proxies between clients and brokers, virtual clusters for multi-tenancy and non-prod consolidation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage&lt;/strong&gt;: fan-out, governance, discovery, self-service. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Often &lt;strong&gt;payoff goes up as you go higher (more system thinking)&lt;/strong&gt; Everyone's comfortable arguing about GP2 vs GP3 (volume types on AWS. Almost nobody thinks "why are 40% of these partitions doing nothing?"&lt;/p&gt;

&lt;p&gt;Speaking of which: most clusters carry &lt;a href="https://www.conduktor.io/blog/the-surprising-cost-of-kafka-partition-waste" rel="noopener noreferrer"&gt;40–70% partition waste&lt;/a&gt;, did you know that? On managed Kafka that's per-partition-hour billing. On self-managed, you hit the ~4,000–6,000 partition-replicas-per-broker ceiling (RF=3 turns 100k partitions into 300k replicas to host and track). KRaft raises the ceiling but it doesn't make the waste free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fan-out is the whole point of Kafka
&lt;/h2&gt;

&lt;p&gt;Kafka exists so that one byte written can be read by N independent consumers, decoupled in time, with zero coordination back to the producer. That's the log abstraction's reason to live.&lt;/p&gt;

&lt;p&gt;Do you measure your average fan-out? If it's 1, you probably shouldn't be running Kafka at all, you're paying for a distributed log to do a point-to-point queue's job. LinkedIn famously ran at ~5.4: the same bytes, written once, read by 5.4 independent teams.&lt;/p&gt;

&lt;p&gt;Cluster cost stays flat while consumers grow, so cost-per-business-outcome is decreasing the more we consume existing topics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cost_per_use_case = cluster_cost / fan_out

fan-out 1  -&amp;gt;  $X      (one team carries the whole bill)
fan-out 3  -&amp;gt;  $X / 3
fan-out 5  -&amp;gt;  $X / 5  (same hardware, five outcomes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Is our Kafka usage growing?" is the wrong question. More business use-cases reading existing data is the best money you'll ever spend. Duplicated topics because nobody could find the existing one is pure waste, more storage, more replication, more pipelines, all because discovery and ownership are missing.&lt;/p&gt;

&lt;p&gt;The same goes for partitions: people over-provision because nobody knows how to size them, and you can't reduce partition count after the fact (breaks key ordering). The only way to surface that waste is &lt;a href="https://www.conduktor.io/blog/chargeback-attribute-map-kafka-costs-to-your-business" rel="noopener noreferrer"&gt;chargeback at the team-and-topic level&lt;/a&gt;. You can't optimize what you can't attribute.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A third of our traffic, we know what it has to do with, but we don't know exactly what they're doing."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the usage layer leaking. It costs money, and nobody can fix it because nobody knows how to, where to look, or just own it. It's not an infra problem, it's governance, discovery, and self-service.&lt;/p&gt;

&lt;p&gt;Cost optimization is &lt;strong&gt;everybody's concern and nobody's objective.&lt;/strong&gt; Teams over-provision because &lt;em&gt;what if we need it later&lt;/em&gt; and &lt;em&gt;what if it breaks when we touch it&lt;/em&gt; are rational fears. "It's expensive" is not a business case. What works is showing the waste, the annual dollar number, and the effort to reclaim it, with a name next to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2026: Where to spend your effort
&lt;/h2&gt;

&lt;p&gt;Most deployments I see have way more headroom in the usage layer than the infra layer: topics nobody reads, partitions nobody needs, teams who'd benefit from streaming but find it too painful to onboard.&lt;/p&gt;

&lt;p&gt;There's a funny industry reflex here too. We chase the next architectural shiny thing, diskless, Iceberg topics, single-AZ, before we've answered the boring questions: who's using this, for what, and why aren't more teams using it?&lt;/p&gt;

&lt;p&gt;My actual recommendation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do the infrastructure pass once.&lt;/strong&gt; Instance types, AZ placement, BYOC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do the config pass once.&lt;/strong&gt; Compression, retention, partition right-sizing, fetch-from-follower, tiered storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spend the rest of the year on the usage layer.&lt;/strong&gt; Fan-out, ownership, discovery, chargeback, self-service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 1 and 2 are a sprint. Step 3 is the marathon.&lt;/p&gt;

&lt;p&gt;If you want to see where your usage layer is leaking, Conduktor's field engineering team does a &lt;a href="https://www.conduktor.io/contact/kafka-cost-analysis" rel="noopener noreferrer"&gt;free Kafka cost analysis&lt;/a&gt;: they'll map cost back to teams and topics and show you where the payoff sits. And if you just want to keep reading, &lt;a href="https://www.conduktor.io/blog/a-better-conversation-about-kafka-costs" rel="noopener noreferrer"&gt;Why Kafka Costs Keep Rising&lt;/a&gt; and &lt;a href="https://www.conduktor.io/blog/the-surprising-cost-of-kafka-partition-waste" rel="noopener noreferrer"&gt;the partition waste deep-dive&lt;/a&gt; are good next stops.&lt;/p&gt;

&lt;p&gt;What's your average fan-out? If you don't know it off the top of your head, that's probably where I'd start.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>dataengineering</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>We Built a Kafka Proxy. Here's Everything It Ended Up Doing.</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 15 Jun 2026 13:43:15 +0000</pubDate>
      <link>https://dev.to/conduktor/you-can-do-what-with-a-kafka-proxy-42b1</link>
      <guid>https://dev.to/conduktor/you-can-do-what-with-a-kafka-proxy-42b1</guid>
      <description>&lt;p&gt;At Current 2026, I realized that nobody knows exactly what a Kafka proxy can do.&lt;/p&gt;

&lt;p&gt;Most engineers and architects think it's just some kind of reverse-proxy for Kafka (think nginx) to do routing and used to bridge a legacy or non-native client to the cluster. &lt;/p&gt;

&lt;p&gt;That's not it. It's barely the start of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encryption
&lt;/h2&gt;

&lt;p&gt;For instance, an engineer at a UK building society had a hard requirement: encrypt personally identifiable fields before they ever hit Kafka: emails, national insurance numbers, that kind of data.&lt;/p&gt;

&lt;p&gt;His team built encryption into the application layer. Every producer that touched PII got encryption code. Every consumer got decryption code. Key handling, rotation, etc. to manage across services.&lt;/p&gt;

&lt;p&gt;Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In every producer that touches PII...&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ProducerRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;keyRef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pii-key"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSsn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSsn&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;keyRef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pii-key"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProducerRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"customers"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// And the mirror image in every consumer...&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSsn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSsn&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiply that by all the micro-services to update and maintain now (cross languages, versioning, access to KMS etc.). That's quite expensive, at implementation time and to maintain.&lt;/p&gt;

&lt;p&gt;He didn't know a Kafka proxy could have done the whole thing at the record level, outside the apps. When we chat about it, he just realized he might have done a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Kafka proxy does
&lt;/h2&gt;

&lt;p&gt;A Kafka proxy sits between your clients and your brokers and speaks the Kafka protocol. Clients connect to it exactly like they'd connect to a broker. No SDK, no app changes. It works for Kafka clients, Kafka Connect, Kafka Streams, Flink, Spark, etc. It's fully transparent to them.&lt;/p&gt;

&lt;p&gt;It makes it a natural place to put policy that doesn't belong inside your application and doesn't belong inside the cluster either.&lt;/p&gt;

&lt;p&gt;Encryption is the obvious one. Instead of touching dozens of applications, you declare the rule once. With &lt;a href="https://www.conduktor.io/gateway" rel="noopener noreferrer"&gt;Conduktor Gateway&lt;/a&gt; it's what we call an interceptor: a small piece of config applied to traffic matching a topic pattern. Roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Interceptor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"apiVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gateway/v2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"encrypt-customer-pii"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pluginClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"io.conduktor.gateway.interceptor.EncryptionPlugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customers.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kmsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VAULT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"vault"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://vault:8200"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"recordValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fieldName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AES256_GCM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keySecretId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pii-key"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fieldName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AES256_GCM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keySecretId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pii-key"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The proxy encrypts the fields on the way in and authorized consumers get them decrypted on the way out, everyone else gets ciphertext. The application code shrinks back to just... sending a record, not dealing with KMS and secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Same producer, after.&lt;/span&gt;
&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProducerRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"customers"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the rule lives in one declarative place (the proxy), you can do things that are painful at the app layer.&lt;/p&gt;

&lt;p&gt;For instance, crypto-shredding for GDPR. Delete the key, and every message encrypted with it becomes unreadable, instantly, across all your retention. You don't go hunting through topics for one person's data. You revoke a key. Done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Masking, validation, isolation: same one place
&lt;/h2&gt;

&lt;p&gt;Once the proxy is in the Kafka path, the same pattern opens a lot of doors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Field-level masking&lt;/strong&gt;: show &lt;code&gt;j***@example.com&lt;/code&gt; to one team, the real value to another, same topic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema and payload validation&lt;/strong&gt;: reject malformed records at the edge instead of poisoning a downstream consumer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Topic aliasing for migration&lt;/strong&gt;: point clients at a stable name while you move the real topic between clusters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual clusters&lt;/strong&gt;: carve one physical cluster into isolated tenants without standing up new infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit and policy enforcement&lt;/strong&gt;: log and gate access without patching the broker or the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that touches application code or broker config. It's policy, declared once, enforced in the path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this connects to cost and self-service
&lt;/h2&gt;

&lt;p&gt;The other big topic at the conference was cost. Conduktor published a &lt;a href="https://www.conduktor.io/resources/ebooks/where-kafka-costs-hide-a-field-guide" rel="noopener noreferrer"&gt;field guide on where Kafka costs hide&lt;/a&gt; in April.&lt;/p&gt;

&lt;p&gt;Then the self-service conversation: teams want developers to create topics and request access in autonomy, but simple Topic on GitOps solution is just not enough, because self-service without guardrails easily turns Kafka into a mess:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If somebody goes onto the tool and adds in something ridiculous, like a thousand partitions, we need someone to have eyes on that. That's something we've learned we can't let go of."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Look at what encryption-at-the-proxy, cost guardrails, and self-service approval gates have in common. They're all policy that belongs &lt;em&gt;between&lt;/em&gt; your developers and your brokers, not baked into either one. Push it into the app and you copy-paste it dozens of time. Push it into the cluster and you can't change it without a migration. Put it in the layer in between, declare it once, and you can actually govern it.&lt;/p&gt;

&lt;p&gt;To build AI agents on top of streaming, the plumbing must come first: ownership, schema discipline, key custody, data quality before the data even enters Kafka, etc. A proxy that enforces structure and policy is a big chunk of that plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  So where does your policy live?
&lt;/h2&gt;

&lt;p&gt;What people think a proxy does is pass packets. What it really does is more of a safekeeper that holds the policy.&lt;/p&gt;

&lt;p&gt;If you've got encryption, masking, validation, or multi-tenant isolation scattered across your services right now, it's worth asking whether any of it should be living one layer down instead.&lt;/p&gt;

&lt;p&gt;Want to go deeper? The &lt;a href="https://www.conduktor.io/gateway" rel="noopener noreferrer"&gt;Gateway overview&lt;/a&gt; walks through the interceptor model, and the &lt;a href="https://www.conduktor.io/blog/what-we-learned-at-current-2026" rel="noopener noreferrer"&gt;original Current 2026 write-up&lt;/a&gt; has the rest of what we heard on the floor.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>architecture</category>
      <category>dataengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>I Gave Claude Admin Access to My Kafka Cluster</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 08 Jun 2026 13:34:35 +0000</pubDate>
      <link>https://dev.to/conduktor/i-let-an-ai-agent-set-up-my-entire-kafka-platform-heres-what-actually-happened-220m</link>
      <guid>https://dev.to/conduktor/i-let-an-ai-agent-set-up-my-entire-kafka-platform-heres-what-actually-happened-220m</guid>
      <description>&lt;p&gt;Your AI coding assistant can explain consumer groups, rebalancing, and exactly-once semantics. Ask it to actually &lt;em&gt;set up&lt;/em&gt; a Kafka platform with governance, though, and it won't be able to do that on its own.&lt;/p&gt;

&lt;p&gt;Between hallucinations, misunderstanding, production impact (I really saw Claude messing up a rolling upgrade of Kafka brokers), and the lack of knowledge of the products your Kafka infra is relying on, there's a lot working against it&lt;/p&gt;

&lt;p&gt;The models, besides their training, have zero context about your infra. They've never seen your cluster, don't know your policies (technical, governance), and often have no way to check anything against your actual environment.&lt;/p&gt;

&lt;p&gt;You can give it the missing context using Conduktor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that was missing
&lt;/h2&gt;

&lt;p&gt;There is an open-source &lt;a href="https://github.com/conduktor/skills" rel="noopener noreferrer"&gt;Conduktor skill&lt;/a&gt; you install into your AI assistant. It works with Claude Code, Cursor, VS Code Copilot, Gemini CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add conduktor/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is teaching the agent the whole platform and how to run process against it: Console, Gateway, and the CLI, so it can be efficient and not hallucinate.&lt;/p&gt;

&lt;p&gt;After the install, the agent discovers your environment (Kafka clusters, Schema Registry, policies, etc.), asks questions based on what it finds, generates configs with &lt;em&gt;real&lt;/em&gt; values and best practices, and runs everything with dry-run validation before it touches anything.&lt;/p&gt;

&lt;p&gt;The CLI are really its "hands" as more deep than just MCP. The skill is the playbook where all the experience and practices from years of usage are written. This does a big difference VS "generate some YAML and cross fingers"&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting from absolutely nothing
&lt;/h2&gt;

&lt;p&gt;You can start from scratch with just Docker running and nothing else. No Kafka, no Conduktor, no config. When I just ask this (with the Conduktor skill setup): &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;install Conduktor and set it up so I can login&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It checked my environment, asked what I was trying to do, wrote a &lt;code&gt;docker-compose.yml&lt;/code&gt;, spun up the containers, hit one error along the way, self-corrected, and handed me a working platform, Kafka &amp;amp; Console perfectly configured.&lt;/p&gt;

&lt;p&gt;I could ask the same but on my production Kubernetes. It would follow best practices too, use Helm, discover my environment, etc., and in minutes everything would be wired perfectly, with policies already in place.&lt;/p&gt;

&lt;p&gt;This is much more powerful than a "human" quickstart, as the range of applications it covers is just wider and more production-ready already. The agent knows the Kafka domain, and with the skill it knows Conduktor, so the combination of both makes it ask me the right questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Governance, without becoming a Kafka lawyer
&lt;/h2&gt;

&lt;p&gt;Running Kafka isn't the hard part anymore. Making it &lt;em&gt;safe for a team to share&lt;/em&gt; is the hard part: naming conventions, ownership boundaries, policies. This is what prevent a Kafka cluster from turning into a wasteland of &lt;code&gt;test-topic-final-v2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The beautiful thing is to be able to ask large prompts like this now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;set up governance for two teams, Payments and Analytics, with topic policies and cross-team permissions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It worked in stages and figured out the dependency ordering itself. When the API rejected something, it read the rejection, restructured the YAML, and retried, with minimal hand-holding from me (just asking what policies I want based on what's possible). It ended up creating the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TopicPolicy&lt;/code&gt; objects: locking down naming per team, enforcing safe defaults (retention, replication, required labels) across every topic. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Application&lt;/code&gt; objects with non-overlapping resource boundaries to define ownership of resources and teams.&lt;/li&gt;
&lt;li&gt;Topics with descriptions and labels in the catalog.&lt;/li&gt;
&lt;li&gt;Cross-team permission giving Analytics read access to &lt;code&gt;payments.orders.*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is federated ownership in practice: the platform team sets the boundaries, developers move freely inside them. Normally that knowledge takes months to accumulate and lives spreadsheet or Jira tickets. Here it lives in a skill file that every agent on the team can read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now flip to the developer side
&lt;/h2&gt;

&lt;p&gt;Once those guardrails exist, a developer on the Payments team installs the &lt;em&gt;same skill&lt;/em&gt; and never has to know any of it happened. No &lt;code&gt;ApplicationInstance&lt;/code&gt;, no &lt;code&gt;TopicPolicy&lt;/code&gt;, no YAML. They just talk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What topics do we have?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent runs &lt;code&gt;conduktor get Topic&lt;/code&gt; and shows the catalog — descriptions, owners, labels, visibility. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I need a topic for my service."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent checks their &lt;code&gt;ApplicationInstance&lt;/code&gt;, reads the policy constraints (naming prefix &lt;code&gt;payments.*&lt;/code&gt;, retention one-to-seven days, a required &lt;code&gt;data-criticality&lt;/code&gt; label), asks what the topic is for, generates compliant YAML, dry-runs it, and applies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Topic/payments.fulfillment.shipped: Created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer just got a topic that's compliant by default. Without the skill, that's a JIRA ticket most likely, and asking platform team what's the right shape and what to put.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How do I produce to my topic?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It reads the cluster config, grabs the real bootstrap server, and hands back working code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;confluent_kafka&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Producer&lt;/span&gt;

&lt;span class="n"&gt;producer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bootstrap.servers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost:19092&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payments.fulfillment.shipped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ord-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ord-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy, paste, run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I need to read the Analytics team's clickstream."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent finds that &lt;code&gt;analytics.clickstream.pageviews&lt;/code&gt; belongs to the Analytics team, then writes a read-only permission scoped to exactly that topic, at both the Kafka and Console layers. The developer doesn't know what an ACL is or what &lt;code&gt;patternType: LITERAL&lt;/code&gt; means. They asked in English and got access. &lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually take away from this
&lt;/h2&gt;

&lt;p&gt;This walkthrough only touched governance and onboarding. The skill also covers Gateway (Kafka proxy) encryption, data quality rules, Terraform export, and CI/CD scaffolding.&lt;/p&gt;

&lt;p&gt;Try it, it's one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add conduktor/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's &lt;a href="https://github.com/conduktor/skills" rel="noopener noreferrer"&gt;open source&lt;/a&gt;, so if you hit a workflow it handles badly, open a PR. And if you're new to Conduktor, the &lt;a href="https://www.conduktor.io/community" rel="noopener noreferrer"&gt;Community Edition&lt;/a&gt; is free and self-hosted, the skill will do the install for you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was adapted from the &lt;a href="https://www.conduktor.io/blog/set-up-a-kafka-platform-with-an-ai-agent" rel="noopener noreferrer"&gt;original on the Conduktor blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>dataengineering</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Most Kafka Cost Calculators Miss This</title>
      <dc:creator>Stéphane Derosiaux</dc:creator>
      <pubDate>Mon, 25 May 2026 15:19:36 +0000</pubDate>
      <link>https://dev.to/conduktor/how-to-analyze-the-cost-of-kafka-2a4b</link>
      <guid>https://dev.to/conduktor/how-to-analyze-the-cost-of-kafka-2a4b</guid>
      <description>&lt;p&gt;Which side are you on: "This is just what Kafka costs at scale" or "We should switch to a cheaper Kafka provider"?&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://conduktor.io" rel="noopener noreferrer"&gt;Conduktor&lt;/a&gt;, our field team works inside Kafka environments that have been running for a long time. We see this: most Kafka teams are overpaying by 25 to 40 percent. Not because anyone did anything wrong, but because of how Kafka got built up over time.&lt;/p&gt;

&lt;p&gt;The cost drivers of Kafka are weirdly context-dependent: the infrastructure and the provider are a tiny part of the full picture. &lt;/p&gt;

&lt;p&gt;The "how" it's being used is the real question.&lt;/p&gt;




&lt;h2&gt;
  
  
  Five bad patterns eating budget
&lt;/h2&gt;

&lt;p&gt;Below is what see, the same patterns show up everywhere, and are the first things we work with our customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Partition overprovisioning
&lt;/h3&gt;

&lt;p&gt;"How many partitions?" is the most common question with Kafka. I heard last week someone telling me an org just defaults to "64". I was shocked. Not only providers may price per partitions, but from a Kafka point of view: this takes metadata and open files etc.&lt;/p&gt;

&lt;p&gt;Partitions depend on throughput and concurrency expected (consumer parallelism). If a 64-partitions topic is sitting in a cluster with barely no traffic, you're just losing money on all sides. Multiply by dozens or hundreds of topics at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Retention that makes no sense
&lt;/h3&gt;

&lt;p&gt;Long retention on topics that nobody reads past the last few hours. Do you need replay? Default is 7-day retention, but it's often applied uniformly, when some topics only need a couple of hours and others genuinely need weeks.&lt;/p&gt;

&lt;p&gt;Tips: when using compacted topics and/or Kafka streams (changelog etc.), data is being stored indefinitely, that can cause some security/regulations issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Let's spin up another cluster
&lt;/h3&gt;

&lt;p&gt;One-cluster-per-team was a reasonable isolation strategy a long time ago. We saw this multiple times, more than 500 clusters, with tons of mirroring to share data. Throwing money down the drain.&lt;/p&gt;

&lt;p&gt;You're paying for underutilized clusters instead of consolidating onto fewer well-managed ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Zombie topics
&lt;/h3&gt;

&lt;p&gt;Topics created for experiments, migrations, or one-off tests that were never cleaned up. It's a simple thing but cost so much money as no one is looking. Every one of them is replicated and has retention costs. We've seen enterprises with hundreds of zombie topics, who were so surprised when we showed them.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Runaway egress
&lt;/h3&gt;

&lt;p&gt;We had a customer where egress was running 30x higher than ingress on a single topic because of a misconfigured consumer. Buggy consumers, unnecessary fan-out, and chatty clients create traffic patterns that are invisible without dedicated infra monitoring. Egress is rarely free.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to deal with it
&lt;/h2&gt;

&lt;p&gt;Pick your starting point based on where the waste is concentrated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop the bleeding: better defaults
&lt;/h3&gt;

&lt;p&gt;Low-coordination work that pays off over time. It's better to have exceptions rather than wrong defaults you can't rollback.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set sensible low partition defaults (3) and short retention (1 day). Increase if necessary only. &lt;/li&gt;
&lt;li&gt;Enforce client-side compression. (Conduktor Gateway)&lt;/li&gt;
&lt;li&gt;Require ownership metadata at topic creation. (Conduktor)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This won't reduce your bill right away, but it will prevent it from getting worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trim the fat: optimize what's running
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tune retention where it's drifted, analyze consumer patterns.&lt;/li&gt;
&lt;li&gt;Retire topics with no active producers or consumers.&lt;/li&gt;
&lt;li&gt;Right-size partition counts (this is the hard one, since it means recreating topics and coordinating with every producer and consumer). - Consolidate Kafka clusters, introduce multi-tenancy (Conduktor)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This work easily moves the infrastructure bill, we saw reductions of $500k just doing this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Now, keep it clean, be disciplined
&lt;/h2&gt;

&lt;p&gt;After a cleanup, the same "drift" will start operating again.&lt;/p&gt;

&lt;p&gt;To help you keeping the direction, have absolute visibility into what you Kafka ecosystems contains and what it costs (&lt;a href="https://conduktor.io/blog/chargeback-attribute-map-kafka-costs-to-your-business" rel="noopener noreferrer"&gt;chargeback&lt;/a&gt; is powerful for this), clear ownership so every topic and cluster has a team accountable for it, and a regular review cadence to catch drift before it becomes permanent. Not heavyweight governance. Just enough discipline that the cleanup doesn't have to be repeated every year.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;The diagnostic question is simple: which of these patterns are present in your environment, and what are they costing you?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://conduktor.io/blog/a-better-conversation-about-kafka-costs" rel="noopener noreferrer"&gt;original deep-dive&lt;/a&gt; goes further into the four layers of Kafka cost (infrastructure, ecosystem tooling, vendor/licensing, and operational) and includes a framework for sequencing the work.&lt;/p&gt;

&lt;p&gt;If you want to look at your own estate, Conduktor's field team does a &lt;a href="https://conduktor.io/contact/demo" rel="noopener noreferrer"&gt;free cost analysis&lt;/a&gt; where they walk through your environment with you and give you concrete numbers.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>datastreaming</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
