Go Programming
Confluent-Kafka-Go
Linux
Application Development
Software Engineering

Building Go Application using confluent-kafka-go on Linux

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

confluent-kafka-go is Confluent’s Go client for Apache Kafka, built on top of librdkafka. On Linux, the main build questions are no longer “Can Go talk to Kafka?” but “Which client version should I import, do I need system librdkafka, and what build flags does my Linux target require?”

Start With the Current Build Model

Modern confluent-kafka-go projects should use the v2 module path:

go
import "github.com/confluentinc/confluent-kafka-go/v2/kafka"

That matters because older tutorials often use a pre-v2 import path and outdated install steps.

Another important detail is that the project uses cgo, because it wraps librdkafka. That means CGO_ENABLED must stay enabled. If you try to build with CGO_ENABLED=0, the client will not build correctly.

On many mainstream Linux systems, you do not need to install librdkafka manually just to get started. Current versions of the client include prebuilt librdkafka binaries for supported Linux targets. Manual installation is mainly for unsupported platforms or cases where you need features such as GSSAPI or a custom dynamic link.

Create the Project and Add the Dependency

The simplest path on a glibc-based Linux system looks like this:

bash
1mkdir kafka-demo
2cd kafka-demo
3go mod init example.com/kafka-demo
4go get github.com/confluentinc/confluent-kafka-go/v2/kafka
5go build ./...

If you are building on Alpine Linux, add the musl build tag:

bash
go build -tags musl ./...

That is one of the most common Linux-specific details people miss. Alpine uses musl rather than glibc, so the extra build tag matters.

A Minimal Producer Example

Here is a small producer that sends one message and waits for delivery reporting:

go
1package main
2
3import (
4	"fmt"
5	"time"
6
7	"github.com/confluentinc/confluent-kafka-go/v2/kafka"
8)
9
10func main() {
11	producer, err := kafka.NewProducer(&kafka.ConfigMap{
12		"bootstrap.servers": "localhost:9092",
13	})
14	if err != nil {
15		panic(err)
16	}
17	defer producer.Close()
18
19	topic := "demo-topic"
20
21	deliveryChan := make(chan kafka.Event, 1)
22	err = producer.Produce(&kafka.Message{
23		TopicPartition: kafka.TopicPartition{
24			Topic:     &topic,
25			Partition: kafka.PartitionAny,
26		},
27		Key:   []byte("order-1"),
28		Value: []byte("created"),
29	}, deliveryChan)
30	if err != nil {
31		panic(err)
32	}
33
34	select {
35	case event := <-deliveryChan:
36		msg := event.(*kafka.Message)
37		if msg.TopicPartition.Error != nil {
38			fmt.Println("delivery failed:", msg.TopicPartition.Error)
39		} else {
40			fmt.Printf("delivered to %v\n", msg.TopicPartition)
41		}
42	case <-time.After(5 * time.Second):
43		fmt.Println("delivery timed out")
44	}
45}

This sample is intentionally small, but it shows the core pieces:

  • 'bootstrap.servers identifies the broker list'
  • 'Produce is non-blocking'
  • delivery reports come back through an event channel

That last point is important. The send call queues work to the underlying client. Success is not guaranteed until the delivery report confirms it.

A Minimal Consumer Example

Once the build works, a simple consumer helps verify the runtime path:

go
1package main
2
3import (
4	"fmt"
5
6	"github.com/confluentinc/confluent-kafka-go/v2/kafka"
7)
8
9func main() {
10	consumer, err := kafka.NewConsumer(&kafka.ConfigMap{
11		"bootstrap.servers": "localhost:9092",
12		"group.id":          "demo-group",
13		"auto.offset.reset": "earliest",
14	})
15	if err != nil {
16		panic(err)
17	}
18	defer consumer.Close()
19
20	if err := consumer.Subscribe("demo-topic", nil); err != nil {
21		panic(err)
22	}
23
24	for {
25		msg, err := consumer.ReadMessage(-1)
26		if err != nil {
27			fmt.Println("consumer error:", err)
28			continue
29		}
30
31		fmt.Printf("key=%s value=%s\n", string(msg.Key), string(msg.Value))
32	}
33}

If both producer and consumer run successfully, the Linux toolchain, cgo, and Kafka connectivity are usually in good shape.

When You Need System librdkafka

Although many Linux builds work with the bundled binaries, there are situations where system installation still matters:

  • you need Kerberos or another feature not present in the bundled build
  • your Linux target is not one of the supported prebuilt platforms
  • your organization requires linking against a controlled system package

In those cases, install librdkafka using the Linux package manager and build with the dynamic tag expected by the client documentation.

That is why old advice that says “always install librdkafka-dev first” is no longer universally correct. It depends on the target and feature set.

Common Pitfalls

The most common pitfall is using the old import path without /v2. That pulls in stale examples and often leads to confusing build instructions.

Another mistake is disabling cgo. confluent-kafka-go is a Go wrapper around a C library, so CGO_ENABLED=0 is incompatible with normal builds.

A third issue is forgetting the musl build tag on Alpine Linux. The code may be fine, but the wrong Linux target assumptions will still break the build.

Finally, many developers test only whether go build succeeds and forget runtime verification. A successful compile does not prove the broker connection, topic configuration, or delivery path actually work.

Summary

  • Use the current v2 import path for confluent-kafka-go.
  • Keep cgo enabled because the client depends on librdkafka.
  • On many Linux systems, the bundled librdkafka is enough for a normal build.
  • Alpine Linux needs the musl build tag.
  • Validate both build success and live producer or consumer behavior before calling the setup complete.

Course illustration
Course illustration

All Rights Reserved.