← Blog

Kafka Go Clients: franz-go vs kafka-go vs Sarama

The Go ecosystem has four major Kafka clients that do not share a common implementation. Sarama, kafka-go, and franz-go each implement the Kafka protocol in pure Go, while confluent-kafka-go wraps Confluent's librdkafka C library via cgo. The differences go beyond API style: Kafka feature coverage, consumer-group behavior, build requirements, and default partitioning all vary from client to client.

This guide compares the four across deployment, consumer-group protocols (including KIP-848), authentication, default partitioners, and Schema Registry support. Capabilities are evaluated against specific releases, and a GitHub snapshot compares maintenance activity. Finally, it outlines when to stay on Sarama or kafka-go and how to check whether a new client routes keyed records to different partitions than the old one.

Quick decision

SituationBest starting pointWhat to verify
New pure Go service, or a build that must avoid cgofranz-goSchema Registry pkg/sr requires application-provided codecs; one account handled all recorded PR merges in the 12-month snapshot.
Reader / Writer API for basic produce and consumekafka-goThe default Writer ignores record keys and uses round-robin. There is no high-level transactional producer API, cooperative-sticky assignor, or KIP-848 consumer-group support. Confluent Cloud flags its consumer as noncompliant.
Confluent's Schema Registry serializers or commercial support, and cgo is acceptableconfluent-kafka-goAlpine/musl builds require -tags musl; GSSAPI requires a separately installed librdkafka.
Migrating from Sarama or kafka-go for broader protocol support without cgofranz-goDefault partitioners differ, so keyed records can move to other partitions. See Keep keyed records on the same partitions.

Tip: These clients provide Kafka messaging, not a Kafka Streams equivalent. For Kafka-backed tables, messaging frameworks, or in-process stream pipelines, see Goka vs Watermill vs go-streams.

Pure Go or cgo?

Sarama, kafka-go, and franz-go build with CGO_ENABLED=0. For Linux targets, a standard Go cross-build can produce a statically linked binary without a target C toolchain. This simplifies deployment in scratch or distroless images, although TLS connections still need the required CA certificates. Pure Go client code also remains visible in Go stack traces and CPU profiles.

confluent-kafka-go wraps librdkafka through cgo. That trade-off is often worthwhile when you need Confluent's first-party Schema Registry serializers or commercial support. The build and deployment requirements are:

  • Build: CGO_ENABLED=1 and a C compiler are required. Cross-compiling needs a C toolchain for the target architecture.
  • Containers: The bundled librdkafka archive does not make the final binary fully static: a glibc build still depends on the target system's libc. Alpine/musl builds require -tags musl; a fully static musl build also needs a musl C compiler and external-linker flags. Test the binary inside the production container image.
  • GSSAPI/Kerberos: The bundled library excludes GSSAPI. Enabling it requires a separately installed librdkafka and -tags dynamic.

See Confluent's build instructions.

Kafka Go clients compared

Capabilities verified against official documentation and package artifacts: 26 Sep 2026

