run eBPF on Raspberry Pi
Embedded Systems
William  

How I Run eBPF on Raspberry Pi: A Step-by-Step Guide

I wanted to run eBPF on Raspberry Pi to get real tracing in my small home lab — not an academic demo but usable data from real services.

I hit a hard stop: the stock kernel lacked BTF, so Grafana Beyla-style CO-RE tracing failed with relocation errors.

My goal was simple: a Pi that boots normally, keeps its device tree, and accepts ebpf programs without breaks.

I focus only on kernel changes that make tracing work. No distro debates, no hand-waving. I use Bookworm-era Raspberry Pi OS specifics and follow the official kernel documentation as the baseline.

Table of Contents

Key Takeaways

  • I needed a custom kernel to enable required tracing features.
  • Stock kernels may handle basic tasks but can fail CO-RE without BTF.
  • The method preserves the device tree and normal boot behavior.
  • I show repeatable steps to reapply after kernel updates.
  • Official Raspberry Pi kernel documentation is the starting point; I note the exact tweaks I applied.

What breaks on the stock Raspberry Pi kernel and how to confirm it

Tracing failed early — the loader could not find kernel type information. I spot this fast by hunting for a precise Beyla error log.

Exact error to look for:

time=2024-11-15T16:03:32.999Z level=ERROR msg=”couldn’t trace process. Stopping process tracer” component=ebpf.ProcessTracer path=/usr/local/bin/cephcsi pid=2675 error=”loading and assigning BPF objects: field UprobeServeHTTP: program uprobe_ServeHTTP: apply CO-RE relocations: load kernel spec: no BTF found for kernel version 6.6.51+rpt-rpi-2712: not supported”

Plain language: the loader needs a BTF file with type information. It fails to read that file and aborts the program load. CO-RE relocations need those types to adapt to layout changes.

Quick checks I run first:

  • Confirm the board model via /proc/device-tree and uname.
  • Verify OS release (Bookworm) using /etc/os-release.
  • Check the running kernel version string — note suffixes like +rpt-rpi-2712 when you compare builds.

Important point: some simple probes may work without BTF. In my case, Beyla fails specifically because CO-RE and uprobes need that information — not because eBPF is entirely broken.

Kernel features you need for eBPF tracing and why they matter

Getting reliable tracing meant enabling a handful of kernel features and avoiding defaults that silently block probes.

Minimum config flags to enable

I enable these in my build config because tools like Beyla expect them:

  • CONFIG_ARCH_SUPPORTS_UPROBES=y
  • CONFIG_UPROBES=y
  • CONFIG_KPROBES=y and CONFIG_KRETPROBES=y
  • CONFIG_DEBUG_INFO_BTF=y
  • Support flags: CONFIG_HAVE_KPROBES, CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE

Uprobes vs kprobes for tracing

Uprobes attach to user-space functions. Kprobes attach to kernel functions. If you want function-level events in a user process, uprobes must be set.

Where ebpf programs run

The verifier checks safety first. That prevents bad memory access and infinite loops.

After verification, the linux kernel runs code in the interpreter or via JIT. The ebpf program executes at the hook point.

Sharing data with user space

Maps hold counters, histograms, and state. User space reads maps to export metrics or logs. No maps — no persistent event data.

CapabilityWhy it mattersConfig symbol
User-space probesAttach to app functions for latency and errorsCONFIG_UPROBES
Kernel probesTrace kernel events and stackframesCONFIG_KPROBES / CONFIG_KRETPROBES
BTF debug infoCO-RE relocations and type layoutCONFIG_DEBUG_INFO_BTF
XDPHigh-rate packet handling at device boundary — may need custom kernelCONFIG_XDP_SOCKETS (consider enabling)

Build prep on Raspberry Pi: source code, tools, and a clean baseline config

I build the kernel directly on the board to keep variables predictable and the workflow simple.

Why local? Cross-compilation adds toolchain quirks. A local build uses the native compiler and shell behavior you will boot with. Less mystery. Fewer broken assumptions.

A modern workspace featuring a Raspberry Pi setup as the focal point, placed on a clean wooden desk. In the foreground, close-up of the Raspberry Pi connected to various peripherals, with colorful LED lights illuminating the board. A laptop is open next to it, showing a terminal window with source code and configuration details displayed on the screen. In the middle ground, neatly arranged tools like a soldering iron, wires, and a multimeter. In the background, a large wall-mounted monitor displays a network diagram illustrating eBPF architecture and connections. Soft, diffused lighting creates a focused and professional atmosphere, highlighting the intricate details of the setup, inviting viewers into the world of technology and programming.

Clone the kernel source

Copy the official source with a shallow clone to save time and space:

git clone --depth=1 https://github.com/raspberrypi/linux
cd linux

Set the target and generate the default config

Export the kernel name so the build system matches /boot/firmware expectations:

export KERNEL=kernel_2712
make bcm2712_defconfig

Generating bcm2712_defconfig applies board defaults—Wi‑Fi, overlays, and device support—so you don’t miss essential settings.

