General availability (GA) Open source
Last reviewed: October 1, 2026

pyroscope.ebpf

pyroscope.ebpf configures an eBPF profiling job for the current host. The component forwards the collected performance profiles to the receivers you list in forward_to.

The pyroscope.ebpf component embeds the grafana/opentelemetry-ebpf-profiler which is a fork of open-telemetry/opentelemetry-ebpf-profiler.

Note

To use the pyroscope.ebpf component you must run Alloy as root and inside the host PID namespace. On Kubernetes, the simplest option is to set securityContext.privileged: true. Users who prefer least-privilege can instead grant the specific capabilities required.

Note

The profiler requires file system storage at /tmp/symb-cache to store symbol cache data. Ensure this directory is accessible and has sufficient storage space.

You can specify multiple pyroscope.ebpf components by giving them different labels, however it’s not recommended as it can lead to additional memory and CPU usage.

Required privileges

When running on Kubernetes without privileged: true, grant the following Linux capabilities and mount the kernel filesystem paths:

CapabilityPurpose
BPFLoad and manage eBPF programs and maps.
PERFMONAttach performance events and read performance counters.
SYS_PTRACERead /proc/<pid>/ entries.
CHECKPOINT_RESTOREFollow magic-links in /proc/<pid>/map_files/* for ELF symbol reading (kernel 5.9+; use SYS_ADMIN on older kernels).
SYS_RESOURCERaise RLIMIT_MEMLOCK for eBPF map locking.
DAC_READ_SEARCHRead ELF binaries and /proc entries regardless of DAC permission bits.
SYSLOGRead the kernel ring buffer for eBPF verifier diagnostics.

Mount /sys/kernel/tracing (on older kernel versions you might need /sys/kernel/debug instead) from the host as read-only volumes so the tracer can attach tracepoints.

Supported languages

  • Native code (C/C++, Rust, Zig, Go, etc. without debug symbols on host)
  • Broad set of high-level languages (HotSpot JVM, Python, Ruby, PHP, Node.js, V8, Perl).

Usage

Alloy
pyroscope.ebpf "<LABEL>" {
  targets    = <TARGET_LIST>
  forward_to = <RECEIVER_LIST>
}

Arguments

The component configures and starts an eBPF profiling job to collect performance profiles from the current host.

You can use the following arguments with pyroscope.ebpf:

NameTypeDescriptionDefaultRequired
forward_tolist(ProfilesReceiver)List of receivers to send collected profiles to.yes
bpf_fs_rootstringRoot path of the BPF filesystem for pinned maps used in trace correlation."/sys/fs/bpf/"no
build_id_cache_sizeintDeprecated (no-op), previously controlled the size of the elf file build id -> symbols table LRU cache.64no
cache_roundsintDeprecated (no-op), previously controlled the number of cache rounds.no
collect_intervaldurationHow frequently to collect profiles."15s"no
collect_kernel_profileboolDeprecated (no-op), previously enabled collection of kernel-space profiles.trueno
collect_user_profileboolDeprecated (no-op), previously enabled collection of user-space profiles.trueno
commstringHow the component includes the process command name in profiles. One of none, label, stackframe, or both."none"no
container_id_cache_sizeintDeprecated (no-op), previously controlled the size of the PID -> container ID table LRU cache.1024no
demanglestringC++ demangle mode. Available options are: none, simplified, templates, or full."none"no
dotnet_enabledboolA flag to enable or disable .NET profiling.trueno
go_enabledboolA flag to enable or disable Go profiling.trueno
go_table_fallbackboolDeprecated (no-op), previously enabled symbol lookup in .sym / .dynsym sections when .gopclntab lookup failed.falseno
hotspot_enabledboolA flag to enable or disable HotSpot profiling.trueno
kernel_framesboolInclude kernel-space frames in collected profiles. Set to false to drop kernel frames from each stack trace.trueno
lazy_modeboolEnable lazy mode to defer eBPF profiler startup until the component discovers targets.falseno
load_probeboolLoad a generic eBPF program that you can attach to a kernel or user-space hook externally.falseno
no_kernel_version_checkboolSkip the kernel version check for eBPF support.falseno
obi_process_context_enabledboolEnable profile correlation with traces generated by components compatible with OpenTelemetry eBPF Instrumentation.trueno
off_cpu_thresholdfloatProbability from 0 to 1 for recording an off-CPU event. A value of 0 disables off-CPU profiling.0no
perl_enabledboolA flag to enable or disable Perl profiling.trueno
php_enabledboolA flag to enable or disable PHP profiling.trueno
pid_cache_sizeintDeprecated (no-op), previously controlled the size of the PID -> proc symbols table LRU cache.32no
pid_labelboolAttach the process PID to each pprof sample as a pid label.falseno
pid_map_sizeintDeprecated (no-op), previously controlled the size of eBPF PID map.2048no
probe_linkslist(string)List of kernel or user-space probes from which to collect stack traces.no
python_enabledboolA flag to enable or disable Python profiling.trueno
ruby_enabledboolA flag to enable or disable Ruby profiling.trueno
same_file_cache_sizeintDeprecated (no-op), previously controlled the size of the elf file -> symbols table LRU cache.8no
sample_rateintHow many times per second to collect profile samples.19no
symbols_map_sizeintDeprecated (no-op), previously controlled the size of the eBPF symbols map.16384no
targetslist(map(string))List of process or container targets to profile.no
u_probe_linkslist(string)Deprecated. Use probe_links instead.no
v8_enabledboolA flag to enable or disable V8 profiling.trueno
verbose_modeboolEnable verbose logging for the eBPF profiler.falseno

Caution

Use no_kernel_version_check only when you run on an older kernel that includes backported eBPF features from a later release. When you skip this check, the profiler can’t verify that your kernel has the eBPF features it needs, and profiling may fail or produce incomplete results. Proceed at your own risk.

You must set the forward_to argument. Omitted fields take their default values.

Several arguments carry the label “Deprecated (no-op)”. These arguments once configured cache sizes and behaviors, but they no longer have any effect. Remove these arguments from your configuration.

Blocks

pyroscope.ebpf doesn’t support any blocks.

Exported fields

pyroscope.ebpf doesn’t export any fields.

Component health

pyroscope.ebpf reports itself as unhealthy if you give it an invalid configuration, or if the eBPF profiling session fails to start, for example because of missing privileges or an unsupported kernel.

Debug information

  • elf_cache per build id and per same file symbol tables and their sizes in symbols count.
  • pid_cache per process elf symbol tables and their sizes in symbols count.
  • targets the active targets the component tracks.

Debug metrics

The following Prometheus metrics are exposed:

NameTypeDescription
pyroscope_ebpf_active_targetsgaugeNumber of active targets the component tracks.
pyroscope_ebpf_pprofs_totalcounterNumber of pprof profiles collected by the eBPF component, per service_name.
pyroscope_ebpf_pprof_bytes_totalcounterTotal number of bytes in pprof profiles collected by the eBPF component, per service_name.
pyroscope_ebpf_pprof_samples_totalcounterTotal number of samples in pprof profiles collected by the eBPF component, per service_name.
pyroscope_ebpf_profiling_sessions_failing_totalcounterNumber of profiling sessions failed.
pyroscope_ebpf_profiling_sessions_totalcounterNumber of profiling sessions started by the eBPF component.
pyroscope_fanout_latencyhistogramWrite latency for sending to direct and indirect components.

eBPF profiler internal metrics

The component also exposes internal metrics from the embedded eBPF profiler. The component emits these metrics only after recording each one at least once, so not all metrics appear on every host. The metrics carry an otel_scope_name="pyroscope.ebpf" label.

Notable metrics include:

Native unwinding

NameTypeDescription
UnwindNativeAttempts_totalcounterUnwind attempts since the previous check.
UnwindNativeFrames_totalcounterUnwound frames since the previous check.
UnwindNativeStackDeltaStop_totalcounterNumber of stop stack deltas in the native unwinder (success).
UnwindNativeSmallPC_totalcounterNumber of times PC held a value smaller than 0x1000.
UnwindErrStackLengthExceeded_totalcounterNumber of times the unwinder exceeded MAX_FRAME_UNWINDS.

Interpreter unwinding

NameTypeDescription
UnwindPythonAttempts_totalcounterNumber of attempted Python unwinds.
UnwindPythonFrames_totalcounterNumber of unwound Python frames.
UnwindHotspotAttempts_totalcounterNumber of attempted HotSpot JVM unwinds.
UnwindHotspotFrames_totalcounterNumber of unwound HotSpot JVM frames.
UnwindRubyAttempts_totalcounterNumber of attempted Ruby unwinds.
UnwindRubyFrames_totalcounterNumber of unwound Ruby frames.
UnwindPHPAttempts_totalcounterNumber of attempted PHP unwinds.
UnwindPHPFrames_totalcounterNumber of unwound PHP frames.
UnwindPerlAttempts_totalcounterNumber of attempted Perl unwinds.
UnwindPerlFrames_totalcounterNumber of unwound Perl frames.
UnwindV8Attempts_totalcounterNumber of attempted V8 unwinds.
UnwindV8Frames_totalcounterNumber of unwound V8 frames.
UnwindDotnetAttempts_totalcounterNumber of attempted .NET unwinds.
UnwindDotnetFrames_totalcounterNumber of unwound .NET frames.

Symbolization

NameTypeDescription
PythonSymbolizationSuccesses_totalcounterNumber of successfully symbolized Python frames.
PythonSymbolizationFailures_totalcounterNumber of Python frames that failed symbolization.
HotspotSymbolizationSuccesses_totalcounterNumber of successfully symbolized HotSpot frames.
HotspotSymbolizationFailures_totalcounterNumber of HotSpot frames that failed symbolization.
RubySymbolizationSuccess_totalcounterNumber of successfully symbolized Ruby frames.
RubySymbolizationFailure_totalcounterNumber of Ruby frames that failed symbolization.

Process management

NameTypeDescription
NumProcNew_totalcounterNumber of new PID events.
NumProcExit_totalcounterNumber of exit PID events.
NumGenericPID_totalcounterNumber of generic PID events.

eBPF map state

NameTypeDescription
NumExeIDLoadedToEBPFgaugeThe number of executables loaded to eBPF maps.
HashmapPidPageToMappingInfogaugeCurrent size of the pid_page_to_mapping_info hash map.
HashmapNumStackDeltaPagesgaugeCurrent size of the stack delta pages hash map.
UnwindInfoArraySizegaugeCurrent size of the unwind info array.

The opentelemetry-ebpf-profiler metrics.json file defines the full list of metrics.

Profile collecting behavior

The pyroscope.ebpf component collects stack traces associated with a process running on the current host. You can use the sample_rate argument to define the number of stack traces collected per second. The default is 19.

The following labels are automatically injected into the collected profiles if you haven’t defined them. These labels can help you pin down a profiling target.

LabelDescription
__container_id__The container ID derived from target.
__name__Pyroscope metric name. Defaults to process_cpu.
service_namePyroscope service name. It’s automatically selected from discovery meta labels if possible. Otherwise defaults to unspecified.
otel.scope.nameThe instrumentation scope name, set to com.grafana.alloy/pyroscope.ebpf.
otel.scope.versionThe Alloy version that produced the profile.

Alloy only sets otel.scope.name and otel.scope.version if they aren’t already present on the profile. otel.scope.version is only set when otel.scope.name matches the default value.

Targets

Each target in targets must include one of the following special labels, and the label must correspond to the container or process you profile:

  • __container_id__: The container ID.
  • __meta_docker_container_id: The ID of the Docker container.
  • __meta_kubernetes_pod_container_id: The ID of the Kubernetes Pod container.
  • __process_pid__ : The process ID.

Each process is then associated with a specified target from the targets list, determined by a container ID or process PID.

If a process’s container ID matches a target’s container ID label, the component aggregates the stack traces per target by container ID. If a process’s PID matches a target’s process PID label, the component aggregates the stack traces per target by process PID. Otherwise the process isn’t profiled.

Service name

Every profile carries a service_name label. You can set it on the target yourself. If you don’t, the component adds the label and infers a value, in order, from the following sources:

  • __meta_kubernetes_pod_annotation_pyroscope_io_service_name, which is a pyroscope.io/service_name Pod annotation.
  • __meta_kubernetes_namespace and __meta_kubernetes_pod_container_name, combined into ebpf/<namespace>/<container_name>.
  • __meta_docker_container_name.
  • __meta_dockerswarm_container_label_service_name.
  • __meta_dockerswarm_service_name.

When you don’t set service_name and the component can’t infer a value, it uses unspecified.

Troubleshoot unknown symbols

The component extracts symbols from several sources, including:

  • The .gopclntab section in Go language ELF files.
  • The .symtab and .dynsym sections in the debug ELF file.
  • The .symtab and .dynsym sections in the ELF file.

The search for debug files follows the GNU Debugger algorithm. For example, if the profiler wants to find the debug file for /lib/x86_64-linux-gnu/libc.so.6 with a .gnu_debuglink set to libc.so.6.debug and a build ID 0123456789abcdef. The component examines the following paths:

  • /usr/lib/debug/.build-id/01/0123456789abcdef.debug
  • /lib/x86_64-linux-gnu/libc.so.6.debug
  • /lib/x86_64-linux-gnu/.debug/libc.so.6.debug
  • /usr/lib/debug/lib/x86_64-linux-gnu/libc.so.6.debug

Deal with unknown symbols

Unknown symbols in the profiles you’ve collected indicate that the profiler couldn’t access an ELF file associated with a given address in the trace.

This can occur for several reasons:

  • The process has terminated, making the ELF file inaccessible.
  • The ELF file is either corrupted or not recognized as an ELF file.
  • There is no corresponding ELF file entry in /proc/pid/maps for the address in the stack trace.

Address unresolved symbols

If you only see module names without corresponding function names, for example, /lib/x86_64-linux-gnu/libc.so.6, the component couldn’t map the symbols to their respective function names.

This can occur for several reasons:

  • The build process stripped the binary, leaving no .symtab, .dynsym, or .gopclntab sections in the ELF file.
  • The debug file is missing, or the component can’t locate it.

To fix this for your binaries, ensure that they’re either not stripped or that you have separate debug files available. You can achieve this by running:

Bash
objcopy --only-keep-debug elf elf.debug
strip elf -o elf.stripped
objcopy --add-gnu-debuglink=elf.debug elf.stripped elf.debuglink

For system libraries, install the debug symbols. On Ubuntu, for example, you can install debug symbols for libc by executing:

Bash
apt install libc6-dbg

Understand flat stack traces

If your profiles show many shallow stack traces, typically 1-2 frames deep, your compiler might have omitted frame pointers.

To compile your code with frame pointers, include the -fno-omit-frame-pointer flag in your compiler options.

Examples

The following examples show how to discover profiling targets and send the resulting profiles to a Pyroscope endpoint.

Kubernetes discovery

In the following example, the component collects performance profiles from Pods on the same node, discovered using discovery.kubernetes. Pod selection relies on the HOSTNAME environment variable, which is a Pod name if Alloy is used as an Alloy Helm chart. The example sets the service_name label to ebpf/{__meta_kubernetes_namespace}/{__meta_kubernetes_pod_container_name} from Kubernetes meta labels.

Alloy
discovery.kubernetes "all_pods" {
  role = "pod"
  selectors {
    field = "spec.nodeName=" + sys.env("HOSTNAME")
    role = "pod"
  }
}

discovery.relabel "local_pods" {
  targets = discovery.kubernetes.all_pods.targets
  rule {
    action = "drop"
    regex = "Succeeded|Failed"
    source_labels = ["__meta_kubernetes_pod_phase"]
  }
  rule {
    action = "replace"
    regex = "(.*)@(.*)"
    replacement = "ebpf/${1}/${2}"
    separator = "@"
    source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_container_name"]
    target_label = "service_name"
  }
  rule {
    action = "labelmap"
    regex = "__meta_kubernetes_pod_label_(.+)"
  }
  rule {
    action = "replace"
    source_labels = ["__meta_kubernetes_namespace"]
    target_label = "namespace"
  }
  rule {
    action = "replace"
    source_labels = ["__meta_kubernetes_pod_name"]
    target_label = "pod"
  }
  rule {
    action = "replace"
    source_labels = ["__meta_kubernetes_pod_node_name"]
    target_label = "node"
  }
  rule {
    action = "replace"
    source_labels = ["__meta_kubernetes_pod_container_name"]
    target_label = "container"
  }
}
pyroscope.ebpf "local_pods" {
  forward_to = [ pyroscope.write.endpoint.receiver ]
  targets = discovery.relabel.local_pods.output
}

pyroscope.write "endpoint" {
  endpoint {
    url = "http://pyroscope:4040"
  }
}

Docker discovery

The following example collects performance profiles from containers discovered by discovery.docker and ignores all other profiles collected from outside any docker container. The example sets the service_name label from the __meta_docker_container_name label.

Alloy
discovery.docker "linux" {
  host = "unix:///var/run/docker.sock"
}

discovery.relabel "local_containers" {
  targets = discovery.docker.linux.targets
  rule {
    action = "replace"
    source_labels = ["__meta_docker_container_name"]
    target_label = "service_name"
  }
}

pyroscope.write "staging" {
  endpoint {
    url = "http://pyroscope:4040"
  }
}

pyroscope.ebpf "default" {
  forward_to   = [ pyroscope.write.staging.receiver ]
  targets      = discovery.relabel.local_containers.output
}

Collect profiles from probes

Use probe_links to collect stack traces whenever something calls the kernel or user-space probes you specify. Kernel probes use the format <PROBE_TYPE>:<SYMBOL>, and user-space probes use the format <PROBE_TYPE>:<EXECUTABLE_PATH>:<SYMBOL>. Supported probe types are kprobe, kretprobe, uprobe, and uretprobe.

The following example collects profiles when the kernel calls do_sys_open or when libc calls malloc:

Alloy
pyroscope.ebpf "probes" {
	targets     = <TARGET_LIST>
	forward_to  = <RECEIVER_LIST>
	probe_links = [
		"kprobe:do_sys_open",
		"uprobe:/usr/lib/libc.so.6:malloc",
	]
}

Replace the following:

  • <TARGET_LIST>: The list of process or container targets to profile.
  • <RECEIVER_LIST>: The list of profile receivers to forward profiles to.

Correlate request profiles with Beyla traces and spans

When obi_process_context_enabled is true, pyroscope.ebpf uses a shared eBPF map under bpf_fs_root to correlate profile samples with traces generated by components compatible with OpenTelemetry eBPF Instrumentation (OBI), such as beyla.ebpf. Correlated samples include the trace_id and span_id labels. Components that share process context must use the same BPF filesystem root.

The following example uses beyla.ebpf to instrument requests to services listening on any port. It also profiles local processes and forwards the profiles to Pyroscope. Both components use their default BPF filesystem root at /sys/fs/bpf/.

Alloy
beyla.ebpf "autoinstrument" {
	discovery {
		instrument {
			open_ports = "1-65535"
		}
	}

	output {
		traces = [otelcol.processor.batch.traces.input]
	}
}

otelcol.processor.batch "traces" {
	output {
		traces = [otelcol.exporter.otlphttp.traces.input]
	}
}

otelcol.exporter.otlphttp "traces" {
	client {
		endpoint = "<OTLP_TRACES_ENDPOINT>"
	}
}

discovery.process "all" { }

discovery.relabel "profiled_processes" {
	targets = discovery.process.all.targets

	rule {
		action        = "replace"
		source_labels = ["__meta_process_exe"]
		regex         = ".*/([^/]+)$"
		replacement   = "$1"
		target_label  = "service_name"
	}
}

pyroscope.ebpf "request_profiles" {
	targets                     = discovery.relabel.profiled_processes.output
	forward_to                  = [pyroscope.write.profiles.receiver]
	bpf_fs_root                 = "/sys/fs/bpf/"
	obi_process_context_enabled = true
}

pyroscope.write "profiles" {
	endpoint {
		url = "<PYROSCOPE_URL>"
	}
}

Replace the following:

  • <OTLP_TRACES_ENDPOINT>: The OTLP HTTP endpoint to send traces to.
  • <PYROSCOPE_URL>: The URL of the Pyroscope server to send profiles to.

Compatible components

pyroscope.ebpf can accept arguments from the following components:

Note

Connecting some components may not be sensible or components may require further configuration to make the connection work correctly. Refer to the linked documentation for more details.