Areafranz-go v1.22.0kafka-go v0.4.51Sarama v1.61.0confluent-kafka-go v2.15.1
APIkgo.ClientWriter, ReaderSyncProducer, AsyncProducer, ConsumerGroupProducer, Consumer
Consumer modelkgo.Client with PollFetches / PollRecords; configurable commitsReader with GroupID; FetchMessage and CommitMessagesClaim handlers in ConsumerGroup.ConsumeConsumer.SubscribeTopics and Poll / ReadMessage; configurable commits
Admin APIkadmAdmin helpers in the same moduleCluster admin types in the same moduleAdminClient
Transactions / EOSTransactions and documented EOS flowNo high-level transactional producer or consume-transform-produce APIProducer transactionslibrdkafka transactions
Consumer-group protocolsCooperative-sticky; KIP-848 via ServerSideBalancer() in v1.22.0 (Kafka 4.0+, 4.3+ recommended)No built-in cooperative assignor; no documented KIP-848 consumer membershipCooperative-sticky from v1.60.1; KIP-848 ConsumerGroupDescribe API only, no consumer membershipcooperative-sticky; KIP-848 GA from 2.12.0 (Kafka 4.0+; opt-in)
AuthenticationTLS; SASL PLAIN, SCRAM, OAUTHBEARER, GSSAPI, AWS_MSK_IAMTLS; SASL PLAIN and SCRAM; separate AWS MSK IAM moduleTLS; SASL PLAIN, SCRAM, OAUTHBEARER, GSSAPITLS; SASL PLAIN, SCRAM, OAUTHBEARER; GSSAPI with dynamic librdkafka
Default partitionerKeyed: Murmur2; unkeyed: adaptive sticky batchesKeyed and unkeyed: round-robin; keys ignored unless Balancer is setKeyed: FNV-1a; unkeyed: randomKeyed: CRC32 (consistent_random); unkeyed: sticky random
Schema Registrypkg/sr client and Serde helper; provide codecsSeparate integrationSeparate integrationFirst-party schemaregistry serializers

A successful produce does not prove another service can deserialize the record. Schema Registry framing, headers, and compression still have to match across clients.

Cooperative rebalancing under the classic group protocol (KIP-429) is separate from KIP-848's broker-side assignment. The default partitioner row describes the high-level producer defaults. See the kafka-go partitioner guide, franz-go partitioner documentation, and librdkafka configuration before changing a keyed producer.

franz-go

franz-go is a pure Go client built around a single *kgo.Client for producing, consuming, and issuing Kafka requests. The project aims to support every client-facing Kafka feature from Kafka 0.8.0 onward. Admin helpers live in kadm.

Capabilities and limitations

  • API: A single kgo.Client handles both producing and consuming; there are no separate producer and consumer types. Consumers retrieve records through PollFetches or PollRecords.
  • Protocol coverage: v1.22.0 includes idempotent and transactional production and a documented consume-transform-produce EOS flow. See the project's transaction guide.
  • Consumer commits: Consumer groups automatically commit offsets by default, including offsets for records returned by a poll that may still be processing. Use AutoCommitMarks(), disable auto-commit and commit after processing, or use BlockRebalanceOnPoll() with its rebalance-timeout trade-off when processing must finish first.
  • Consumer groups: Cooperative-sticky assignment is supported under the classic protocol. KIP-848 is available through ServerSideBalancer() in v1.22.0. It requires Kafka 4.0+ and a range or sticky balancer; the project recommends Kafka 4.3+ because earlier brokers can return STALE_MEMBER_EPOCH during normal offset commits. The classic protocol remains the default.
  • Default partitioner: Keyed records use Murmur2, matching the Java client's default. Unkeyed records stay on a partition until the default byte threshold is reached, then choose the next partition adaptively.
  • Schema Registry: The separately versioned pkg/sr module provides a Registry API client and a Serde for Confluent's wire format. It does not include Avro, Protobuf, or JSON Schema codecs; applications register their own encode and decode functions.
  • Maintenance: The repository snapshot shows the most commits and merged PRs of the four clients, with a median merge time of 2.1 hours. One account handled all recorded merges in the preceding year.

When to choose: Choose franz-go for a new cgo-free service that needs broad Kafka protocol coverage, transactional consume-transform-produce flows, or KIP-848. Prefer confluent-kafka-go when Confluent's first-party serializers or commercial support are required and cgo is acceptable.

kafka-go

kafka-go is Segment's pure Go client. It provides context-aware high-level Writer and Reader types, plus the lower-level Conn API for direct broker operations.

