Skip to main content
← All Articles Β· Β· 11 min read

eBPF: Revolutionizing Linux Observability and Networking in 2026

eBPF in 2026: run sandboxed kernel programs for networking, security, and observability. Learn how Cilium, Falco, and Pixie use eBPF with code examples.

ebpflinuxnetworkingobservabilitycilium

If you have used Cilium for Kubernetes networking, Falco for runtime security, or Pixie for zero-instrumentation observability, you have already used eBPF. You just may not have known it. In 2026, eBPF is the substrate on which the next generation of Linux infrastructure tooling is built β€” and understanding it makes you a significantly more capable systems engineer.

What Is eBPF?

eBPF (extended Berkeley Packet Filter) is a technology that lets you run sandboxed programs inside the Linux kernel without modifying kernel source code or loading kernel modules. Originally designed for packet filtering (hence the name), it has evolved into a general-purpose kernel programmability framework.

The key insight: the Linux kernel is a massive, privileged runtime that sits between all hardware and all software. By running code at that layer, you can observe and influence virtually everything β€” network packets, system calls, file system operations, CPU scheduling β€” with near-zero overhead and without touching application code.

Why eBPF Matters for Developers

Before eBPF, you had three options for deep system instrumentation:

  1. Kernel modules β€” powerful but dangerous, can crash the kernel, require recompilation
  2. ptrace/strace β€” safe but catastrophically slow (2-100x overhead)
  3. User-space agents β€” limited visibility, can miss kernel-level events

eBPF gives you kernel-level visibility with safety guarantees. An eBPF verifier checks every program before it runs, ensuring it cannot crash the kernel, loop infinitely, or access arbitrary memory. The JIT compiler then compiles the verified bytecode to native machine code β€” performance is within single-digit percent of native kernel code.

How eBPF Works: The Internals

Understanding the execution model makes you a better user of eBPF-based tools and helps you debug when things go wrong.

The eBPF Pipeline

Developer writes eBPF program (C or Rust)
        ↓
Compiled to eBPF bytecode (LLVM/clang)
        ↓
Loaded into kernel via bpf() syscall
        ↓
eBPF Verifier (safety checks)
        ↓
JIT Compiler (bytecode β†’ native machine code)
        ↓
Attached to hook point (tracepoint, kprobe, XDP, etc.)
        ↓
Executes on event, writes to eBPF maps
        ↓
User-space reads maps via file descriptors

Hook Points

eBPF programs attach to kernel hook points. The most important categories:

Hook TypeTriggerUse Cases
kprobe/kretprobeAny kernel function entry/exitPerformance tracing, debugging
tracepointStable kernel trace eventsSyscall monitoring, scheduler events
XDP (eXpress Data Path)Packet arrival (before sk_buff)DDoS mitigation, load balancing
TC (Traffic Control)Ingress/egress on network interfacePolicy enforcement, packet mangling
LSM (Linux Security Module)Security decision pointsZero-trust enforcement
uprobe/uretprobeUser-space function entry/exitLanguage runtime tracing

eBPF Maps

Maps are the communication channel between eBPF programs and user-space. They are key-value stores that persist across program invocations:

// BPF map definition (kernel side)
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 10240);
    __type(key, u32);   // PID
    __type(value, u64); // syscall count
} syscall_count SEC(".maps");

// Kernel-side update
SEC("tracepoint/syscalls/sys_enter_write")
int trace_write(struct trace_event_raw_sys_enter *ctx) {
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    u64 *count = bpf_map_lookup_elem(&syscall_count, &pid);
    if (count) {
        (*count)++;
    } else {
        u64 initial = 1;
        bpf_map_update_elem(&syscall_count, &pid, &initial, BPF_ANY);
    }
    return 0;
}

bpftrace: eBPF for Humans

bpftrace is a high-level tracing language built on eBPF. It is the awk of kernel tracing β€” a one-liner tool for production diagnostics.

bpftrace One-Liners

Count syscalls by process name:

bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'

Trace all open() calls with filename and latency:

bpftrace -e '
tracepoint:syscalls:sys_enter_openat {
    @start[tid] = nsecs;
    @fname[tid] = str(args->filename);
}
tracepoint:syscalls:sys_exit_openat /@start[tid]/ {
    $lat = nsecs - @start[tid];
    printf("%-16s %-6d %6d us %s\n", comm, pid, $lat/1000, @fname[tid]);
    delete(@start[tid]);
    delete(@fname[tid]);
}'

