What Is eBPF-Based Instrumentation in OpenTelemetry?

What Is eBPF-Based Instrumentation in OpenTelemetry?

A plain-language guide to collecting traces, metrics and profiles by watching programs from inside the operating system, with no code changes, no restarts and no language-specific agents.

Picture a company with three hundred services. Some are written in Go, some in Java, some in Python, and a few in C++ that nobody has touched for years. A handful are programs bought from vendors, delivered as a single file with no source code at all. Leadership wants to see how every request moves through every service, and wants it by next quarter. The traditional answer is to add monitoring code to each program, one language at a time. For this company, that answer means months of work, and for the vendor programs it means no answer at all.

eBPF offers a different route. It is a technology built into the Linux operating system that lets small, safe programs watch what other programs are doing, from the outside, as it happens. OpenTelemetry projects use that ability to produce standard telemetry without asking the observed programs to change. The result is a way to get useful traces, metrics and performance profiles for software you cannot or do not want to modify.

This guide explains eBPF from scratch, shows how OpenTelemetry uses it, and is honest about its limits. You do not need to know anything about operating system internals. Every new term is explained when it first appears, and each big idea comes with an everyday comparison.

AThe Problem: Watching Code You Cannot or Will Not Change

Good observability starts with data, and data starts with instrumentation. The hardest part is often getting that instrumentation into place.

What Instrumentation Means

Instrumentation is the act of making a program report on itself. A program that is instrumented creates traces, which follow a single request across many services; metrics, which are numbers measured over time such as requests per second; and logs, which are timestamped records of events. Together these are called telemetry. OpenTelemetry is the open-source project that defines standard ways to create, name and send it.

The usual way to instrument is to add a library to your code and call it at the interesting moments. That works well when you own the code, know the language, and have time. In practice, one or more of those conditions often fails.

Three Common Obstacles

Access

No source code

Vendor products, old binaries and third-party tools arrive without code you can change. You can run them, but you cannot edit them.

Diversity

Many languages

Each language needs its own library, its own setup and its own expertise. A fleet of six languages means six projects.

Time

No one has capacity

Teams with product deadlines rarely volunteer to add monitoring code to stable services that are not currently on fire.

Risk

Fear of change

Touching a critical service just to add telemetry can feel riskier than living without it, especially for old systems.

Everyday analogy

Imagine an office building with fifty tenants. One way to track who comes and goes is to ask every tenant to keep a visitor log at their own door. That gives rich detail, but it needs fifty tenants to cooperate. Another way is to let the building’s front desk record everyone who walks through the lobby and take the elevator. The desk never sees what happens inside a room, but it needs no cooperation from anybody. eBPF-based instrumentation works like the front desk.

What Teams Want From a Solution

  • Fast coverage. See something useful for every service within days, not quarters.
  • No code changes. Do not edit, rebuild or redeploy the programs being watched.
  • Language independence. One approach that works the same way for any language.
  • Standard output. Data in OpenTelemetry’s standard format, so that existing tools and dashboards keep working.
  • Low overhead. Little extra load on already busy production machines.
0
lines of application code changed
1
agent per machine, not per language
Days
to first useful coverage, not months
i
Where this guide is going

eBPF fits the front-desk role well, and it is most valuable when combined with in-code instrumentation for the details only the code can know. Later chapters explain exactly where each approach shines.

BThree Ways to Collect Telemetry

Before looking at eBPF, it helps to see where it sits among the other options.

Option One: Manual Instrumentation With an SDK

An SDK, short for software development kit, is the OpenTelemetry library that lives inside your program. With manual instrumentation, developers call it directly: start a span here, record a metric there, attach the customer tier as an attribute. It gives the richest, most meaningful data, because the people who wrote the code decide exactly what matters. It also needs the most effort and must be repeated for every language.

Option Two: In-Process Auto-Instrumentation

Many languages let an agent attach to a running program and add instrumentation automatically to popular libraries. The Java agent, which hooks into the Java runtime, is the best-known example, and similar tools exist for Python, Node.js and .NET. Developers change no code, but they must add the agent to every service, and each language has its own agent with its own quirks. The agent lives inside the program’s process and shares its memory and CPU.

Option Three: eBPF-Based Instrumentation

With eBPF, a separate program runs next to the application, often one per machine, and installs tiny probes in the operating system’s kernel. These probes watch system events, such as a network read, and the moments when certain functions in the application are called. The separate program turns those observations into standard OpenTelemetry spans and metrics. The application is unaware and unchanged.

ApproachWhere it runsCode changesDepth of detailLanguage dependence
Manual SDKInside the programYes, throughoutDeepest; business meaningOne library per language
In-process agentInside the programNone, but each service is configuredDeep for supported librariesOne agent per language
eBPF-basedOutside the program, in the kernel and a helperNoneRequest-level; protocol and function viewMostly independent
Everyday analogy

Think of three ways to know what happens in a restaurant. A manual SDK is the chef narrating each step of every dish. An in-process agent is a camera mounted in the kitchen that understands recipes in one cuisine. eBPF is a sensor at the entrance and at the pass-through window, counting orders, timing how long dishes take and noting which table receives them, without knowing a thing about cooking.

!
Common misunderstanding

eBPF is not “better” than SDKs, and it does not replace them. It trades depth for reach. It excels at breadth, but it cannot learn that a request belongs to a premium customer unless that fact appears somewhere it can observe.

CWhat Is eBPF?

The name sounds technical, but the idea is simple: run small, checked programs inside the operating system without modifying it.

A Short History

The story starts in 1992, when researchers designed the BSD Packet Filter, known as BPF. Its job was narrow: let a tool like tcpdump tell the kernel which network packets it wanted, so that the kernel could discard the rest early and cheaply. BPF was a tiny virtual machine inside the kernel, and its design proved sound for decades.

Around 2014, engineers extended it dramatically for Linux. The new version, extended BPF or eBPF, supports far more than packet filtering. Programs can be attached to many kinds of events, can remember information between events, and can pass data to ordinary programs. Despite the name, eBPF today is a general-purpose way to safely extend the kernel, and the letters are treated as a name, not an abbreviation.

What the Kernel Is, and Why Programs Want to Watch It

The kernel is the core of the operating system. It manages memory, schedules which program runs, talks to disks and network cards, and mediates every request an application makes of the machine. Every file opened, every network connection made and every byte sent passes through it. That makes the kernel a perfect observation post: if you can watch it, you can see what every program on the machine is doing.

Historically, extending the kernel meant either changing its source code, a slow process involving the whole operating system community, or loading a kernel module, a plug-in with full power that can crash the entire machine if it has a bug. eBPF offers a third path.

Everyday analogy

A phone’s app store is a good picture. Apps can do useful things with the phone’s sensors and settings, but only after a review checks that they will not crash the phone or steal data. eBPF programs are like reviewed apps for the kernel: they get access to events in the system, but only after an automatic checker proves they are safe.

Safety Through the Verifier

The key to eBPF is the verifier, a checker built into the kernel. Before any eBPF program is allowed to run, the verifier analyzes every possible path through it. It rejects programs that could loop forever, read memory they should not, or crash the system. A program that passes is compiled into native machine code by a just-in-time compiler, so it runs at close to full speed.

The price of this safety is restriction. eBPF programs must be small and must finish quickly. They cannot wait for a disk or sleep. They cannot call arbitrary kernel functions, only a limited set of approved helpers. That limited power is exactly what makes them safe to run in production.

1992
year the original packet filter was designed
~2014
when extended BPF arrived in Linux
0
kernel rebuilds needed to add a program

Where eBPF Is Used Today

Observability is only one use. eBPF powers networking, where projects such as Cilium route and filter traffic inside the kernel, and large-scale load balancers and attack filters process packets at very high speed. It powers security tools that watch for suspicious system calls. Performance engineers have used it for years to answer questions like “why is this machine slow?” without installing heavy tools. OpenTelemetry’s use of it is a natural extension of that story.

DHow an eBPF Program Runs, Step by Step

Following one probe from creation to data delivery explains most of the vocabulary you will meet.