Capabilities and limitations

  • API: The high-level Reader and Writer types avoid Sarama's handler and channel model, reducing setup code for basic produce and consume.
  • Default partitioner: The default Writer balancer is round-robin and ignores record keys. Set Balancer explicitly, for example to &kafka.Hash{}, if a key must stay on one partition.
  • Consumer commits: With a consumer group, ReadMessage automatically commits offsets and may do so before processing finishes. Use FetchMessage with CommitMessages for explicit control. Committing a later message also commits every earlier offset in that partition, so concurrent processing must preserve commit order.
  • Protocol coverage: The project documents testing against Kafka 0.10.1.0 through 2.7.1; later brokers may work, but newer APIs may be missing. v0.4.51 has no high-level transactional producer or consume-transform-produce API. Administrative operations use Conn methods and protocol request types rather than a dedicated AdminClient.
  • Consumer groups: No built-in cooperative-sticky assignor; no documented KIP-848 consumer-group membership. Confluent Cloud lists the latest kafka-go consumer as noncompliant.
  • Schema Registry: kafka-go does not include a Schema Registry client or serializers; schema encoding requires a separate integration.
  • Maintenance: The 25 Sep 2026 snapshot shows no default-branch commits or merged PRs in 90 days, with 83 open PRs at a median age of 2.6 years. Check the release history and issue backlog for requirements you expect to evolve.

When to choose: Choose kafka-go when the Reader/Writer API is the priority, v0.4.51 covers the required broker and consumer-group behavior, and its maintenance pace is acceptable for the service. For a new service that needs broader protocol coverage, start with franz-go.

Sarama

Sarama is the oldest of the four clients: a pure Go library created at Shopify in 2013 and now hosted by IBM under the import path github.com/IBM/sarama. It provides synchronous and asynchronous producers, consumer groups, and admin APIs.

Capabilities and limitations

  • Compatibility: Sarama documents a "2 releases + 2 months" policy for Kafka and Go: it supports the two latest stable releases, with a two-month grace period for older ones. Pin the module version and review the changelog when upgrading Kafka or Go.
  • API surface: Sarama separates synchronous and asynchronous producers, consumer groups, and admin operations. Consumer groups require a ConsumerGroupHandler. Applications using AsyncProducer must drain the error channel and, when Producer.Return.Successes is enabled, the success channel. This explicit lifecycle provides control but adds coordination and boilerplate.
  • Consumer commits: MarkMessage and MarkOffset only mark an offset as processed. With auto-commit enabled by default, Sarama commits marked offsets in the background every second; offsets advance only as far as the handler marks them. Call session.Commit() for a synchronous commit. Marked offsets not yet committed can be lost in a crash, so processing should tolerate redelivery. A new group starts from the newest offset by default (Consumer.Offsets.Initial = OffsetNewest).
  • Consumer groups: Cooperative-sticky rebalancing under the classic group protocol is supported from v1.60.1. v1.61.0 adds the KIP-848 ConsumerGroupDescribe API only; KIP-848 consumer-group membership is not supported.
  • Default partitioner: NewHashPartitioner hashes non-nil keys with FNV-1a, not the Java client's Murmur2, and sends records with nil keys to a random partition. Check key placement before another producer writes the same keys; see Keep keyed records on the same partitions.
  • Schema Registry: Sarama does not include a Schema Registry client or serializers, so schema encoding requires a separate integration.
  • Maintenance: The repository snapshot shows recent commits and merged PRs, but one account handled 98% of recorded PR merges in the preceding year.

When to choose: Keep Sarama when an existing application meets its requirements. For a new pure Go service, start with franz-go unless compatibility with existing Sarama code and tooling, or the team's experience with its API, provides a concrete advantage.

confluent-kafka-go

confluent-kafka-go is Confluent's Go client. Producer, consumer, and admin APIs wrap librdkafka through cgo. See Pure Go or cgo? for build and container requirements.