Histogram of read() sizes:

bpftrace -e 'tracepoint:syscalls:sys_exit_read { @bytes = hist(args->ret); }'

Track TCP connections by destination IP:

bpftrace -e 'kprobe:tcp_connect {
    $sk = (struct sock *)arg0;
    printf("%-16s β†’ %s:%d\n", comm,
        ntop(AF_INET, $sk->__sk_common.skc_daddr),
        $sk->__sk_common.skc_dport);
}'

These run on a live production kernel. Zero application changes required.

Writing a Full eBPF Program with libbpf

For production tooling, you write eBPF programs in C and load them from a Go or Rust user-space process using libbpf or cilium/ebpf:

// http_trace.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

struct event {
    u32 pid;
    char comm[16];
    char data[256];
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} events SEC(".maps");

SEC("uprobe//usr/lib/libssl.so:SSL_write")
int trace_ssl_write(struct pt_regs *ctx) {
    struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e) return 0;

    e->pid = bpf_get_current_pid_tgid() >> 32;
    bpf_get_current_comm(&e->comm, sizeof(e->comm));

    void *buf = (void *)PT_REGS_PARM2(ctx);
    bpf_probe_read_user(&e->data, sizeof(e->data), buf);

    bpf_ringbuf_submit(e, 0);
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

This hooks into OpenSSL’s SSL_write β€” enabling you to capture HTTPS traffic in plaintext without a proxy, without TLS interception, and without any application changes. This is how tools like Pixie achieve zero-instrumentation tracing.

Cilium: eBPF-Native Kubernetes Networking

Cilium replaces kube-proxy and iptables with an eBPF-native networking layer. It is the CNI plugin of choice for performance-sensitive Kubernetes deployments in 2026.

Why Replace kube-proxy?

kube-proxy implements Kubernetes Service load balancing via iptables rules. At scale, iptables has a fundamental problem: rule evaluation is O(n). With 10,000 services, every packet traverses thousands of rules. This adds measurable latency and CPU overhead.

Cilium replaces iptables with eBPF hash maps. Service lookup becomes O(1). The performance difference becomes significant above ~1,000 services.

Installing Cilium

# Install with Helm
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.16.0 \
  --namespace kube-system \
  --set kubeProxyReplacement=strict \
  --set k8sServiceHost=<API_SERVER_IP> \
  --set k8sServicePort=6443

# Verify installation
cilium status --wait
cilium connectivity test

Cilium Network Policies

Cilium extends Kubernetes NetworkPolicy with identity-aware, layer 7-aware policies:

# Allow GET /api/v1/users from frontend only
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-policy
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: /api/v1/users

This is Layer 7 policy enforcement at kernel speed β€” no sidecar proxy required.

Hubble: Network Observability

Hubble is Cilium’s observability layer. It gives you real-time network flow visibility:

# Install Hubble CLI
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all \
  https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz
tar xzvf hubble-linux-amd64.tar.gz
sudo mv hubble /usr/local/bin/

# Enable Hubble in Cilium
helm upgrade cilium cilium/cilium \
  --namespace kube-system \
  --reuse-values \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true

# Watch live flows
hubble observe --follow --namespace production

The output shows every network connection between pods β€” source, destination, protocol, verdict (allowed/dropped) β€” with microsecond timestamps. No tcpdump. No packet capture. Zero overhead on the application.

Falco: Runtime Security with eBPF

Falco is a runtime threat detection engine. It uses eBPF to watch system calls and generates alerts when behavior deviates from policy.

# falco-rules.yaml β€” detect shell spawned inside container
- rule: Terminal Shell in Container
  desc: A shell was spawned inside a container
  condition: >
    spawned_process and container
    and shell_procs and proc.tty != 0
    and container_entrypoint
  output: >
    A shell was spawned in a container with an attached terminal
    (user=%user.name %container.info shell=%proc.name
     parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)
  priority: NOTICE
  tags: [container, shell, mitre_execution]
