# cilium/ebpf: writing eBPF programs in pure Go

> The cilium/ebpf library loads, modifies and attaches eBPF programs from Go without cgo, and bpf2go compiles C programs into embeddable Go. It is for Go engineers building long-running kernel observability and networking tools, not for Kubernetes users looking for the Cilium CNI.

**cilium/ebpf** — ebpf-go is a pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel.

- Repository: https://github.com/cilium/ebpf
- Website: https://ebpf-go.dev
- Stars: 7,979 · Forks: 893
- Language: Go
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cilium-ebpf

## What cilium/ebpf solves, and who it is not for

Loading an eBPF program from a Go process means talking to the bpf syscall directly: creating maps, resolving BTF type information, fixing up relocations in the ELF object, attaching to a hook and keeping the file descriptors alive. cilium/ebpf packages that work. The README describes it as a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs, with minimal external dependencies, intended to be used in long running processes. The pure-Go part is the distinguishing property: no cgo, so cross-compilation and static binaries stay simple.

The audience is Go engineers writing agents, profilers or network dataplanes that ship their own eBPF. It is not the Cilium CNI, despite the shared name and organisation. If you are looking for Kubernetes pod networking, NetworkPolicy or kube-proxy replacement, this repository is the wrong artefact; the Cilium project is a separate codebase that happens to be the origin of this library. The README also points readers to ebpf.io for complementary projects rather than presenting itself as a platform.

## The packages that make up the library

The README lists the packages explicitly, and the list is a good map of the design. asm is a basic assembler so you can emit eBPF instructions from Go, though the README notes you do not need it if you prefer writing the program in C. cmd/bpf2go compiles C and generates Go code for loading the program and its maps. link attaches programs to hooks. perf reads from a PERF_EVENT_ARRAY and ringbuf reads from a BPF_MAP_TYPE_RINGBUF map, which are the two event channels you will pick between. features is described as the equivalent of bpftool feature probe implemented natively in Go, useful for deciding at runtime whether a kernel supports what you need. rlimit lifts the RLIMIT_MEMLOCK constraint on kernels before 5.11. btf reads the BPF Type Format, and pin works with pinned objects on bpffs.

That split matters because btf and features are the parts that make the library usable across kernel versions. Instead of assuming a feature exists, you can query. The release notes reinforce how central BTF has become: v0.22.0 mentions vmlinux BTF caching changes, and v0.21.0 mentions BTF deduplication alongside Struct Ops and Weak Symbols.

## Installing cilium/ebpf and running a first kprobe

The README does not inline install steps; it points to the Getting Started guide at ebpf-go.dev/guides/getting-started. The module path is github.com/cilium/ebpf, so the usual Go module workflow applies.

```bash
go get github.com/cilium/ebpf
```

That adds the dependency. The go.mod in the repository declares go 1.25.0, so a toolchain at least that new is required by the module itself.

For programs written in C, the second step is bpf2go, which the README says compiles and embeds eBPF programs written in C within Go code and auto-generates Go code for loading and manipulating the program and map objects. It is invoked through go:generate rather than as a standalone binary you install globally.

```go
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-22 kprobe ./kprobe.c -- -I./headers
```

Running go generate produces a Go file with a loadKprobe function and the compiled object embedded. Note the -cc flag: the repository Makefile pins CLANG ?= clang-22 and CFLAGS := -O2 -g -Wall -Werror -mcpu=v2, so the toolchain version is not incidental. If your clang is older, the generated objects may not match what the library expects.

Attaching is then a matter of the link package. The repository ships examples/kprobe/, examples/tracepoint_in_c/, examples/tracepoint_in_go/, examples/xdp/, examples/tcx/, examples/ringbuffer/ and others, each with its own directory, and examples/README.md indexes them. Those directories are the practical reference for the load-and-attach sequence, since the top-level README stops at the package list.

## Where cilium/ebpf stops being the right tool

The requirements section is unusually honest about the boundary. Linux on amd64 and arm64 is tested against kernel.org LTS releases, and the README says >= 4.4 should work but EOL'ed versions are not supported. Other architectures are best effort, and 32bit arches are not supported at all. If your fleet includes 32-bit ARM nodes, this library will not cover them, and no amount of configuration changes that.

The second constraint is the toolchain. bpf2go compiles C, which means a clang and LLVM toolchain must exist at build time. The Makefile pins llvm-strip-22 and llvm-objcopy-22 alongside clang-22, and the CI runs inside a container image referenced from testdata/docker/IMAGE. Reproducing that environment is the maintainers' answer to version drift, and it is a signal that a mismatched clang is a real failure mode rather than a theoretical one.