flowchart TD
  A["Developer writes a small program
in restricted C or Rust"] --> B["Compiler produces eBPF bytecode"] B --> C["User-space loader asks the kernel
to load the program"] C --> D{"Verifier checks safety"} D -->|"Rejected"| X["Load fails with an error"] D -->|"Accepted"| E["Compiled to native code"] E --> F["Attached to a hook
for example a function call or a network read"] F --> G["Event happens and the program runs"] G --> H["Results written to a map or ring buffer"] H --> I["User-space agent reads the data"]
Figure 1 — The life of an eBPF probe, from source code to data in a user-space agent.

The Steps in Plain Words

1

Write and compile

The program is written in a restricted style of a systems language and compiled into a compact instruction set that the kernel understands.

2

Load

An ordinary program running in user space, the normal area where applications live, asks the kernel to accept the new code. This requires special permissions.

3

Verify

The kernel’s verifier proves the code cannot misbehave. Failure here is common during development and gives detailed error messages.

4

Attach

The program is connected to a hook, a specific kind of event, such as “a function in this application was called”.

5

Run on events

Each time the event happens, the kernel runs the program for a few microseconds. It reads a few values, perhaps the arguments of the call and the current time.

6

Share the result

The program stores what it saw in a shared structure, and a user-space agent collects it.

Maps and Ring Buffers

eBPF programs cannot hold information in ordinary variables between runs, because each run is a fresh, short event. Instead they use maps, shared tables managed by the kernel. A program can record “request 42 started at this time” in a map when a call begins, and look it up when the call ends to compute the duration. Maps are also how user-space and kernel programs exchange settings, such as which processes to watch.

To send a stream of observations out, programs write to a ring buffer, a fixed-size circular area that the agent reads. If the agent falls behind and the buffer fills, new events are dropped. That is a design trade-off to remember: the kernel side never waits, so a slow reader loses data instead of slowing the system.

Compile Once, Run Everywhere

Kernels differ in the exact layout of their internal data. A program written for one kernel version may look in the wrong place on another. The solution, called CO-RE for compile once, run everywhere, relies on a description of the kernel’s own data layout, called BTF. At load time the loader adjusts the program to match the machine it runs on. This is why modern eBPF agents can ship one build for many kernels, and why they usually require a kernel that exposes BTF.

i
Tip

When a team says “our agent does not start on that host”, the first two questions are whether the kernel is new enough and whether it exposes BTF. Those two answers explain most compatibility issues before you read a single log.

EHooks: Where eBPF Listens

An eBPF program does nothing until it is attached to an event. The choice of hook decides what the instrumentation can see.

The Main Hook Types

HookWhat it watchesTypical observability useStability
Kernel probeEntry or exit of a function inside the kernelNetwork reads and writes, connection setupTied to kernel function names, which can change
TracepointA deliberately placed, documented event in the kernelProcess start, scheduling, system callsMost stable, maintained as an interface
User-space probeEntry or exit of a function inside an application or libraryRequest handlers, TLS libraries, language runtime functionsTied to the application’s symbols and version
Static user probeMarkers placed by a library’s authorsDatabase or runtime events where availableStable when provided
Performance eventA timer or hardware counter firing at a regular rateSampling profilersStable
Network hooksPackets or sockets as they pass throughFlow metrics, header handlingStable, with kernel-version differences
Everyday analogy

Think of sensors in a factory. Tripwires on doors fire whenever something passes, like kernel and user-space probes. A security guard making rounds every ten minutes is like a sampling profiler, taking a snapshot at a steady rhythm. A toll booth on the road checks every vehicle’s size and speed, like network hooks. Each sensor suits a different question.

User-Space Probes and Their Fragility

User-space probes are the tool that lets eBPF understand application behaviour. A probe can be attached to a specific function in a program, so that each time the function runs, the eBPF code can read its arguments. To place such a probe, the agent must know where the function lives in the program’s file, usually by reading its symbol table, the list of function names and addresses.

This creates dependencies. If a binary is stripped of symbols to save space, probes cannot find their targets. If a new version of a library renames or reorganizes a function, probes written for the old layout may fail or read the wrong data. The instrumentation must be updated whenever supported libraries change enough to move things around. This is the main reason that probes into application code need constant maintenance, while probes on stable kernel events change rarely.

Kernel Probes and Stable Surfaces

Probes at the kernel level, such as those watching network reads and writes, are more portable across applications because every program uses the same system interfaces. A web server in any language eventually calls the kernel to read bytes from a connection. That common surface is what lets a single agent observe programs written in many languages. The agent then examines the bytes to recognize protocols such as HTTP, which is the topic of later chapters.

!
Common trap

It is tempting to believe that “eBPF sees everything”. It sees what passes through the hooks it attached to. If the information never touches a hook, or touches it in a form the agent cannot interpret, it is invisible.

FThe Moving Parts of an eBPF-Based Agent

An eBPF instrumentation tool is not just a few kernel probes. It is a small system with a kernel half and a user-space half, each doing the jobs it is best at.

flowchart LR
  subgraph Node["One machine"]
    APP["Applications
any language"] --> K["Kernel with eBPF probes"] K --> RB["Ring buffers and maps"] RB --> AG["User-space agent
builds spans and metrics"] end AG -->|"OTLP"| COL["Collector"] COL --> BE["Backend"]
Figure 2 — Probes in the kernel feed a user-space agent on the same machine, which exports standard telemetry onward.

The Kernel Half

The kernel half is a set of small eBPF programs attached to hooks. Their job is deliberately narrow: notice that something happened, capture a few facts, and hand them over quickly. They record things like the time, the process that triggered the event, the connection involved and the first bytes of a message. They do not try to understand what they see, because they are limited in size and cannot do heavy work.

The User-Space Half

The user-space half is an ordinary program, often written in Go or Rust, running as a normal process. It loads and attaches the probes, reads the ring buffers, and does the heavy thinking that the kernel side may not do. It parses protocol bytes into requests and responses, pairs them up to compute durations, builds OpenTelemetry spans and metrics with standard names, enriches them with information such as the Kubernetes pod name, and exports them using OTLP, the OpenTelemetry Protocol, usually to a Collector.

PartJobRuns where
ProbesNotice events and capture raw factsInside the kernel
Maps and ring buffersHold state and carry events outwardKernel memory shared with user space
Loader and discoveryAttach probes to the right processes as they start and stopAgent
ParsersTurn raw bytes into requests, responses and operationsAgent
Span and metric builderCreate standard telemetry from the parsed eventsAgent
Metadata enricherAdd service, pod, namespace and node detailsAgent
ExporterSend data through OTLPAgent
Everyday analogy

Consider a news agency with field reporters and an editorial desk. Reporters at the scene send short, quick notes: who, where, when. They have no time to write polished stories. The desk receives the notes, checks facts, adds background and publishes a finished article. The kernel probes are the reporters, and the user-space agent is the desk.

Why Split the Work This Way

Doing as little as possible inside the kernel keeps programs inside the verifier’s limits and keeps the risk low. If the user-space half crashes, the application keeps running, because it never depended on the agent. If a parser has a bug, it affects the agent’s output, not the stability of the machine. And updating parsing logic is as easy as updating an ordinary program, with no kernel changes required.

i
Tip

When evaluating any eBPF-based tool, ask which tasks happen in the kernel and which in user space. A design that keeps the kernel side small is easier to trust, easier to port between kernels and easier to reason about under load.

GOpenTelemetry’s eBPF Projects

Several projects bring eBPF into the OpenTelemetry family. They differ in what they watch and in how mature they are.

Go Automatic Instrumentation

The OpenTelemetry Go automatic instrumentation project targets programs written in Go. Go programs are compiled into a single native file with no runtime agent mechanism like Java’s, so a classic in-process agent is not possible. The project attaches user-space probes to well-known functions in popular Go libraries, such as the standard web server and client, database access, and common messaging and RPC libraries. When those functions run, the probes read their arguments and produce spans. It usually targets a single process identified by the path of its executable, and it can be deployed in Kubernetes through the OpenTelemetry Operator or as a companion container.

OpenTelemetry eBPF Instrumentation

The second effort is a language-independent tool. It began as Beyla, a project from Grafana Labs, and as far as is publicly known it was contributed to the OpenTelemetry project and continues there as OpenTelemetry eBPF Instrumentation, often abbreviated OBI. It watches kernel-level activity to recognize web, RPC and database traffic from programs in many languages and produces spans and request metrics. It can discover which processes to watch using listening ports, executable names or Kubernetes labels, and it understands Kubernetes metadata. Because the project is evolving quickly, check its current documentation for supported protocols and kernel requirements.

The eBPF Profiler

The third project is a continuous profiler, contributed to OpenTelemetry from a commercial profiling agent. Instead of watching requests, it samples what code is running on each CPU many times per second across the whole machine and builds a picture of where time is spent. It is notable for walking the call stacks of native code and several language runtimes, directly from eBPF, without needing special compile options in the programs. Profiles are a newer OpenTelemetry signal that is still being defined, so expect changes.

Network Observability and the Wider Ecosystem

Other work focuses on network observability: watching connections, bytes and retransmissions between workloads to build maps of who talks to whom. Beyond OpenTelemetry’s own repositories, an ecosystem of tools uses eBPF and can export OTLP. Examples include Kubernetes-focused observability platforms and distributions that mix eBPF for some languages with in-process agents for others, plus offerings from commercial vendors. Each has its own strengths, and their behaviour does not always match the OpenTelemetry projects exactly.

ProjectWhat it observesMain signalsThings to check
Go automatic instrumentationGo libraries inside chosen Go processesTracesSupported Go versions and libraries; one process per instance
eBPF instrumentation (OBI)Protocol traffic from many languagesTraces, request metricsProtocol support; kernel features; context propagation behaviour
eBPF profilerCPU activity of the whole machineProfilesRuntime support; the profiles signal’s stability
Network-focused toolsConnections and flowsMetricsProject activity and cardinality of flow data
!
Check before you commit

Names, ownership and maturity of these projects have changed over time and are still changing. Before choosing one, read its current repository notes for stability level, supported kernels, supported protocols and known limitations, instead of relying on any summary, including this one.

HHow Telemetry Appears Without Touching Code

The magic has a mechanism. Understanding it explains both the strengths and the limits of the approach.

Recognizing Protocols From Bytes

When a program receives an HTTP request, the bytes arrive through the kernel. A probe on the kernel function that delivers those bytes can see the first part of each message. The user-space agent examines it. HTTP requests begin with a recognizable pattern: a method, a path and a version. The agent can tell that a request arrived, which method and path it used, and, when the program writes its reply, which status code it sent. The time between the two is the request’s duration. This works for any language, because any web server must read and write through the kernel.

The same idea extends to other protocols: HTTP/2 and gRPC, several database protocols, caches and message brokers. Each needs a parser that understands that protocol’s message layout, so supported protocols are a key feature to check in any tool.

Everyday analogy

A court reporter in a courtroom records who spoke, when, and for how long, and can even note the words said. The reporter does not know what the lawyers are thinking, why the case matters or how the judge will rule. The reporter sees the public surface of the proceedings, which is enough to produce a precise timeline.

Seeing Inside Encrypted Traffic

Most production traffic is encrypted. At the kernel level, bytes on the connection are scrambled and cannot be read. The workaround is to attach user-space probes to the functions in the encryption library, such as the widely used OpenSSL or the Go standard library’s encryption, at the point where plain data is handed over for encryption or has just been decrypted. This yields readable requests without breaking encryption on the wire. It depends on the application using a library the agent knows how to hook, which is one more compatibility question to ask.

Hooking Language-Level Functions

For some languages, especially Go, user-space probes can be attached directly to functions in common libraries. The probe reads the function’s arguments and sees structured information, such as the parsed request object, rather than raw bytes. That gives a more exact view, at the cost of depending on library versions and internal data layouts.

From Events to Standard Telemetry

1

Pair start and end

A request seen at time A and a response seen at time B become one operation with a duration.

2

Build a span

The operation becomes a span, a server span for a request arriving or a client span for a request leaving, with the method, status and address as standard attributes.

3

Derive metrics

Counting operations by status gives request rate and error rate. Recording durations in a histogram gives latency distributions. Together these are known as the RED metrics: rate, errors and duration.

4

Name things properly

Attributes follow OpenTelemetry’s semantic conventions, the standard dictionary of names, so dashboards built for SDK data work with eBPF data too.

i
Tip

Check which edition of the semantic conventions your eBPF agent follows. If your SDK-instrumented services use a different edition, dashboards that filter on attribute names will show only part of the fleet until you reconcile them.

IFollowing One Request Through an eBPF Agent

A single request, traced step by step, shows exactly where the kernel probes and the agent do their work.

sequenceDiagram
  participant C as Caller
  participant K as Kernel probes
  participant A as Application
  participant G as eBPF agent
  participant O as Collector
  C->>A: HTTP request arrives
  K->>K: Note read event and start time
  A->>A: Handles the request
  A->>C: Response is written
  K->>G: Event with timing and first bytes
  G->>G: Parse request and build span
  G->>O: Send span and metrics over OTLP
    
Figure 3 — The application never calls the agent. The probes and the agent observe from the side.

The Story in Eight Steps

1

A request arrives

A caller sends an HTTP request to the checkout service. The bytes pass through the network stack into the kernel.

2

A probe records the start

A probe on the read path stores the current time in a map, keyed by the connection and the process.

3

The application works

The checkout service handles the request. The probes do nothing unless the service reads or writes something relevant.

4

A downstream call leaves

The service calls the inventory service. A probe on the write path notes a client operation starting.

5

The downstream call returns

When the inventory response is read, the probe computes the client duration and emits an event.

6

The response is written

The service writes its answer. A probe notes the end time and emits an event with the timing and the first bytes.

7

The agent builds spans

In user space, the agent parses the bytes, matches start with end and forms a server span for the request and a client span for the downstream call.

8

Export

Spans and metric updates are batched and sent to the Collector. Total extra work in the request path was a few microseconds per probe hit.

What Is Missing From the Story

Notice what the agent knows and what it does not. It knows the method, path, status, timing and the endpoints of each connection. It does not know the customer’s identity, the order’s value, which branch of the code ran, or why the downstream call happened. It also has to decide how the server span and the client span relate, and how this trace connects to the caller’s trace on another machine. That last question is difficult enough to need its own chapter.

The agent sees the shape of the conversation, not the meaning of the business.

JThe Hard Problem: Context Propagation

A trace is only a trace if its pieces connect. Making them connect from outside the application is the hardest thing eBPF instrumentation does.

What Context Propagation Is

When service A calls service B, they need to share an identifier saying “this call belongs to trace number 4711, and I am span number 9”. That information travels in a standard HTTP header called traceparent, defined by the W3C Trace Context standard. Service B reads the header and continues the same trace. This hand-off is called context propagation. Without it, each service creates its own separate, unconnected trace.

Everyday analogy

Imagine a relay race where each runner carries a numbered baton. The baton tells timekeepers which race the runner belongs to. If a runner drops the baton and picks up an unnumbered one, the timekeepers see many short sprints and no race. The header is the numbered baton.

Reading Is Easy, Writing Is Hard

Reading the incoming header is straightforward. The bytes are already present in the request, so the agent can read the trace identifier and attach its spans to the caller’s trace. Writing the header into an outgoing request is much harder. The bytes of the outgoing request belong to the application. To add a header, something must change those bytes before they are sent, and the safety rules that make eBPF trustworthy deliberately restrict how much a program may change.

How Tools Approach It

ApproachHow it worksLimits
Honor headers the application already sendsIf the application or an SDK passes the header along, the agent reads and uses itRequires some cooperation from the application
Runtime-specific injectionFor some runtimes, probes cooperate with the application’s own data structures to add the headerTied to specific languages and library versions
Network-level injectionNetwork hooks add the header to supported protocols in outgoing trafficNeeds certain kernel features; works only for some protocols and conditions
Best-effort linkingMatch calls by connection details and timing, without headersApproximate; can mislink under heavy concurrency

The details differ between tools and change as projects mature, so check what your chosen tool supports on your kernel version and protocols.

What Happens When Propagation Fails

When the chain breaks, each service still gets accurate spans and metrics, but traces no longer cross service boundaries. You see many short traces instead of one long one. Service maps built from connection data still work, because they depend on who connected to whom, not on trace identifiers. Latency by service is still correct. What you lose is the ability to follow one specific request from front door to database.

i
A practical hybrid

Many teams get complete traces cheaply by making sure every service simply forwards the trace header it receives, which often needs only a small shared library or proxy setting, while eBPF produces the spans. The application passes the baton; the agent keeps the times.

!
Common trap

Do not judge eBPF tracing by a quick demo of two services where everything is connected. Test your real call patterns, including asynchronous queues, proxies and load balancers, before promising end-to-end traces to stakeholders.

KLanguage Coverage: What Works for Which Language

The promise of “any language” is real at one level and limited at another. The difference depends on how each language runs.

Two Levels of Visibility

At the protocol level, the agent sees traffic crossing the kernel: requests, responses, timing and connection details. This works for almost any language, because every language eventually calls the same kernel functions. At the function level, the agent sees inside the application, such as the arguments of a specific library call. This depends on how the language is compiled and executed, and works only where probes can find stable function addresses.

Language familyProtocol-level viewFunction-level viewNotes
GoYesYes, for supported librariesCompiled to native code; probes read arguments; sensitive to Go and library versions
C, C++, RustYesPossible when symbols are presentStripped binaries hide function names; inlining removes probe points
Java, Kotlin, ScalaYesLimitedThe runtime generates code on the fly, so stable function addresses are scarce; an in-process agent is far richer
Python, Ruby, PHP, Node.jsYesLimitedInterpreters run application code as data; in-process agents give deeper views
.NET languagesYesLimitedSimilar to Java, with runtime-generated code
Everyday analogy

A security guard at a building entrance does not need to speak every tenant’s language to record who enters and when. That is protocol-level visibility. Reading a note a tenant is writing at their desk requires understanding their language and being close enough to see it. That is function-level visibility, and it works only for languages the guard has studied.

The Concurrency Puzzle

To compute a request’s duration, the agent must match its start to its end. In old-style servers, one thread handles one request at a time, so matching by thread is easy. Modern programs handle many requests on the same thread, or move one request between threads: event loops in Node.js, goroutines in Go, async code in many languages. Matching must therefore rely on the connection a request arrived on, not on the thread. This works well for simple request-response protocols, and less well when many requests share one connection, as in HTTP/2 and gRPC, where the agent must also track stream identifiers.

When Depth Matters, Add an In-Process Agent

For Java and similar runtimes, a common pattern is to let the eBPF agent provide baseline coverage for everything, and attach the language’s own in-process agent only to the services where deeper detail repays the extra setup. The protocol-level data from eBPF still gives you a consistent view across the fleet, even for services that have no in-process agent.

i
Tip

Inventory your fleet by language and by criticality before choosing a plan. A rough table of “language, number of services, need for deep detail” quickly shows where eBPF alone is enough and where you need more.

LDeploying on Kubernetes

Kubernetes is where eBPF instrumentation is most often used, because one agent per machine can cover every pod on it.

The Node-Level Agent

Kubernetes organizes work into pods, each holding one or more containers, running on machines called nodes. A DaemonSet is a Kubernetes object that runs exactly one copy of a program on every node. Deploying the eBPF agent as a DaemonSet gives complete node coverage: when a new pod lands on a node, the local agent notices and begins watching it, with no change to the pod.

flowchart TD
  subgraph N1["Node 1"]
    P1["Pod A"] -.->|"observed by"| AG1["Agent pod from the DaemonSet"]
    P2["Pod B"] -.->|"observed by"| AG1
  end
  subgraph N2["Node 2"]
    P3["Pod C"] -.->|"observed by"| AG2["Agent pod from the DaemonSet"]
  end
  AG1 -->|"OTLP"| GW["Collector gateway"]
  AG2 -->|"OTLP"| GW
  GW --> BE["Backend"]
    
Figure 4 — One agent per node watches every pod on that node and forwards standard telemetry to a gateway.

What the Agent Needs

  • Visibility into other containers’ processes. By default a container sees only its own processes. The agent usually runs with access to the host’s process list so it can see and probe programs in neighbouring pods.
  • Elevated permissions. Loading eBPF programs needs special capabilities, covered in the next chapter. Many deployments run the agent as a privileged container, and security-conscious ones list only the exact capabilities required.
  • Access to kernel interfaces. The agent mounts a few host paths, such as the filesystem that holds eBPF objects and the file describing kernel data layouts.
  • Read access to Kubernetes metadata. To label telemetry with pod and workload names, the agent asks the Kubernetes API, which needs a role granting read-only access.
Everyday analogy

A DaemonSet agent is like one night security officer assigned to each floor of a hotel. Whenever a new guest checks into a room on that floor, the officer for that floor automatically includes the room in their rounds. Nobody has to tell the guest or the room.

Choosing What to Watch

Watching every process on every node is rarely what you want. Agents let you narrow scope by listening port, executable name, namespace or labels. Starting narrow, for example one namespace, lets you check data quality and measure overhead before expanding. Excluding noisy infrastructure processes also keeps costs and cardinality under control.

Sidecar and Operator Options

Some projects, especially the Go instrumentation, can run beside a single application instead of per node. The OpenTelemetry Operator for Kubernetes can inject such helpers when a pod carries the right annotations, and shared process visibility inside the pod lets the helper see the application’s process. The per-pod approach gives tighter control and clearer ownership, at the price of more copies of the agent and more resource requests.

!
Common trap

Resource requests and limits for the agent matter. If the agent is starved of CPU, it cannot drain its ring buffers fast enough, and events are dropped while the application looks perfectly healthy. Give agents a reasonable reservation and monitor their drop counters.

MKernel Requirements, Privileges and Compatibility

eBPF is powerful because it works at a low level, and that brings requirements that SDKs never have.

The Requirements Checklist

RequirementWhy it existsHow to check
Linux hostThe mature eBPF ecosystem lives on Linux; other systems have limited or different implementationsConfirm node operating systems in every cluster
Recent enough kernelFeatures such as ring buffers and certain hooks arrived in specific kernel versionsCheck the kernel release on each node; compare with the tool’s documentation
Kernel type information (BTF)Lets one program build adapt to different kernelsLook for the kernel’s BTF file; ask the vendor if it is missing
Specific capabilitiesLoading programs and attaching probes are privileged operationsRead the tool’s list and grant exactly those
Permission from security policyContainer runtime, mandatory access control and kernel lockdown can block eBPFTest in a staging cluster with production policies
Symbols or runtime supportUser-space probes need function names or a known runtime layoutTest sample binaries from each team

Capabilities and Privilege

Linux divides administrator power into small pieces called capabilities. Newer kernels have capabilities specifically for loading eBPF programs and for performance monitoring, so agents no longer need full administrator power. Typical agents still ask for a short list that includes permission to load programs, to observe other processes and to read certain system files. The tool’s documentation lists the exact set. Granting only that list, instead of a blanket privileged mode, is a straightforward security improvement.

The Compatibility Matrix

Large organizations run a mix of kernels: new cloud images, older long-term-support enterprise distributions with features back-ported, and vendor-specific kernels. Build a simple matrix of operating system image, kernel version, BTF availability and security policy for each cluster. Test the agent on one machine from each row before a broad rollout. The results often show a few special cases, such as an older node pool that needs an upgrade or an exception.

Everyday analogy

Installing a new smart-home device requires the right plug, voltage and Wi-Fi standard. A device that works perfectly in one house may not work in an older one. Checking each house before installing saves frustrating surprises. The compatibility matrix is that house-by-house check for your clusters.

Where eBPF Agents Cannot Run

Some environments simply do not allow it. Serverless container platforms that give you no access to the underlying kernel cannot host a node-level agent. Some managed Kubernetes offerings restrict privileged workloads or hide the nodes. Local development on laptops with other operating systems usually runs a Linux virtual machine, which may or may not expose the features required. In these places, in-process instrumentation remains the practical choice.

i
Tip

Involve your security team early and give them a one-page summary: what the agent can see, which capabilities it needs, where data goes and how it can be disabled. Agents that are explained are approved far faster than agents that are discovered.

NIdentity and Metadata: Giving Telemetry a Name

An observation of “a request took 80 milliseconds” is useless until you know which service it belongs to. eBPF sees processes and sockets; people think in services and teams.

The Identity Gap

What eBPF sees at the kernel level is a process identifier, a binary name and a network address. What dashboards need is a service name, a namespace, a version, a deployment environment and an owner. Bridging this gap is the job of the agent’s metadata enrichment, and doing it well decides whether the data is usable.

Where the Names Come From

AttributeTypical sourceRisk
Service nameAn environment variable visible on the process, a Kubernetes label, the workload name, or the executable name as a last resortExecutable names such as “java” or “python” tell you nothing about the service
Namespace, pod and nodeThe Kubernetes APIStale data if the API cache lags behind pod changes
Workload and ownerLabels or annotations on the deploymentMissing labels produce “unknown” ownership
Service versionImage tag or a label“latest” tags hide real versions
EnvironmentCluster or namespace conventionsInconsistent naming between clusters

Aligning With SDK-Produced Data

If some services use SDKs and others use eBPF, the same service should carry the same name in both worlds. Otherwise a dashboard for “checkout” shows half the story, and totals are wrong. Establish one naming rule, such as “the service name is the Kubernetes workload name unless an explicit label overrides it”, and apply it in the agent, in SDK configuration and in the Collector. Write the rule down and check it with a periodic report of names that appear in only one source.

Everyday analogy

A hospital might record a patient by hospital number in one system and by name in another. When both systems are combined, the patient appears twice unless someone ensures that one identifier links them. A consistent service name is the shared patient number across your telemetry sources.

Kubernetes Cost and Permissions

Enrichment from the Kubernetes API requires the agent to watch pods and other objects. On large clusters, hundreds of agents each watching everything can create real load on the API. Efficient agents cache data, watch only the objects on their own node, or delegate enrichment to a central Collector. Whichever design you choose, grant read-only roles, and test behaviour during large rollouts, when many pods change at once.

!
Common trap

A service named after its program, such as “node” or “java”, is a signal that identity enrichment is not working. Count these generic names in your first week and fix the source of the naming before building any dashboards on top.

OConnecting the Agent to the Collector Pipeline

The agent is the first stop. The OpenTelemetry Collector is where shared policy is applied before data reaches the backend.

Where to Send the Data

The agent exports using OTLP to a Collector, either one running on the same node or a shared gateway pool. A Collector is a program with receivers that accept data, processors that change it, and exporters that send it onward. Sending through a Collector, rather than straight to a backend, lets you apply consistent rules to data from both eBPF agents and SDKs: filtering, renaming, sampling, redaction and routing.

A Sensible Processor Order

1

Memory limiter

First in line, so the Collector can refuse data before its memory fills up during a spike.

2

Enrichment

Add or correct resource details such as Kubernetes metadata, when the agent has not already done so.

3

Filtering and normalization

Drop health-check spans, replace raw URL paths with route templates, and align attribute names with your organization’s conventions.

4

Sampling

Reduce volume by keeping a share of traces, or the interesting ones, before they are stored.

5

Batching

Group items into efficient batches just before export, so you never batch what earlier steps would have discarded.

Everyday analogy

A factory’s loading dock has stages: a gate that limits how many trucks enter, a check that labels each box, a station that removes defective goods, and finally a crew that packs boxes into full trucks. Each stage is cheap on its own and effective in order. A Collector pipeline works the same way.

The Cardinality Problem Is Bigger With Protocol Parsing

An agent that reads request paths from raw bytes may see paths like /orders/8841, /orders/8842 and so on, with a different identifier each time. If these become metric attributes, each identifier creates a new time series, and the backend’s cost balloons. Good agents try to infer route templates, such as /orders/{id}, and good Collector pipelines add a normalization step as a safety net. Watch the number of distinct values of the route attribute during the first days of a rollout.

Mind the Edition of the Names

The agent emits attribute and metric names from some edition of OpenTelemetry’s semantic conventions, the shared dictionary of names. Your SDK-based services may emit a different edition. Check the schema URL, the label that identifies the edition, and normalize in the Collector where editions differ. A short comparison of the agent’s output against your registry or rule sheet, done before broad rollout, prevents surprises in dashboards and alerts.

i
Tip

Treat the agent like any other telemetry producer: give it a named pipeline in the Collector, with its own filters and limits. If it misbehaves, you can throttle or disable its data without touching anything else.

PPerformance and Overhead: What Does It Really Cost?

eBPF is often described as low overhead. That is true in design, but the actual cost depends on how many events your machines produce and how much work the agent does with each one.

Where the Cost Comes From

Cost sourceWho paysWhat drives it
Probe executionThe application’s own request path, in microsecondsNumber of events hit, and how much each probe does
Copying events outKernel and agentBytes captured per event and event rate
Parsing and span buildingThe agent’s CPUProtocol complexity and request rate
Metadata lookupsThe agent and the Kubernetes APIPod churn and caching quality
ExportThe agent and the networkSpans and metrics produced, and batching
Memory for maps and buffersKernel memory on the nodeNumber of tracked connections and buffer sizes

A Back-of-the-Envelope Estimate

The following numbers are illustrative assumptions, not measurements. Suppose a node serves 5,000 requests per second, and each request produces about six probe events: one for the inbound read, one for the outbound response write, and two each for a downstream call. That is 30,000 events per second. If a probe takes about one microsecond to run, the kernel-side cost is 30 milliseconds of CPU per second, roughly three percent of one core spread across the application’s own request paths.

Now consider the agent. If parsing and span building take around ten microseconds per event, the agent needs about 300 milliseconds of CPU per second, close to a third of one core. The lesson is that the user-space half usually costs more than the kernel half. Efficient parsing, caching and batching matter more than shaving nanoseconds from probes.

Everyday analogy

A toll booth adds a few seconds to each car’s journey, which is almost nothing for one car. The real cost shows up at the accounting office that processes millions of tickets afterward. Probes are the toll booth; the agent is the accounting office.

Sharing Across Processes

An in-process agent runs inside every service, with its own memory and CPU use, multiplied by the number of service instances. An eBPF agent runs once per node, so its fixed costs are paid once for all the pods on the machine. For nodes that host dozens of small services, this amortization is a real advantage. For a node running a single large service, the benefit is smaller.

How to Measure It Yourself

1

Choose matched nodes

Pick two groups of similar nodes carrying similar traffic, one with the agent and one without.

2

Compare the application

Measure request latency at high percentiles, throughput and CPU of the application on both groups over several days, including peak hours.

3

Measure the agent

Record the agent’s CPU, memory, dropped events and export volume. Watch drop counters especially closely.

4

Decide with data

If differences in the application are within noise and the agent’s footprint fits your budget, widen the rollout. If not, tune scope before going further.

!
Common trap

Benchmarks run on idle test machines often flatter the tool. Always test during production-like load, and include the busiest hour of your busiest day, because event rates and ring-buffer pressure peak when you most need the data.

QTuning an eBPF Agent at Scale

A handful of settings decide whether an agent is quiet and useful or noisy and costly across hundreds of nodes.

Narrow the Scope

The cheapest event is one that is never observed. Restrict the agent to the namespaces, ports or workloads you care about. Exclude infrastructure noise and test traffic. Disable protocols you do not use, so parsers for them are not even loaded. Scope is the biggest single lever, and the easiest to adjust gradually.

Mind the Ring Buffer

Remember that the ring buffer is a fixed-size shared area. If events arrive faster than the agent drains them, new events are dropped. Suppose each event carries 300 bytes and the node produces 30,000 events per second. That is about nine megabytes per second flowing into the buffer. A buffer of eight mebibytes would hold less than one second of events. If the agent stalls for longer than that, because of CPU starvation or a garbage-collection pause, data is lost. Larger buffers buy time, at the cost of kernel memory, and sufficient CPU for the agent buys more.

A Table of Levers

LeverEffectRisk
Scope by namespace, port or labelFewer events, lower costBlind spots if scope is too narrow
Disable unneeded protocolsLess parsing, smaller attack surfaceMissing data when new protocols appear
Limit bytes captured per eventSmaller buffers, less sensitive data exposureCut-off headers or truncated queries
Larger ring bufferAbsorbs bursts and agent pausesMore pinned kernel memory
Agent CPU reservationKeeps up with event ratesHigher fixed cost per node
Sampling of tracesLower span volumeRare problems may be missed
Route templatingControls metric cardinalityWrong templates merge distinct endpoints
Export batch settingsFewer, larger requests; less CPUMore delay before data appears
Everyday analogy

Tuning a security camera system means deciding which doors to watch, how much storage to keep and how often to check recordings. Watching every door in high resolution and keeping everything forever is simple but expensive. Choosing the important doors and a sensible resolution gives most of the value for a fraction of the cost.

Treat Rollout as an Experiment

Start with one cluster and one namespace. Record three baseline numbers: agent CPU, drop counts and number of distinct route values. Expand in steps, such as ten percent of nodes, then half, then all, and check the same numbers at each step. Keep a documented way to disable the agent quickly, such as a configuration flag, so that a problem found at three in the morning can be contained in minutes.

i
Tip

Create a dashboard for the agents themselves: events seen, events dropped, spans exported, export errors, CPU and memory. An observability tool that is not itself observed is the most common surprise during incidents.

RContinuous Profiling With eBPF

Traces tell you which request was slow. Profiles tell you which lines of code were busy. eBPF makes the second kind of data cheap enough to collect all the time.

What a Profile Is

A CPU profile is a statistical picture of where a program spends its processing time. A profiler periodically interrupts the processor, notes the call stack, the chain of functions currently running, and moves on. After thousands of samples, the functions that appear most often are the ones using the most CPU. Results are usually drawn as a flame graph, where wider boxes mean more time.

Everyday analogy

Imagine photographing a busy office every few seconds for a week and counting how many photos show each person at the printer, the meeting room or the coffee machine. You do not need to watch constantly. After enough photos, the counts reveal where time really goes. Profiling is that office photography for code.

Why eBPF Suits Profiling

A performance event hook lets an eBPF program run at a fixed rate on each CPU, for example some tens of times per second. At each tick, the program records the current stack. Because it runs in the kernel and sees every process, a single agent per machine profiles all programs at once, without restarting them or adding special flags. At such a low sampling rate, overhead stays small enough to leave switched on permanently, which is what makes continuous profiling practical.

The hard part is reading stacks. Native programs may omit the information that makes unwinding easy. Interpreted and just-in-time-compiled languages keep their own structures. OpenTelemetry’s eBPF profiler contains logic to unwind native code and several popular language runtimes directly from eBPF, which is a large part of its value.

Cost of the Data

Take a machine with 64 CPU cores and a sampling rate of 20 samples per second per core. That is 1,280 stacks per second. Many repeat the same few paths, so the agent aggregates them into counts before export, and the data is small compared with traces. Turning raw addresses into readable function names, called symbolization, is often done later on a server, using debug information, to keep the node light.

What Questions It Answers

QuestionWhat the profile shows
Why did CPU usage double after the last release?Which functions grew wider in the flame graph between versions
Which service is the best target for cost savings?The services and functions that consume the most processor time across the fleet
Is the time in our code or in a library?Stacks split by application code, runtime and system libraries
What is burning CPU during incidents?A stack-level view for the exact time window, available without reproducing the issue
!
Maturity note

Profiling is a newer OpenTelemetry signal, and its data model and protocol support are still being finalized. Links between a profile and a particular trace are also still developing. Plan for change, and test your chosen backend’s support before relying on it.

SNetwork Observability With eBPF

Sometimes the question is not what the code did but how the network behaved between services. eBPF sits right where that information lives.

What the Kernel Knows About Connections

The kernel tracks every network connection: who opened it, to whom, how many bytes moved, how many packets were lost and retransmitted, and how long round trips take. Probes and socket hooks can sample these facts without touching the application. Combined with Kubernetes metadata, they turn raw addresses into statements like “the checkout pods sent 40 gigabytes to the inventory pods yesterday, with a rising rate of retransmissions”.

Typical Network Signals

SignalMeaningWhy you care
Bytes and packets per workload pairHow much traffic flows between two servicesFinds chatty services and capacity limits
RetransmissionsPackets that had to be resentEarly sign of congestion or faulty links
Round-trip timeNetwork delay between endpointsSeparates network slowness from application slowness
Connection counts and resetsHow often connections open, close or failExposes connection storms and misconfigured timeouts
DNS lookup time and failuresHow quickly names are resolvedA frequent hidden cause of intermittent latency
Cross-zone or cross-region trafficData crossing cost boundariesCloud providers often bill for it
Everyday analogy

Road traffic counters tell a city planner how many vehicles pass between neighbourhoods, how often they stop and how long trips take, without opening a single truck to see its cargo. Network observability is the road counter. It is superb for questions about volume and delay, and says nothing about contents.

Service Maps From Connections

Because each connection has a source and a destination, you can draw a map of which services talk to which, without any trace headers. This map is built from facts, not from declared configuration, so it reveals surprising dependencies such as a forgotten service still being called every minute. Even where full traces cannot be connected, these maps remain accurate.

Beware of Flow Cardinality

A raw list of every connection includes ephemeral ports and short-lived pod addresses, so its cardinality, the number of unique combinations, is enormous. Good network tools aggregate by workload, namespace or zone before export. Keep the unit of reporting at the level that people reason about, such as “service A to service B”, and drop port and address details from long-term metrics. Otherwise, the backend cost grows with every pod restart.

i
Tip

Use network metrics to separate causes. When a service is slow, round-trip time and retransmissions tell you quickly whether the network is to blame, which can save a whole incident’s worth of guessing.

TSecurity, Privacy and Trust

An agent that can see every request on a machine is powerful, and anything powerful must be handled with care.

What the Agent Can See

At the protocol level, an eBPF agent can read the beginning of every message crossing the machine. That includes URLs, headers and sometimes parts of bodies. Those may contain things that should never be stored: access tokens in headers, personal identifiers in paths, secrets in query strings, or fragments of database statements with real values. Even if the agent never exports these fields, it has had access to them, which matters for risk assessment and compliance.

Everyday analogy

The front desk of a building sees every visitor’s face and name. That is useful, and also sensitive. A good building limits who works at the desk, what is written in the logbook and how long the logbook is kept. An eBPF agent is the front desk of your machines and deserves the same discipline.

Principles for Safe Use

  • Capture the minimum. Limit how many bytes per message are captured and which fields are kept. Do not capture bodies unless you have a specific, reviewed need.
  • Redact early. Remove or mask sensitive values in the agent, and again in the Collector, before anything is stored.
  • Exclude sensitive workloads. Keep payment, health or identity systems out of scope until their owners have reviewed what will be observed.
  • Get consent from owners. In shared clusters, tell teams what is being observed and how to opt out or adjust.
  • Minimize privileges. Grant exactly the capabilities the tool documents, not a blanket privileged mode.

The Agent Is a Valuable Target

Because the agent has elevated permissions on every node, compromising it would be attractive to an attacker. Reduce that risk like you would for any privileged component: pull images only from trusted registries and verify signatures where available, pin versions, restrict who can modify the DaemonSet, keep the agent’s own network access narrow, and review its permissions after each upgrade. Log changes to its configuration, so that unusual changes are visible.

Verified Is Not the Same as Harmless

The kernel’s verifier guarantees that an eBPF program cannot crash the system or read memory outside its allowed areas. It does not guarantee that the program’s purpose is acceptable. A perfectly verified program can still read sensitive data it is allowed to see. Trust therefore comes from knowing who wrote the program, reviewing its source or its vendor’s attestations, and controlling who can load new programs on your nodes.

RiskMitigation
Sensitive values captured from URLs or headersLimit captured bytes; redact in agent and Collector; test with fake sensitive data
Agent compromiseTrusted images, restricted deployment rights, minimal capabilities, configuration audit
Overbroad visibility in shared clustersScope by namespace; publish what is observed; offer opt-out for sensitive teams
Unreviewed programs loaded on nodesRestrict who holds the loading capabilities; alert on unexpected loaded programs
Retention of sensitive data in the backendShort retention for risky fields; access controls on telemetry stores

UWhat eBPF Cannot See

Honest tools have honest limits. Knowing them in advance prevents disappointment and wrong decisions.

The Business Meaning Gap

An eBPF agent sees that a request to /checkout took 230 milliseconds and returned a success code. It does not see that the customer was on a premium plan, that the cart held eleven items or that a discount rule fired. Those facts live inside the application’s memory and logic, and never cross a hook in a form the agent can interpret. Anything that requires knowing the meaning of the data, not just its shape, needs in-code instrumentation.

Errors That Look Like Successes

Many applications return a normal success status even when the work failed, placing the real error inside the body of the response. GraphQL services are a classic example. A protocol-level agent that reads only status codes will report these as healthy requests. If your error signals live in message bodies, you need the application to expose them as attributes or metrics.

A Table of Gaps

You needWhat eBPF givesBetter source
Customer, tenant or plan details on spansNothing, unless visible in headers you allowSDK attributes set in code
Internal steps inside one serviceLittle, except for specific supported functionsManual spans or an in-process agent
Application-level errors hidden in bodiesOnly status codesCustom metrics or span status set in code
Structured log contentNot its strengthApplication logging with trace identifiers
Serverless function invocationsNot available where nodes are hiddenPlatform integration or SDK
Windows serversLimited or none, depending on toolIn-process agents
Calls over unsupported encryption librariesEncrypted bytes onlyIn-process instrumentation or a proxy that terminates encryption
Everyday analogy

A traffic camera at a junction can tell you how many cars passed, how fast and in which direction. It cannot tell you that one of them was an ambulance rushing to an emergency or that the driver was lost. For those facts you need someone inside the vehicle. In-code instrumentation is the person inside the vehicle.

Service Meshes and Double Counting

In clusters that use a service mesh, a sidecar proxy sits beside each application and handles its network traffic. An eBPF agent on the same node can see both the application’s traffic to the proxy and the proxy’s traffic to the network, producing two sets of spans for one call. Decide which layer is the source of truth for each service, and filter the other, so that you do not double the volume or the apparent request count.

!
Common trap

The easiest way to disappoint stakeholders is to promise “full tracing with zero effort” and then discover that important business attributes are absent. Describe the result as “broad, standard coverage on day one, with depth added where it matters”.

VeBPF Versus SDKs, Agents and Service Meshes

No approach wins every comparison. The table below sets them side by side so you can see the trade-offs at a glance.

CriterionManual SDKIn-process agenteBPF-basedService mesh proxy
Setup effortHigh, per serviceMedium, per serviceLow, per nodeMedium, per mesh
Code changesYesNoNoNo
Business-level detailExcellentLimitedNone to limitedNone
Library-level detailOnly what you writeVery goodModerateNone
Language independenceNoNoLargely yesYes
Where overhead landsIn each processIn each processIn kernel and one agent per nodeIn a proxy per pod
Privileges neededNone specialUsually none specialElevated on nodesMesh control rights
Trace propagationCompleteCompleteVaries by tool and kernelNeeds application help
Works on locked-down platformsYesMostlyOften noDepends

Strengths of eBPF-based instrumentation

  • No code changes, rebuilds or restarts
  • One approach for many languages
  • Coverage for programs you cannot modify
  • Fast time to first useful data
  • Fixed costs shared per node
  • Also enables profiling and network visibility

Weaknesses of eBPF-based instrumentation

  • Little business context or custom detail
  • Trace propagation is harder and varies
  • Needs recent Linux kernels and elevated privileges
  • Depends on symbols and library versions for deep probes
  • Not available on many managed or serverless platforms
  • Projects are young and still evolving
Everyday analogy

Choosing among these approaches is like choosing tools for home repairs. A multi-tool gets many small jobs done quickly with no setup. A specialist tool does one task superbly but takes time to set up. Professionals own both, and use each where it fits.

Questions That Decide the Choice

  • Can you change and redeploy the code? If not, eBPF may be your only practical option.
  • Do you need business detail? If so, plan for SDK instrumentation on key services.
  • Can your platform host privileged node agents? If not, in-process agents are the realistic path.
  • How many languages do you run? The more there are, the more attractive a single node-level approach becomes.
  • How important are complete end-to-end traces? If very, test propagation carefully with your actual traffic.

WCombining Approaches: A Floor and a Ceiling

The most successful teams treat eBPF as the floor of their observability and SDKs as the ceiling.

The Floor

The floor is the minimum level of visibility every service must have, no matter who owns it or what language it uses: request rate, errors, duration, dependencies and basic resource use. eBPF is ideal for establishing this floor quickly, because it requires nothing from service teams. Once it is in place, nobody is invisible, and unknown services stop being a surprise during incidents.

The Ceiling

The ceiling is the richest view you can get, built by people who know the code. Critical services receive manual spans around key business steps, custom metrics for things like orders and payments, and attributes that identify customers, plans and features. This costs effort, so it is applied where the value is highest.

flowchart TD
  A["Can you change and redeploy the code?"] -->|"No"| B["Use eBPF as the main source"]
  A -->|"Yes"| C{"Need business detail
or custom spans?"} C -->|"Yes"| D["Use SDK or in-process agent
and add eBPF for gaps"] C -->|"No"| E{"Can the platform run
privileged node agents?"} E -->|"Yes"| F["Use eBPF for fast broad coverage"] E -->|"No"| G["Use in-process agents"]
Figure 5 — A simple way to choose between approaches for each service.

Avoid Duplicates

When a service has both SDK instrumentation and an eBPF agent watching it, both may create a server span for the same request. The result is doubled counts and confusing traces. There are three ways to avoid it. Exclude SDK-instrumented services from the eBPF agent’s scope. Filter one source out in the Collector for those services. Or use an agent that detects existing instrumentation and steps back, if your chosen tool offers that behaviour. Whichever you choose, test with one service and compare counts against your load generator.

One Vocabulary for Both

Combined data is only useful if both sources speak the same language. Agree on one edition of the semantic conventions, one rule for service names, and one set of resource attributes. Use the Collector to normalize any differences. Dashboards then work across the whole fleet, and a service can graduate from eBPF-only to SDK instrumentation without breaking charts.

A Typical Journey

1

Week one

Deploy the eBPF agent to a pilot cluster. Verify names, cardinality and overhead.

2

Month one

Roll out to all clusters that meet kernel requirements. Build baseline dashboards and alerts on request rate, errors and duration.

3

Quarter one

Choose the ten most critical services and add SDK spans and business attributes. Configure the eBPF agent to step aside for them.

4

Ongoing

Promote more services to the ceiling as value is proven, and keep eBPF as the safety net for everything else.

XFailure Modes and Troubleshooting

When data is missing or strange, a short list of usual causes explains most cases.

SymptomLikely causeWhat to do
Agent fails to start on some nodesOld kernel, missing type information, or blocked permissionsCheck kernel version and BTF; review security policy; compare against a working node
No data for one serviceThe service is not discovered, binary has no symbols, or it uses an unsupported libraryCheck scope rules; test with a sample binary; read supported-library lists
Gaps in spans during peak loadRing buffer drops because the agent cannot keep upIncrease agent CPU; enlarge buffer; reduce scope or captured bytes
Doubled request countsBoth SDK and eBPF instrument the same service, or a mesh proxy is also observedPick one source of truth per service and filter the other
Traces broken into piecesNo header propagation between servicesMake applications forward trace headers or use a tool and kernel that support injection
Metric costs jumpRaw URL paths became attributesEnable route templating and add Collector normalization
Services named after programsIdentity enrichment failingFix labels and naming rules; check Kubernetes permissions
Instrumentation stopped after an application upgradeFunction names or layouts changed, so probes no longer matchUpdate the agent; check release notes for supported versions
Agent restarts or runs out of memoryToo many tracked connections or insufficient limitsRaise limits; narrow scope; check connection churn
Kubernetes API load spikesMany agents watching broad sets of objectsUse caching, per-node watches or central enrichment

A Debugging Routine

1

Check the agent’s health

Is it running on the node? Look at its logs for load errors, and at its own metrics for drops and export failures.

2

Confirm the platform basics

Verify kernel version, type information and granted capabilities on the affected node.

3

Confirm discovery

Does the agent list the target process as instrumented? If not, adjust scope rules.

4

Check protocol and encryption support

Is the traffic a protocol the tool parses, and does the application use an encryption library it can hook?

5

Compare with a known good

Run a simple test service, such as a basic web server, on the same node. If it works, the issue lies with the target application.

6

Report upstream

Gather the kernel version, tool version, application language and library versions and open an issue with the project, because many gaps are fixed quickly once reported.

i
Tip

Keep a small, boring test service in every cluster. When something looks wrong, its presence or absence in your dashboards separates “the agent is broken” from “this application is special”.

YBest Practices and Anti-Patterns

Teams that adopt eBPF instrumentation successfully share a few habits. Teams that struggle share a few mistakes.

Habits That Work

Start

Pilot first

Begin with one cluster and one namespace, and measure overhead, drops and cardinality before expanding.

Scope

Watch only what you need

Limit by namespace, port and protocol. Smaller scope means lower cost and less sensitive data seen.

Identity

Fix names early

Agree on service naming rules, and verify that no services appear under generic program names.

Pipeline

Send through a Collector

Apply filtering, redaction, normalization and sampling in one place for all sources.

Safety

Grant minimal privileges

Use the documented capability list, not a blanket privileged mode, and restrict who can change the agent.

Depth

Add SDKs where value is high

Use eBPF for the floor and manual instrumentation for critical business paths.

Watch

Monitor the agents

Track drops, CPU, memory and export errors on a dashboard owned by the platform team.

Plan

Track versions and kernels

Maintain a compatibility matrix and test agent upgrades on canary nodes first.

Mistakes That Teach

ANTI-PATTERN 01 Avoid
What people do

They deploy the agent cluster-wide with default settings on the first day, watching every namespace and every protocol.

Why it hurts

Unexpected cardinality, noisy infrastructure traffic and agent resource pressure appear everywhere at once. Teams lose trust before the value is clear, and the sensitive-data review has not happened.

Do this instead

Roll out in stages with measured checkpoints, and review scope, redaction and overhead at each one.

ANTI-PATTERN 02 Avoid
What people do

They run the agent in a fully privileged mode with broad host access because it was the fastest way to get it working.

Why it hurts

A compromise of that one component gives an attacker control of every node. Security reviews later force a disruptive redesign.

Do this instead

Start from the documented minimal capabilities, test them in staging, and restrict deployment rights from day one.

ANTI-PATTERN 03 Avoid
What people do

They add eBPF on top of services that already have SDK instrumentation and never check for duplicate spans.

Why it hurts

Request counts double, traces show repeated nodes and alerts based on rates fire incorrectly. Costs rise with no new information.

Do this instead

Choose a single source of truth for each service, exclude or filter the other, and verify counts against a known load.

ZReal-World Patterns

The same technology gets used in quite different ways depending on the organization. These illustrative scenarios show the range.

A legacy system nobody wants to touch

A decade-old billing engine written in C++ runs reliably and has no tests that anyone trusts. Adding an SDK would require rebuilding it, which management will not approve. An eBPF agent on the node gives request rates, error rates and latency for the engine’s network calls, and the team finally sees how its slowest calls affect downstream services, all without a single change to the binary.

A platform team establishing a baseline

A platform team supports 300 services in five languages. Rather than coordinate five instrumentation projects, it deploys one agent per node and delivers request metrics and service maps to every team within weeks. It then offers SDK guidance and templates for teams that want richer detail, and measures adoption of both layers.

Vendor-supplied binaries

A company runs third-party software, including a message broker and a commercial gateway, delivered without source. Neither can accept an SDK. The eBPF agent observes them at the protocol level, which reveals that one broker consumer is responsible for most of the end-to-end delay in a nightly job.

An unexplained latency incident

During an incident, SDK traces show a slow service but nothing inside it explains the delay. Network metrics from the same agent reveal retransmissions and long DNS lookups on a single node pool. Profiling data shows the service itself is idle. The cause is a network problem, found in minutes instead of hours.

A cost-reduction campaign

A finance-driven effort asks where compute money goes. Continuous profiles, collected across the fleet at low overhead, show that a few library functions in widely used services consume a surprising share of CPU. Fixing them lets the company shrink several node pools.

Industry Context

eBPF’s reputation rests on years of production use. Large operators have publicly described using it for high-speed load balancing and for filtering attack traffic, and well-known performance engineers helped popularize eBPF tracing tools for finding bottlenecks on busy servers. OpenTelemetry’s contribution is to turn that raw capability into standard telemetry that any compatible tool can consume.

eBPF gives you the floor of observability for free; the ceiling still takes human care.

AAA Maturity Ladder for eBPF Adoption

Adoption grows in stages. Knowing which stage you are in tells you what to do next, and what to avoid doing too early.

LevelWhat it looks likeNext move
1. ExploringA small pilot on a test cluster; a few people understand how it worksMeasure overhead, check kernel compatibility and review data captured
2. BaselineThe agent covers most clusters; request metrics and service maps exist for all servicesFix naming, cardinality and duplicates; share dashboards with teams
3. CombinedCritical services add SDK detail; sources share naming and conventions; Collector policy applies to allAutomate checks and add profiling or network signals where valuable
4. OperatedAgents have dashboards, alerts and upgrade routines; security reviews are routine; teams self-serve scope changesRefine, contribute fixes upstream and retire redundant tooling
Everyday analogy

Learning to drive goes from practising in an empty car park, to driving on quiet roads, to motorways, to driving in storms. Skipping straight to storms is dangerous. Each stage prepares you for the next, and a stage is complete when the skills feel boring.

Do Not Climb Faster Than Your Platform

A company whose clusters run old kernels or locked-down managed services may stay at level one for a while, until platform upgrades catch up. That is a reasonable outcome. The right move is to use in-process agents where eBPF cannot run, and plan the platform changes that would make eBPF possible, instead of forcing a fragile deployment.

ABA Step-by-Step Adoption Playbook

When you are ready to try this in your own environment, follow this order. Each step reduces risk for the next.

1

Inventory the fleet

List services by language, platform and criticality. Note which clusters use which kernels and security policies.

2

Choose the tool

Read the current documentation of the candidate projects. Check supported protocols, kernels, stability notes and how each handles trace propagation.

3

Brief security early

Share capabilities needed, data visible, retention plans and disable procedures. Agree on redaction and excluded workloads.

4

Pilot in staging

Deploy to a cluster with production-like policies. Run realistic load and compare request counts with your load generator.

5

Set naming and pipeline rules

Fix service naming, route templating and Collector processors. Decide how to avoid duplicates with existing SDK data.

6

Measure overhead

Compare matched node groups with and without the agent through at least one peak period. Check drops and agent resources.

7

Roll out in stages

Expand to a small share of production nodes, then half, then all, checking the same measurements at each step and keeping a quick disable switch.

8

Build the ceiling and operate

Add SDK detail to critical services, publish dashboards, monitor the agents and revisit the setup after kernel, tool or application upgrades.

i
Golden rule

Change one thing at a time and keep an easy way back. With a technology that works at the kernel level, the most valuable safety feature is a switch that turns it off in seconds.

ACA Plain-Language Glossary

A quick reference for the terms used in this guide.

TermPlain meaning
eBPFA Linux technology that runs small, safety-checked programs inside the kernel in response to system events.
KernelThe core of the operating system that manages memory, processes, disks and networks.
User spaceThe normal area of the system where applications and agents run, outside the kernel.
VerifierThe kernel’s checker that proves an eBPF program is safe before it can run.
HookAn event in the system, such as a function call or network read, that an eBPF program can be attached to.
ProbeAn eBPF program attached to a hook to watch an event.
MapA shared table in kernel memory used to store state and exchange data.
Ring bufferA fixed-size circular area through which the kernel passes events to a user-space reader.
BTF and CO-REKernel type information and a technique that let one eBPF build adapt to many kernel versions.
CapabilityA small piece of administrator power that can be granted to a program on its own.
InstrumentationThe act of making a program report on itself through telemetry.
SDKThe OpenTelemetry library that lives inside an application and creates telemetry.
Auto-instrumentationAdding telemetry to a program without changing its source code.
Trace and spanA trace is the journey of one request; each timed step in it is a span.
Context propagationPassing a trace identifier from one service to the next so their spans join the same trace.
RED metricsRate, errors and duration: the basic health numbers for a service.
OTLPThe OpenTelemetry Protocol used to send telemetry between components.
CollectorA program that receives, processes and forwards telemetry.
DaemonSetA Kubernetes object that runs one copy of a program on every node.
CardinalityThe number of unique value combinations of an attribute, which drives storage cost.
Profile and flame graphA statistical picture of where code spends CPU time, drawn as boxes whose width shows time.
SymbolizationTurning raw memory addresses in a profile into readable function names.

ADFrequently Asked Questions

Short answers to the questions that come up most often.

Q1Does eBPF replace the OpenTelemetry SDKs?
No. eBPF gives broad, standard coverage without code changes, but it cannot see business meaning or custom steps inside your code. Most teams use eBPF for the floor and SDKs for the ceiling.
Q2Do I need to restart my applications?
Normally not. The agent attaches to running processes from outside. Some tools need processes to be discoverable or to expose symbols, which may influence how the application is built, but they do not require restarts for the basic protocol-level view.
Q3Is it safe to run eBPF programs in production?
The kernel verifier prevents programs from crashing the system or reading memory they should not. Safety also depends on the agent itself being trusted and on limiting its privileges and data capture.
Q4Which kernel version do I need?
It depends on the tool and the features used. Many tools expect a reasonably recent kernel with type information available, and some enterprise kernels back-port features. Always read the tool’s current requirements and test on your own images.
Q5Will I get complete end-to-end traces?
Sometimes. Complete traces need trace headers to pass between services. Tools differ in whether they can inject headers, and applications can help by forwarding headers they receive. Test with your real call patterns.
Q6Can it see inside encrypted traffic?
It can for supported encryption libraries by observing data before encryption or after decryption on the same machine. For unsupported libraries it sees only encrypted bytes.
Q7How much overhead should I expect?
It varies with request rate, protocols and configuration. The user-space agent often costs more than the kernel probes. Measure with matched node groups under peak load rather than relying on general claims.
Q8Does it work on serverless platforms?
Generally not, because you do not control the underlying kernel or run node-level agents there. Use platform integrations or in-process instrumentation instead.
Q9What about Windows?
Mature eBPF observability is a Linux story. Windows has its own, more limited eBPF work, and most OpenTelemetry eBPF tools target Linux. Use in-process agents for Windows workloads.
Q10Are the OpenTelemetry eBPF projects stable?
They are developing quickly, and stability levels differ by project and signal. Profiling in particular is still being defined. Read each project’s current status notes before committing, and plan for upgrades.

AESummary and Key Takeaways

eBPF-based instrumentation lets you observe programs from the operating system’s side, producing standard OpenTelemetry data without touching application code.

You began with a company that could not instrument all its code, and learned how eBPF offers a safe, language-independent way to watch programs from the kernel. You saw how probes and a user-space agent turn low-level events into spans, metrics, profiles and network signals, how the OpenTelemetry projects package those ideas, and where the approach struggles, especially with context propagation, business meaning, privileges and platform limits.

Key Takeaways

  • eBPF runs small, verified programs in the kernel. The verifier makes them safe, and hooks decide which events they can watch.
  • The work is split in two. Probes capture raw facts cheaply in the kernel; a user-space agent parses them, builds standard spans and metrics, adds identity and exports through OTLP.
  • It trades depth for reach. You get broad, language-independent, request-level coverage with no code changes, but little business context or internal detail.
  • Context propagation is the hardest part. Reading trace headers is easy, writing them is not, so test whether your traces connect across services before promising end-to-end views.
  • Requirements and privileges are real. Plan for recent Linux kernels, type information, minimal capabilities, security review and platforms that may not allow node agents.
  • Measure cost and tune scope. The user-space agent usually costs more than the probes, so narrow scope, size buffers, normalize routes and watch the agents themselves.
  • Use eBPF as the floor and SDKs as the ceiling. Cover everything quickly, add detail where it pays, avoid duplicate spans and keep one shared vocabulary for names.