# Detect outbound connection from pod to unexpected IP
- rule: Unexpected Outbound Connection
  desc: Pod established outbound connection to non-allowlisted IP
  condition: >
    outbound and not allowed_ips contains fd.rip
    and container
  output: >
    Unexpected outbound connection (pod=%k8s.pod.name ip=%fd.rip port=%fd.rport)
  priority: WARNING

Falco consumes ~2% CPU in production environments. A comparable user-space agent with ptrace would impose 20-100x that overhead.

Pixie: Zero-Instrumentation Observability

Pixie auto-instruments your Kubernetes applications using eBPF uprobes. You get HTTP/gRPC traces, database query latencies, and profiling data with zero code changes.

# Deploy Pixie
curl -fsSL https://withpixie.ai/install.sh | bash
px deploy

Within minutes, you can query your cluster:

# PxL script β€” HTTP latency by endpoint
import px

df = px.DataFrame(table='http_events', start_time='-5m')
df.latency_ms = df.resp_latency_ns / 1.0E6
df = df.groupby(['req_path']).agg(
    latency_p50=('latency_ms', px.quantiles(50)),
    latency_p99=('latency_ms', px.quantiles(99)),
    count=('latency_ms', px.count)
)
px.display(df, 'HTTP Latency by Endpoint')

No OpenTelemetry instrumentation. No service mesh. No sidecars. The eBPF probes on SSL_read/SSL_write, send, recv reconstruct HTTP requests and responses at the kernel boundary.

eBPF Performance Characteristics

Real-world benchmarks for eBPF-based tools vs. alternatives:

ScenarioiptableseBPF (Cilium)Improvement
Service lookup (10k services)~470ΞΌs~15ΞΌs31x faster
Network policy enforcementO(n) rulesO(1) hash lookupLinear vs constant
Packet processing (XDP)~1.5M pps~24M pps16x faster
System call tracing overhead~100% (strace)~1-3% (eBPF)33-100x less overhead

Getting Started with eBPF Development

Prerequisites

# Ubuntu 22.04+
sudo apt install -y \
  clang llvm libelf-dev libbpf-dev \
  linux-headers-$(uname -r) \
  bpftrace bpftools

# Verify BPF support
uname -r  # should be 5.15+
ls /sys/kernel/btf/vmlinux  # BTF required for CO-RE

Minimal eBPF Hello World

// hello.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_execve")
int handle_execve(void *ctx) {
    bpf_printk("Hello from eBPF! PID: %d\n",
               bpf_get_current_pid_tgid() >> 32);
    return 0;
}

char LICENSE[] SEC("license") = "GPL";
clang -O2 -g -target bpf -c hello.bpf.c -o hello.bpf.o
sudo bpftool prog load hello.bpf.o /sys/fs/bpf/hello
sudo bpftool prog attach ... # attach to tracepoint
sudo cat /sys/kernel/debug/tracing/trace_pipe

For production, use libbpf-bootstrap as your starting template β€” it handles BTF, CO-RE (Compile Once, Run Everywhere), and skeleton generation.

When to Use eBPF (and When Not To)

Use eBPF when:

  • You need zero-instrumentation observability (Pixie, Falco)
  • You need kernel-level performance for networking (Cilium XDP)
  • You need security enforcement at the kernel boundary (LSM hooks)
  • Application code changes are not feasible or desirable
  • You need sub-millisecond event latency

Do not use eBPF when:

  • You just need application-level metrics (use OpenTelemetry)
  • You are running on kernels older than 5.4 (limited CO-RE support)
  • Your team has no familiarity with kernel internals (ops overhead is real)
  • You need Windows support (eBPF for Windows exists but is immature)

Conclusion

eBPF is the most significant change to Linux infrastructure in a decade. It enables a class of tools β€” Cilium, Falco, Pixie, Katran, Tetragon β€” that were previously impossible without invasive application changes or unacceptable performance overhead.

For developers, the practical takeaway is: use eBPF-based tools by default for Kubernetes networking and observability. Cilium over kube-proxy. Pixie or Grafana Beyla for tracing before you reach for OpenTelemetry. Falco for runtime security.

For those who want to go deeper, bpftrace is your gateway drug. The path from one-liners to production eBPF programs is shorter than it looks β€” and the visibility you gain at the end of that path is unlike anything else in the Linux ecosystem.

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.