Capabilities and limitations

  • API: Configuration uses ConfigMap with librdkafka property names rather than typed Go option functions. Producer.Produce() queues records asynchronously; final delivery reports arrive through Events() or a message-specific delivery channel. Consumer uses timeout-based polling (Poll or ReadMessage) rather than context.Context-driven cancellation.
  • Protocol coverage: Most broker protocol behavior comes from the bundled librdkafka version. v2.15.1 includes idempotent and transactional producers and broad admin operations through AdminClient.
  • Consumer commits: enable.auto.commit=true, auto.commit.interval.ms=5000, and enable.auto.offset.store=true by default. For at-least-once processing, set enable.auto.offset.store=false, call StoreMessage or StoreOffsets after processing, and leave auto-commit enabled. Alternatively, disable auto-commit and call CommitMessage or CommitOffsets explicitly.
  • Consumer groups: Cooperative-sticky assignment is available through partition.assignment.strategy. KIP-848 is GA from v2.12.0 with Kafka 4.0+ brokers; enable it with group.protocol=consumer. See Confluent's versioned migration guide. The classic protocol remains the default.
  • Default partitioner: The default consistent_random partitioner hashes keyed records with CRC32, not Murmur2. This does not match the Java client's default or franz-go. To match Java partitioning, set partitioner to murmur2_random.
  • Schema Registry: The module includes first-party serializers for Avro, Protobuf, and JSON Schema that handle schema registration, caching, and wire framing. These integrations and Confluent commercial support are the primary reasons to accept cgo.
  • Maintenance: The repository snapshot shows recent development with merges spread across more accounts than the other clients (top account: 39%), alongside an older open PR backlog (74 open, median age 2.4 years).

When to choose: Choose confluent-kafka-go when Confluent's serializers or commercial support are required and every deployment target supports its cgo build requirements. Choose a pure Go client when the build must avoid a C toolchain.

Migrating from Sarama or kafka-go

When a missing feature or deployment constraint justifies changing clients, test runtime behavior as well as compilation:

  • Producer: Compare partitioning for the same keys, batching, delivery errors, and idempotence or transaction settings. A different partitioner can change per-key ordering across the migration.
  • Consumer: Compare the starting offset, commit timing, retry behavior after a failed record, and partition handoff during rebalances.
  • Consumer-group protocol: Under the classic protocol, every active member must advertise at least one common assignor. Moving from an eager assignor to cooperative-sticky requires two staged rollouts: first add cooperative-sticky alongside the old eager assignor, then remove the eager assignor after every member has been upgraded. Treat KIP-848 as a separate migration; Kafka 4.0+ supports rolling classic-to-consumer upgrades, but assignment, timeout, and rebalance callback behavior changes. See the franz-go Balancers documentation and Confluent's KIP-848 migration guide.
  • Serialization: Preserve key and value encoding, headers, Schema Registry framing, and subject naming so existing consumers can read the new producer's records.
  • Deployment: If the destination is confluent-kafka-go, build and run it on every target OS, CPU architecture, and libc combination, including the production container image.

Run the old and new consumers against a test topic with separate group IDs initialized to the same offsets. Compare processed records and commit positions before moving production traffic. If production will use a new group ID, initialize its offsets explicitly rather than relying on auto.offset.reset.

Keep keyed records on the same partitions

The defaults in the comparison table are not interchangeable. Sarama hashes keys with FNV-1a, kafka-go's default Writer ignores keys and uses round-robin, franz-go uses Murmur2, and confluent-kafka-go uses CRC32. Changing the producer can send the same key to a different partition and break per-key ordering across the cutover.

First identify the old producer's actual partitioner, including custom settings, and keep the topic's partition count fixed during the test.

  • Sarama to franz-go: Use kgo.SaramaCompatHasher with a 32-bit FNV-1a hash function. The similarly named SaramaHasher does not exactly match Sarama's default signed-hash behavior.
import (
    "hash/fnv"

    "github.com/twmb/franz-go/pkg/kgo"
)

fnv32a := func(key []byte) uint32 {
    hasher := fnv.New32a()
    hasher.Write(key)
    return hasher.Sum32()
}

