Why the Old Approaches Were Insufficient
Before eBPF, adding new observability to the Linux kernel was genuinely hard. You had two options:
- Write a Kernel Module: Compiled C code that runs directly in the kernel. Powerful, but a bug in your module can crash the entire system with a kernel panic. Deploying kernel modules requires rebooting or at minimum careful loading procedures, and they must be recompiled for every kernel version.
- Upstream a Kernel Patch: Submit your instrumentation code to the Linux kernel project for inclusion. This process takes months to years, requires convincing maintainers, and your feature won't be available to users until they upgrade their kernel — which might be years later.
Neither option is viable for a team that needs to answer "why is this service slow?" today, in production.
eBPF: A Programmable Kernel
eBPF (Extended Berkeley Packet Filter) is a technology built into the Linux kernel (since kernel 3.18, with major features added through 4.x and 5.x) that allows you to run sandboxed programs inside the kernel without modifying kernel source code or loading kernel modules.
Think of it as a tiny, secure virtual machine embedded in the kernel. You write a small program in a restricted C-like language, compile it to eBPF bytecode, and load it into the running kernel. The kernel then runs your program whenever a specific event occurs — a function call, a network packet arriving, a system call being made, a hardware performance counter firing.
The analogy: imagine you could safely inject custom sensors into a running jet engine — measuring temperature at a specific valve, counting how many times a particular gear rotates per second — without shutting the engine down, and with a guarantee that your sensor code cannot physically damage the engine. That's eBPF.
What eBPF Can Hook Into
eBPF programs can attach to a wide variety of kernel events:
| Hook Type | What It Observes | Example Use |
|---|---|---|
| kprobe/kretprobe | Entry/exit of any kernel function | Trace when tcp_connect() is called |
| tracepoint | Stable, versioned kernel event markers | Syscall entry/exit, scheduler events |
| uprobe | Entry/exit of user-space functions | Trace a specific Go or Python function |
| XDP (eXpress Data Path) | Network packets before the kernel stack | High-performance packet filtering/routing |
| perf_event | Hardware performance counters | CPU cache misses, branch mispredictions |