The third is scope. The library loads and attaches programs; it does not give you a Kubernetes integration, a policy engine or a dataplane. The README's own framing is a set of utilities, and the contributing note asks for use cases that help shape the project. If you want a managed networking layer, you are looking at the wrong repository.

There is also a support-channel caveat the README states plainly: the #ebpf-go Slack channel is described as ephemeral and its history is erased past a certain point, which is less helpful for others running into the same problem later. GitHub Discussions is the durable path.

## cilium/ebpf compared with libbpf and bpftool

The obvious alternative is libbpf, the C library that most eBPF tooling is built on. The difference is the language boundary. libbpf is C, so a Go program using it needs cgo, which complicates cross-compilation and static linking. cilium/ebpf reimplements the loading path in Go, which is why the README can claim minimal external dependencies and why the go.mod dependency list is short: golang.org/x/sys, golang.org/x/sync and jsimonetti/rtnetlink/v2 are the direct requirements, with the rest marked indirect.

The trade-off runs the other way too. libbpf is the reference implementation and tracks kernel features first; a pure-Go reimplementation has to catch up, which is visible in the release notes. v0.22.0 is titled around Linux 7.1 compat, BPF tokens and vmlinux BTF caching changes. If you depend on the newest kernel facility the day it lands, the C ecosystem will usually have it sooner.

bpftool is a different kind of alternative: a command-line utility for inspecting and manipulating BPF objects. The features package is explicitly modelled on bpftool feature probe, so if your need is a one-off inspection rather than an in-process loader, the CLI is simpler. The library earns its place when the loading has to happen inside a long-running Go process that manages its own file descriptors and lifecycle.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-09-21, one day before this writing, so it is under active development by any reasonable reading. The release cadence shown is roughly every four to five months: v0.20.0 on 2025-10-31, v0.21.0 on 2026-03-05, v0.22.0 on 2026-06-29. All are pre-1.0, which is the practical upgrade consideration. A minor version bump can carry behavioural changes; v0.22.0's title mentions Linux 7.1 compat and vmlinux BTF caching changes, and v0.21.0 introduced Struct Ops and Weak Symbols. None of those are patch releases.

Because the module is a library rather than a binary, upgrading means rebuilding your agent and re-running its tests against the kernels you support. The repository's own Makefile builds a long list of testdata targets, including loader-clang-14, loader-clang-17 and loader-$(CLANG), plus loader_nobtf and manyprogs. That is a heavy test matrix, and it is a fair indication of how much surface a version bump can touch.

The licence is MIT, stated in both the README and the LICENSE file at the repository root. MIT is permissive and imposes no copyleft obligation on your own code, but this is not legal advice; if you redistribute the library, keep the copyright notice and licence text with it. One non-licence governance detail worth knowing: the README says the project is governed by Cilium's AI policy and the Linux Foundation's AI policy, with its own policy in AGENTS.md, and that submissions in violation are rejected without warning. If you plan to contribute, read AGENTS.md before opening a pull request.

## Conclusion

Adopt cilium/ebpf if you are writing a Go daemon that loads its own eBPF programs and needs no cgo, and you can pin a kernel version in CI. Do not adopt it if you want the Cilium CNI, or if you need 32-bit architectures, which the README says are not supported. Before committing, run the Getting Started guide's build on your target kernel and confirm bpf2go can find a clang that matches the Makefile's pinned CLANG.

## FAQ

### What is cilium/ebpf?

It is a pure-Go library for loading, compiling and debugging eBPF programs, described in its README as having minimal external dependencies and intended for long running processes. It is the library, not the Cilium CNI.

### How does Cilium use eBPF?

The repository does not describe how the Cilium CNI uses eBPF; it presents itself as a library of utilities for loading, compiling and debugging eBPF programs, and points to ebpf.io for complementary ecosystem projects. Questions about the CNI itself belong with that separate project.

### Can eBPF programs crash the kernel?

The project documentation does not discuss kernel crashes. What it does document is the loading and attachment path through the link package, and a rlimit package that lifts the RLIMIT_MEMLOCK constraint on kernels before 5.11.

## Sources

- [cilium/ebpf on GitHub](https://github.com/cilium/ebpf)
- [License: MIT](https://github.com/cilium/ebpf/blob/main/LICENSE)
- [Project website](https://ebpf-go.dev)
- [README](https://github.com/cilium/ebpf/blob/main/README.md)
- [Releases](https://github.com/cilium/ebpf/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cilium-ebpf