client, err := kgo.NewClient(
    kgo.SeedBrokers(brokers...),
    kgo.RecordPartitioner(kgo.StickyKeyPartitioner(kgo.SaramaCompatHasher(fnv32a))),
)
  • Sarama to kafka-go: Set Writer.Balancer to &kafka.Hash{}; use &kafka.ReferenceHash{} only if the old producer used Sarama's reference hash partitioner. The versioned kafka-go compatibility guide documents both mappings.
  • To confluent-kafka-go: Set partitioner=murmur2_random for Java-compatible Murmur2 placement used by keyed franz-go records, or partitioner=fnv1a_random for Sarama's default NewHashPartitioner (librdkafka implementation); it does not match NewReferenceHashPartitioner. kafka-go's default round-robin placement has no stable key mapping to reproduce.

Produce representative keys, including empty and nil keys where applicable, to a test topic with the same partition count from both clients. Compare the resulting partitions before switching production traffic. Matching a hash function alone is insufficient if the clients treat empty keys or the partition count differently.

Sarama to franz-go

TaskSaramafranz-goMigration check
Configurationsarama.NewConfig() and client or producer constructorskgo.NewClient(opts...)Map broker version, TLS/SASL, retries, batching, and timeouts explicitly.
ProduceSyncProducer.SendMessage or AsyncProducer.Input()ProduceSync or Produce with a completion callbackCompare delivery errors, flush and shutdown behavior, and the partitioner.
Consume in a groupConsumerGroup.Consume with a handler for each claimConsumerGroup and ConsumeTopics options, then PollFetches or PollRecordsPreserve per-partition processing order and the intended start offset.
Commit offsetssession.MarkMessage and session.Commit or auto-commitAutoCommitMarks() with MarkCommitRecords, or DisableAutoCommit() with CommitRecordsCommit only after the work the old service considered complete.
Handle rebalancesHandler Setup / Cleanup and claim lifecycleOnPartitionsAssigned, OnPartitionsRevoked, OnPartitionsLostFlush in-flight work before a partition moves; check callback concurrency.
Handle errorsProducer error channel, returned errors, and consumer errorsProduce callback or result, Fetches.Errors(), and returned errorsKeep failures visible and preserve retry policy.
AdminClusterAdminkadm.NewClientMap only the operations your service actually uses.

franz-go's default group auto-commit timing may differ from a Sarama handler that marks records after processing. Set the commit strategy deliberately, and use the franz-go group documentation when translating a handler. Test partition revocation while work is in flight; a successful compile does not prove equivalent processing guarantees.

kafka-go to franz-go

  • Produce: Replace Writer.WriteMessages with ProduceSync or asynchronous Produce. If consumers relied on kafka-go's round-robin distribution, franz-go's default partitioner will change where keyed records land. If the old writer set a balancer explicitly, map and test that algorithm before the cutover.
  • Consume and commit: Replace a Reader.FetchMessage loop with PollFetches or PollRecords. To preserve explicit commits after processing, configure DisableAutoCommit() and call CommitRecords; franz-go otherwise auto-commits records from previous polls on an interval. A kafka-go service using ReadMessage already has automatic commits, but the timing still needs to be tested.
  • Rebalancing: kafka-go does not provide cooperative-sticky assignment, while franz-go uses it by default under the classic protocol. If both clients temporarily share a group ID, use the staged assignor rollout described above. A separate group ID avoids a mixed group but has independent committed offsets.

GitHub repository metrics

The tables below compare the four clients' GitHub activity, maintenance, and public usage using the same measurement windows and assessment rules as the Python client comparison. Snapshot: 25 Sep 2026.

Project overview

Attributefranz-gokafka-goSaramaconfluent-kafka-go
Repository created25 Mar 20199 May 20175 Jul 201312 Jul 2016
LicenseBSD-3-ClauseMITMITApache-2.0
Repository statusNot archived · originalNot archived · originalNot archived · originalNot archived · original

Activity