Install menuconfig dependencies and open the UI

Install the one package I always forget:

sudo apt-get install libncurses5-dev

Then run make menuconfig and confirm you’re editing the generated .config, not an old file from a prior build.

  • I keep a copy of the working .config in my home directory as a fallback.
  • This way I can reapply a known-good config after updates.
  • Small, repeatable steps make the build and testing loop fast.
ActionWhyExample command
Clone sourceFast checkout; minimal filesgit clone --depth=1 ...
Set targetMatch bootloader namingexport KERNEL=kernel_2712
Generate defaultApply board defaultsmake bcm2712_defconfig

How to run eBPF on Raspberry Pi by compiling a BTF- and uprobe-ready kernel

I start by setting a clear local version so I can spot my custom build after reboot. In menuconfig go to General setup > Local version and append -v8-beyla-compatible (or your suffix).

Next: enable debug info. Open Kernel hacking > Compile-time checks and compiler options > Debug information and turn on the options that expose CONFIG_DEBUG_INFO_BTF. Save—then close and reopen menuconfig to avoid accidental resets.

Then enable uprobes support: Kernel hacking > Tracers > enable “Enable uprobes-based dynamic events”. Save again.

  • Compile targets: make Image.gz modules dtbs.
  • Install modules: sudo make modules_install — this ensures /lib/modules matches your version.
  • Backup existing boot files, then copy new files into /boot/firmware: Image.gz, the Broadcom dtbs and overlays.

Reboot. Verify with uname -r and check /lib/modules for the same version directory. Keep bcc tools installed for quick sanity checks if probes fail.

StepCommand / MenuWhy it matters
Set local versionGeneral setup > Local versionIdentifies custom kernel in logs and uname
Enable BTFKernel hacking > Debug informationAllows CO-RE loaders to read kernel types
Enable uprobesKernel hacking > TracersUser-space function tracing for Beyla
Build & Installmake Image.gz modules dtbs
sudo make modules_install
Produces bootable image with matching modules

Validate the setup with a real eBPF workload and basic tracing tests

The first step is a smoke test that proves the kernel exposes the debug types my tools expect.

Confirm BTF availability. Use bpftool to list BTF for the running kernel. If bpftool shows a BTF blob, the CO-RE loaders have the information they need to relocate types. If you need bpftool, follow the quick guide to install bpftool.

Run a small program to test hooks and maps

Pick a known-good example: a kprobe or tracepoint counter from bcc. Start the example and watch output for counts. A working program attaches, increments a map, and prints lines over time.

What success looks like: probes attach without CO-RE errors, the example prints periodic counts, and maps show live data. Failure signs: the loader errors with “no BTF found” or probes silently fail to attach.

Verify uprobes and Beyla attachment

Next, attach an uprobe to a short-lived user binary. bcc tools fail fast and will tell you if uprobe support is broken.

Finally, start Beyla and watch logs. If the earlier CO-RE relocation error is gone, the process tracer stays running and events stream without the tracer stopping.

CheckCommand / ToolSuccess indicator
BTF presentbpftool btf dumpBTF blob listed for current uname -r
Smoke test programbcc example (trace.py)Counts printed; map values increase
Uprobe attachbcc/uprobe testProbe attaches; events for user process observed
Beyla probe loadBeyla process tracerNo CO-RE relocation errors; tracer remains active

Keeping your Pi stable after the custom kernel: common pitfalls on Bookworm

Keeping a custom kernel healthy on Bookworm takes deliberate file and naming hygiene. I make small, repeatable steps to avoid surprises.

I keep the kernel name suffix aligned with Raspberry Pi conventions so boot scripts and update tooling find the right version. I copy Image.gz, dtbs, and overlays into /boot/firmware together: treat that directory as an API.

After each build I verify /lib/modules matches the running kernel version. Mismatched modules cause silent device failures—Wi‑Fi and USB are usual victims.

I check /usr/lib for extra kernel-related files, run post-install hooks, and update-initramfs when needed. I keep a fallback image and the old modules dir until the system proves stable.

Maintenance: rebase to new upstream releases, rebuild with my config, then revalidate BTF and uprobes before trusting tracing again.

FAQ

What breaks with the stock Raspberry Pi kernel and how do I confirm it?

The common failure is missing BTF debug info — tools report “no BTF found for kernel version … not supported.” Check your model, OS (Bookworm), and uname -r. Inspect /sys/kernel/btf/vmlinux or run bpftool btf dump to confirm presence. If BTF is absent, CO-RE relocations will fail and tracers won’t attach reliably.

What does “BTF” mean in practice and why do CO-RE relocations fail without it?

BTF (BPF Type Format) is compact debug metadata that describes kernel types. CO-RE uses that metadata to relocate programs across kernels. Without BTF the verifier can’t map types and offsets — relocations break and user-space tooling shows errors. You need CONFIG_DEBUG_INFO_BTF enabled in the kernel to fix this.

