pyroscope.java
pyroscope.java continuously profiles Java processes running on the local Linux OS using async-profiler.
Note
To use the
pyroscope.javacomponent you must run Alloy as root and inside host PID namespace.
You can specify multiple pyroscope.java components by giving them different labels.
Usage
pyroscope.java "<LABEL>" {
targets = <TARGET_LIST>
forward_to = <RECEIVER_LIST>
}Target JVM configuration
When you use pyroscope.java to profile Java applications, you can configure the target JVMs with some command line flags that ensure accurate profiling, especially for inlined methods.
Add the following flags to your Java application’s startup command:
-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepointsFor more details, refer to Restrictions/Limitations in the async-profiler documentation.
Additional configuration for Linux capabilities
If your Kubernetes environment has Linux capabilities enabled, configure the following in your Helm values to ensure pyroscope.java functions properly:
alloy:
securityContext:
runAsUser: 0
runAsNonRoot: false
capabilities:
add:
- PERFMON
- SYS_PTRACE
- SYS_RESOURCE
- SYS_ADMINThese capabilities enable Alloy to access performance monitoring subsystems, trace processes, override resource limits, and perform necessary system administration tasks for profiling.
Note
Adjust capabilities based on your specific security requirements and environment, following the principle of least privilege. The capability behavior depends on Container Runtime Interface (CRI) settings. For example, Docker drops capabilities that aren’t on the allowlist by default.
Arguments
You can use the following arguments with pyroscope.java:
After component startup, pyroscope.java creates a temporary directory under the tmp_dir argument and extracts the async-profiler binaries for both glibc and musl into the directory with the following layout.
/tmp/alloy-asprof-glibc-{SHA1}/bin/asprof
/tmp/alloy-asprof-glibc-{SHA1}/lib/libasyncProfiler.so
/tmp/alloy-asprof-musl-{SHA1}/bin/asprof
/tmp/alloy-asprof-musl-{SHA1}/lib/libasyncProfiler.soAfter process profiling startup, the component detects libc type and copies the matching libasyncProfiler.so into the target process’s file system at the exact same path.
Note
The
asprofbinary runs with root permissions. If you change thetmp_dirargument to something other than/tmp, then you must ensure that the directory is only writable by root.The filesystem mounted at
tmp_dirin the Alloy and target containers must allow file execution. A mount option such asnoexecprevents this.
Each target in the targets argument must always include the special __process_pid__ label, which corresponds to the process PID that’s used for profiling.
Warning
pyroscope.javadoesn’t fail or report unhealthy if a target is missing the__process_pid__label. It logs an error and silently skips profiling that target instead.
The component treats labels that start with a double underscore as internal and removes them before scraping.
Every profile carries a service_name label.
You can set it on the target yourself.
If you don’t, pyroscope.java adds the label and infers a value from the following sources, in this order:
__meta_kubernetes_pod_annotation_pyroscope_io_service_namewhich is apyroscope.io/service_namePod annotation.__meta_kubernetes_namespaceand__meta_kubernetes_pod_container_name, combined intojava/<namespace>/<container_name>.__meta_docker_container_name__meta_dockerswarm_container_label_service_nameor__meta_dockerswarm_service_name
When you don’t set service_name and the component can’t infer a value, it uses unspecified.
Injected labels
Independently of the targets argument, pyroscope.java automatically injects the following labels into collected profiles if you haven’t already defined them:
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.
Blocks
You can use the following block with pyroscope.java:
No valid configuration blocks found.
profiling_config
The profiling_config block describes how the component invokes async-profiler.
You can use the following arguments with the profiling_config block:
Refer to profiler-options for more information about async-profiler configuration.
custom_arguments
custom_arguments passes async-profiler start flags directly.
When you set custom_arguments, Alloy skips these options from this block:
cpu, event, per_thread, sample_rate, alloc, lock, log_level, tlab.
For example, this configuration enables multi-event profiling for cpu, alloc, and lock with custom thresholds:
pyroscope.java "java" {
targets = discovery.relabel.java.output
forward_to = [pyroscope.write.staging.receiver]
profiling_config {
interval = "60s"
custom_arguments = ["-e", "cpu,alloc,lock", "--alloc", "2m", "--lock", "10ms"]
}
}Refer to Profiling modes and profiler-options for the complete async-profiler option list.
event
The event argument configures the profiling mode used by async-profiler.
async-profiler supports various profiling modes including CPU profiling, wall-clock profiling, and hardware performance monitoring events.
For a complete overview of all available profiling modes and their use cases, refer to Profiling modes in the async-profiler documentation.
per_thread
Warning
The
per_threadoption doesn’t apply when using JFR output format. Sincepyroscope.javauses JFR format exclusively, this option has no effect. For more details, refer to Options applicable to any output format except JFR in the async-profiler documentation.
Exported fields
pyroscope.java doesn’t export any fields.
Component health
pyroscope.java is only reported as unhealthy if given an invalid configuration.
In those cases, exported fields retain their last healthy values.
Debug information
pyroscope.java doesn’t expose any component-specific debug information.
Debug metrics
pyroscope.java doesn’t expose any component-specific debug metrics.
Example
The following example profiles every Java process on the current host:
pyroscope.write "staging" {
endpoint {
url = "http://localhost:4040"
}
}
discovery.process "all" {
refresh_interval = "60s"
discover_config {
cwd = true
exe = true
commandline = true
username = true
uid = true
container_id = true
}
}
discovery.relabel "java" {
targets = discovery.process.all.targets
rule {
action = "keep"
regex = ".*/java$"
source_labels = ["__meta_process_exe"]
}
}
pyroscope.java "java" {
targets = discovery.relabel.java.output
forward_to = [pyroscope.write.staging.receiver]
profiling_config {
interval = "60s"
alloc = "512k"
cpu = true
sample_rate = 100
lock = "1ms"
}
}Compatible components
pyroscope.java can accept arguments from the following components:
- Components that export Targets
- Components that export Pyroscope
ProfilesReceiver
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.