Metricfranz-gokafka-goSaramaconfluent-kafka-go
Latest default-branch commit25 Sep 202623 Apr 202624 Sep 202624 Sep 2026
Latest GitHub ReleaseNone listedv0.4.51 (23 Apr 2026)v1.61.0 (22 Sep 2026)v2.15.1 (10 Sep 2026)
GitHub Releases (12 mo)02159
Commits386 (90 d) · 1,102 (12 mo)0 (90 d) · 2 (12 mo)86 (90 d) · 385 (12 mo)55 (90 d) · 156 (12 mo)
Issue flow (90 d)13 opened · 15 closed2 opened · 1 closed12 opened · 13 closed3 opened · 3 closed
PR flow (90 d)111 opened · 103 merged12 opened · 0 merged123 opened · 74 merged42 opened · 29 merged
Activity assessment🟢 386 commits and 103 merged PRs in 90 days.🔴 No commits or merged PRs in 90 days.🟢 86 commits and 74 merged PRs in 90 days.🟢 55 commits and 29 merged PRs in 90 days.

Release figures count GitHub Releases, not Go module tags. franz-go has recent version tags despite having no GitHub Release entries in this snapshot.

Maintenance

Metricfranz-gokafka-goSaramaconfluent-kafka-go
Active commit authors (12 mo)4512627
PR merge distribution (12 mo)1 person · 100% of merges2 people · Top 1: 50% · Top 2: 100%2 people · Top 1: 98% · Top 2: 100%17 people · Top 1: 39% · Top 2: 56%
Issue backlog0 open issues182 open · median age 3.2 y14 open · median age 1.8 y202 open · median age 3.6 y
Issue closure rate19/20 closed within 30 d and 90 d0/1 within 30 d or 90 d (small sample)6/7 closed within 30 d · 7/7 within 90 d3/3 closed within 30 d and 90 d (small sample)
PR backlog0 open PRs83 open · median age 2.6 y34 open · median age 1 d74 open · median age 2.4 y
Median PR merge time (90 d)2.1 h (n=103)N/A — no merged PRs1.3 d (n=74)4.2 d (n=29)
Published GitHub security advisories0000
Responsiveness assessment🟢 Median merge time 2.1 h; 19 of 20 cohort issues closed within 90 d.🔴 No PRs merged in 90 d; 83 open PRs have a median age of 2.6 y.🟢 Median merge time 1.3 d; 7 of 7 cohort issues closed within 90 d.🟡 Median merge time 4.2 d; 74 open PRs have a median age of 2.4 y.
PR merge concentration assessment🔴 One account handled all recorded merges.🟡 The largest account handled 50% of merges.🟡 One account handled 98% of merges.🟢 The largest account handled 39% of merges.

PR merge distribution counts non-bot mergedBy accounts, so automated merges may undercount human reviewers. Issue closure rates use issues opened 90–180 days before the snapshot, giving each issue a full 90-day window.

Public usage and interest

Metricfranz-gokafka-goSaramaconfluent-kafka-go
Stars3,0728,63212,5185,168
Forks3058601,869703
GitHub dependents (Used by)1,99010,9897,1627,144
Public usage assessment🟢 1,990 dependents, 305 forks, and 3,072 stars.🟢 10,989 dependents, 860 forks, and 8,632 stars.🟢 7,162 dependents, 1,869 forks, and 12,518 stars.🟢 7,144 dependents, 703 forks, and 5,168 stars.

GitHub dependents are approximate public-repository counts. Stars, forks, and dependents indicate visible usage and interest, not whether a library fits your application's Kafka requirements.

Overall repository signals

  • franz-go has the most recent commits and merged PRs, with fast recorded merge times; one account handled all recorded PR merges in the past year.
  • kafka-go has the highest public dependent count of the four, but no default-branch commits or merged PRs in the last 90 days; its open issue and PR backlogs are old.
  • Sarama has broad public usage and recent development activity, but one account handled 98% of recorded PR merges in the past year.
  • confluent-kafka-go has recent activity and broad public usage, with PR merges spread across more accounts than the other three; its open PR backlog is old.