Which kernel features are essential for tracing (uprobes, kprobes, BTF)?

Enable uprobes, kprobes, BPF, and CONFIG_DEBUG_INFO_BTF at minimum. Also turn on CONFIG_BPF_SYSCALL and required verifier flags. Tracing depends on those options: uprobes for user-space hooks, kprobes for kernel hooks, and BTF for CO-RE compatibility.

How do uprobes differ from kprobes for user-space tracing?

Uprobes attach to user-space instruction addresses — they trace binaries and libraries. Kprobes instrument kernel functions. Uprobes require symbol resolution and are better for tracing specific application behavior; kprobes are for kernel internals. Use uprobes when you need per-process, per-binary visibility.

Where do eBPF programs execute inside the Linux kernel?

Programs run through the verifier, then either in the interpreter or JIT-compiled to native code. The verifier enforces safety; the interpreter executes bytecode if JIT is unavailable. Performance depends on JIT availability and kernel config.

How does eBPF share data with user space?

Via maps — key/value stores exposed to user space through bpf syscall and libraries. Common types: hash, array, perf event array for samples, and ring buffer for higher throughput. Maps let you push counters, histograms, and events back to the tracing app.

When is XDP relevant on this device and does it need a custom kernel?

XDP matters when you need line-rate packet processing or early-drop DDoS protection. On small ARM boards you may need a kernel with XDP and NIC driver support compiled in; some drivers lack full XDP hooks, so a custom kernel helps.

What’s the recommended prep before building a kernel for this board?

Clone the Raspberry Pi Linux source that matches your hardware. Install build tools and dependencies — gcc, make, bc, flex, bison, and libncurses5-dev for menuconfig. Start from a known baseline config to avoid stray options.

How do I set the correct kernel target and get bcm2712_defconfig?

Check the vendor documentation for the Pi 5 kernel target. In the source tree run make bcm2712_defconfig (or the vendor-provided defconfig) to create a baseline. That ensures device tree and platform options match the board.

Which packages are required for menuconfig and kernel configuration?

Install libncurses5-dev (or libncurses-dev), make, gcc, bc, and libelf-dev for build helpers. Then run make menuconfig to tweak tracing, debug info, and BTF-related options.

How do I add a local version string to identify a custom kernel after reboot?

In menuconfig set “Local version” under General setup or edit EXTRAVERSION in the top-level Makefile. A short suffix helps verify uname -r matches your build after boot.

Which config flags enable CONFIG_DEBUG_INFO_BTF and debug info generation?

Enable CONFIG_DEBUG_INFO, CONFIG_DEBUG_INFO_BTF, CONFIG_BPF, and CONFIG_BPF_SYSCALL. Also enable CONFIG_DEBUG_INFO_REDUCED and build-id support if your toolchain needs it. Confirm via scripts/config or menuconfig.

Where is the “uprobes-based dynamic events” option located?

It lives under Kernel hacking > Tracers in menuconfig. Enable any options labeled uprobes or dynamic events to allow user-space probe registration and improve compatibility with tracing tools.

What’s the right sequence to compile kernel, modules, and DTBs?

Build in this order: make -jN Image.gz (or Image), modules, and dtbs. Then run make modules_install and copy Image*, dtbs, and overlays into /boot/firmware. This ensures modules match the kernel image.

How do I install modules and kernel files to the boot partition?

After modules_install, copy /lib/modules/ to the target root. Then copy the kernel Image or Image.gz plus dtbs and overlays to /boot/firmware. Update config.txt if you use a custom suffix and reboot.

How can I confirm the new kernel is running after reboot?

Use uname -r to check the version and local suffix. Verify /sys/kernel/btf/vmlinux exists and compare kernel build timestamps. If your local version string is present, you booted the right image.

How do I confirm BTF availability for the running kernel?

Check /sys/kernel/btf/vmlinux or run bpftool btf dump file /sys/kernel/btf/vmlinux. If the file exists and bpftool can dump types, BTF is available and tools can perform CO-RE relocations.

What simple eBPF workload should I run to validate tracing end-to-end?

Run a minimal userspace tracer that attaches an uprobe to a known binary path and writes events to a perf buffer or ring buffer. Confirm the program attaches, events appear, and no CO-RE errors show in stderr.

How do I check that Beyla (or similar tools) can attach probes without CO-RE errors?

Start the tool with verbose logging. If it reports successful attach and no “no BTF” or relocation failures, you’re good. Use bpftool prog show and map list to confirm loaded programs and maps.

What common pitfalls affect stability after installing a custom kernel on Bookworm?

Watch for mismatched modules, missing firmware, and package-managed headers that expect stock kernels. Keep kernel package updates in mind — apt may install a different kernel. Keep a backup boot entry so you can revert quickly.

What resources and tools should I keep handy while doing this work?

Keep bpftool, libbpf, clang/llvm for building programs, and bcc for quick scripts. Use the kernel build logs, upstream docs, and vendor README. Those save time when troubleshooting verifier messages or JIT issues.