These are maintenance and adoption signals, not evidence of runtime quality or performance.

Test Go Kafka clients with Kafma

A clean go build does not show which partition a key reached, what bytes a producer wrote, or what a consumer does with a record it cannot decode. Go Kafka clients expose keys and values as []byte in their consumer APIs, so deserialization and tombstone handling live in application code or a serializer layer. Retry and commit defaults still differ by client.

Kafma is a desktop Kafka UI that runs alongside your Go service. Its Kafka console provides one client-independent place to inspect producer output, send controlled records, and watch the consumer group's committed position.

Compare partitions for the same keys

Produce the same keys from the old and new producers to a test topic with the same partition count. In Kafma, compare each record's key and partition. Look for two problems: one key spread across partitions by a single producer, as kafka-go's default round-robin Writer does, and one key placed differently by two producers, such as Sarama's FNV-1a and franz-go's Murmur2.

The example below sends customer_42 to a three-partition topic. kafka-go spreads three records across P0, P1, and P2, while Sarama sends the key to P2 and franz-go sends it to P0. The expanded franz-go record also shows the test headers and decoded JSON value.

Kafma comparing the partitions selected by kafka-go, Sarama, and franz-go for the same record key

Check Schema Registry framing

Sarama and kafka-go rely on a separate library for Schema Registry framing, and franz-go's pkg/sr needs codecs you supply. When a consumer cannot decode a record, open it in Kafma. With Schema Registry configured, Decoded shows the value resolved through its schema ID, and Raw shows the original bytes as hexadecimal. Browse subjects and versions in Kafma's Schema Registry UI, then compare their schema IDs with the record.

In the default payload-prefix format, the raw value starts with a zero magic byte and a 4-byte schema ID. If Raw starts with plain JSON or text, the prefix is absent; check whether the producer intentionally uses header-based schema identification before treating the record as unframed.

Kafma showing an Avro record decoded through schema ID 7, with the Decoded and Raw views

Send bad records to a running consumer

Keep the consumer running and use the producer panel to send, to one partition: a valid record, a payload the decoder rejects, a tombstone with a null value, and another valid record. Check whether each bad record is retried, skipped, or stops the partition, and whether the final record is processed and committed.

An unrecovered panic, such as a nil dereference while handling a tombstone, exits the process and stops consumption on every partition.

To test shutdown, use Loop to keep records arriving, then send the service SIGTERM. Open Watch Group and confirm that the member leaves, assignments settle, and the committed position matches the last record the application completed. Kafma reads this state without joining the group, triggering a rebalance, or committing offsets. To follow lag and partition assignments across every member, open the group in Kafma's consumer group UI.

Kafma watching pending and consumed records around a consumer group's committed offsets

Clone data for migration tests

Kafma Data Clone copies a selected range of real records to a test topic. Use it to exercise older schema versions, missing headers, tombstones, and other cases that hand-written fixtures miss. It can copy keys, values, timestamps, and headers, register required schemas, and mask selected fields.

Use the clone to check decoding and consumer behavior. Copied records receive new offsets and may land on different partitions, so compare partitions with records produced directly by the old and new clients instead. See the Data Clone guide for copy behavior.

Kafma cloning selected topics and masked records into a test cluster

Download Kafma and connect to the same test cluster as the Go service.

Frequently asked questions

Is there an official Apache Kafka client for Go?

No. Apache Kafka's first-party client is Java (kafka-clients). In Go, Confluent maintains confluent-kafka-go on librdkafka. Sarama, kafka-go, and franz-go are independent pure Go implementations.

Is confluent-kafka-go faster than franz-go?

There is no reliable general winner. librdkafka can perform well, but cgo alone does not make confluent-kafka-go faster than franz-go. Throughput and latency depend on message size, batching, compression, acks, broker limits, and commit behavior. Benchmark the same workload and delivery guarantees on the same brokers before using performance to choose.

Can I build confluent-kafka-go with CGO_ENABLED=0?

No. confluent-kafka-go wraps librdkafka and requires cgo. If the build must use CGO_ENABLED=0, choose Sarama, kafka-go, or franz-go. See Pure Go or cgo?.

Do Go Kafka clients partition keys the same way as Java?

For records with non-null keys, franz-go uses Java-compatible Murmur2 by default. The other clients need configuration:

  • kafka-go: Set Writer.Balancer to &kafka.Murmur2Balancer{}, which the kafka-go documentation describes as compatible with the Java default partitioner.
  • confluent-kafka-go: Set partitioner=murmur2_random; see the librdkafka configuration.
  • Sarama: Implement a custom partitioner that matches Java's Murmur2 hash, including how Java converts the hash to a positive value before selecting a partition.

Records without keys follow each client's own round-robin, sticky, or random strategy, so Java compatibility applies only to keyed records. Verify placement with representative keys before switching producers.

Which Go Kafka clients support KIP-848?

franz-go and confluent-kafka-go support KIP-848 as an opt-in on Kafka 4.0+. franz-go v1.22.0 enables it through ServerSideBalancer() and recommends Kafka 4.3+; confluent-kafka-go v2.15.1 uses group.protocol=consumer. Sarama v1.61.0 adds only the KIP-848 ConsumerGroupDescribe API, not group membership, and kafka-go v0.4.51 does not document KIP-848 group membership. Cooperative-sticky rebalancing under the classic protocol is a different feature.

Which Go Kafka client works with AWS MSK IAM?

All four can connect, through different mechanisms:

  • franz-go: The built-in pkg/sasl/aws package implements AWS_MSK_IAM directly.
  • kafka-go: The separate aws_msk_iam_v2 package implements AWS_MSK_IAM.
  • Sarama: Generate SASL/OAUTHBEARER tokens with AWS's Go signer, which includes a Sarama example.
  • confluent-kafka-go: Handle OAuthBearerTokenRefresh events, generate tokens with the AWS signer, and pass them to SetOAuthBearerToken().

Configure TLS and test credential refresh against your MSK cluster.

Is kafka-go still maintained?

The repository is not archived, and v0.4.51 was released on 23 Apr 2026. However, the 25 Sep 2026 snapshot found no default-branch commits or merged PRs in the preceding 90 days, two commits in 12 months, and 83 open PRs with a median age of 2.6 years. That indicates a slower recent maintenance pace; it does not establish that the library is abandoned or unsuitable for an existing service. Check whether the released version supports your brokers and required features before starting a new service on it.

Can producers and consumers use different Go Kafka libraries?

Yes. They must agree on key and value serialization, headers, compression, and Schema Registry framing. Consumers that share a group also need compatible group protocols. Mixing group.protocol=consumer with classic-protocol members in one group needs a planned upgrade.

If producers use different libraries for the same keyed records, align their partitioners first. Otherwise, records with the same key can land on different partitions and lose per-key ordering. See Compare partitions for the same keys.

Does Go have Kafka Streams?

Not as Apache Kafka Streams. For Kafka-backed tables, messaging frameworks, and in-process pipelines, see Goka vs Watermill vs go-streams. Most services only need a client.

Conclusion

For a new cgo-free Go service, start with franz-go when protocol coverage and recent development activity matter. Consider kafka-go when the Reader/Writer API is the priority and its current release, broker compatibility, and maintenance pace meet your needs. Use confluent-kafka-go when Confluent's serializers or support are required and cgo is acceptable. Keep Sarama when it already works.

Whichever client you choose, compare where keyed records land before replacing an existing producer or running both producers side by side; all four clients use different default partitioners.

This guide is maintained by the team behind Kafma.

Ready to try the Kafka IDE?

Available for macOS, Windows, Linux