Pyroscope
Grafana LabsOfficialPublished by Grafana Labs
- Skill
- Tool integration
- Cloud and DevOps
- Apache-2.0
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Grafana Pyroscope provides continuous profiling for applications, enabling developers to identify performance bottlenecks like CPU and memory hotspots. It supports multiple instrumentation methods, including language-specific SDKs for direct pushing and eBPF-based auto-instrumentation via Grafana Alloy for zero-code profiling. The tool generates flame graphs to visualize resource consumption and supports advanced querying via ProfileQL, allowing for regression analysis and correlation with distributed traces.
What it does
- Captures continuous profiles including CPU, memory, allocations, and mutex contention.
- Supports auto-instrumentation using eBPF via Grafana Alloy for zero-code deployment.
- Provides language-specific SDKs for Go, Java, Python, Ruby, Node.js, .NET, and Rust.
- Visualizes performance data through interactive flame graphs.
- Enables complex data filtering and analysis using the ProfileQL query language.
- Supports trace-to-profile linking to correlate slow traces with specific code execution paths.
When to use it
- When you need to identify CPU or memory hotspots in a production service.
- When you want to implement continuous profiling without modifying application code.
- When you need to compare two profiles to debug performance regressions.
- When correlating slow Tempo traces with specific code-level performance data.
- When you need to monitor resource usage across a Kubernetes cluster.
Advantages
- Reduces manual debugging time by providing visual performance insights.
- Offers flexible instrumentation options ranging from SDKs to zero-code eBPF.
- Integrates seamlessly with the Grafana ecosystem for unified observability.
- Supports deep analysis of resource usage types like goroutines and mutex contention.
Usage examples
- “How do I instrument my Python application for continuous profiling?”
- Install the pyroscope-io package and use pyroscope.configure() to set the application name and server address, allowing the app to push profiles automatically.
- “How can I profile all pods in my Kubernetes cluster without changing code?”
- Deploy Grafana Alloy as a cluster-wide agent using the pyroscope.ebpf component, which uses eBPF to collect profiles from all pods automatically.
- “How do I find CPU hotspots for my service using ProfileQL?”
- Use a query like {service_name="myapp", __profile_type__="process_cpu:cpu:nanoseconds:cpu:nanoseconds"} in the Grafana Explore interface to view the flame graph.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/grafana/pyroscope/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip pyroscope.zip -d .agents/skills
Documentation by Grafana Labs
Grafana Pyroscope
Continuous profiling — flame graphs of CPU, memory, allocations, mutex contention, goroutines.
Prerequisites
Pyroscope server (OSS) or Grafana Cloud Profiles endpoint
For Cloud: numeric Pyroscope user (stack id) + API key
For eBPF via Alloy: root + host PID + Linux ≥ 5.8 with BTF (or RHEL 4.18+)
Instrumentation paths
Alloy eBPF (preferred) — auto-instrument, no code change
SDK direct push — application calls Pyroscope API
SDK → Alloy — SDK posts to
pyroscope.receive_http, Alloy forwards
Common Workflows
1. Instrument an app with the SDK (representative: Python)
pip install pyroscope-io==1.0.11import pyroscope, os
pyroscope.configure(
application_name="my.python.app",
server_address="http://pyroscope:4040",
sample_rate=100, oncpu=True,
tags={"region": os.getenv("REGION"), "env": "prod"},
)
# Dynamic tag for a hot path
with pyroscope.tag_wrapper({"controller": "slow_controller"}):
slow_code()# Verify the app is pushing — Pyroscope ingests samples in a few seconds
curl -s http://pyroscope:4040/ready # → "ready"
curl -s http://pyroscope:4040/api/v1/labels | jq '.data | index("service_name")' # → not null
# Verify the service shows up in Grafana → Explore → Profiles → service dropdown.Other SDKs (Java agent, Node, Ruby, .NET, Rust) + Cloud auth + tunable env vars: references/sdks.md.
2. Cluster-wide eBPF profiling with Alloy
# config.alloy — full block in references/ebpf-and-query.md
pyroscope.ebpf "local_pods" {
forward_to = [pyroscope.write.cloud.receiver]
targets = discovery.relabel.local_pods.output
sample_rate = 97
collect_interval = "15s"
}
pyroscope.write "cloud" {
endpoint {
url = "https://profiles-prod-xxx.grafana.net"
basic_auth { username = sys.env("PYROSCOPE_USER")
password = sys.env("GRAFANA_API_KEY") }
}
}# 1. Reload Alloy
curl -X POST http://localhost:12345/-/reload
# 2. Verify the eBPF component is healthy
curl -s http://localhost:12345/api/v0/web/components \
| jq '.[] | select(.id|contains("pyroscope.ebpf")) | {id,health:.health.state}'
# Expect: health.state == "healthy"
# 3. Verify profiles arriving in Pyroscope
# Grafana → Explore → Profiles datasource → query:
# {namespace="default", __profile_type__="process_cpu:cpu:nanoseconds:cpu:nanoseconds"}
# Expect flame graph to render with frames from the target pods.3. Query with ProfileQL
{service_name="myapp", env="prod",
__profile_type__="process_cpu:cpu:nanoseconds:cpu:nanoseconds"}Profile-type list + full ProfileQL grammar: references/ebpf-and-query.md.
Troubleshooting
SDK starts but no flame graph → check the app actually called
start()/configure()(some SDKs are lazy); checkserver_addressreachable from inside the containerAlloy eBPF component
unhealthywith BPF errors → kernel < 5.8 or BTF missing;ls /sys/kernel/btf/vmlinuxCloud push 401 → wrong
basic_auth_username(must be the numeric stack id, not the slug)Profile shows up but with no frames → for Java, set
PYROSCOPE_FORMAT=jfr; for Python on Alpine, ensureprocfsandglibccompatibility
Resources
references/sdks.md— Java/Node/Ruby/.NET/Rust install + config + env-var table + profile-type matrixreferences/ebpf-and-query.md— full Alloy eBPF pipeline + ProfileQL
Files
- ATTRIBUTION.mdAdded by AutoGineer838 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md4.7 KiB
- references/ebpf-and-query.md1.7 KiB
- references/sdks.md4.1 KiB
references/sdks.md
Pyroscope SDKs by language
Each SDK pushes profiles to server_address. For Grafana Cloud Profiles, use the basic-auth form with the Pyroscope user (numeric stack id) + a Grafana Cloud API key.
Python
pip install pyroscope-io==1.0.11import pyroscope, os
pyroscope.configure(
application_name="my.python.app",
server_address="http://pyroscope:4040",
sample_rate=100, oncpu=True,
tags={"region": os.getenv("REGION"), "env": "prod"},
)
with pyroscope.tag_wrapper({"controller": "slow_controller"}):
slow_code()Grafana Cloud:
pyroscope.configure(
application_name="my.python.app",
server_address="https://profiles-prod-xxx.grafana.net",
basic_auth_username="123456",
basic_auth_password=os.getenv("GRAFANA_API_KEY"),
)Java
<!-- Maven -->
<dependency>
<groupId>io.pyroscope</groupId>
<artifactId>agent</artifactId>
<version>2.1.2</version>
</dependency>PyroscopeAgent.start(
new Config.Builder()
.setApplicationName("my-java-app")
.setProfilingEvent(EventType.ITIMER)
.setFormat(Format.JFR)
.setServerAddress("http://pyroscope:4040")
.build()
);
Pyroscope.LabelsWrapper.run(new LabelsSet("controller", "slow_controller"), () -> {
slowCode();
});Or Java agent (no code change):
export PYROSCOPE_APPLICATION_NAME=my.java.app
export PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040
java -javaagent:pyroscope.jar -jar app.jar| Env var | Description | Default |
|---|---|---|
PYROSCOPE_FORMAT | jfr for multiple events | collapsed |
PYROSCOPE_PROFILER_EVENT | itimer, cpu, wall | itimer |
PYROSCOPE_PROFILER_ALLOC | Allocation bytes threshold; 0 = all | disabled |
PYROSCOPE_PROFILER_LOCK | Lock contention threshold (ns) | disabled |
PYROSCOPE_UPLOAD_INTERVAL | Upload frequency | 10s |
Node.js
npm install @pyroscope/nodejsconst Pyroscope = require('@pyroscope/nodejs');
Pyroscope.init({
serverAddress: 'http://pyroscope:4040',
appName: 'my-node-service',
tags: { region: process.env.REGION },
basicAuthUser: process.env.PYROSCOPE_USER,
basicAuthPassword: process.env.PYROSCOPE_PASSWORD,
flushIntervalMs: 60000,
});
Pyroscope.start();
Pyroscope.wrapWithLabels({ vehicle: 'bike' }, () => slowCode());Ruby
bundle add pyroscoperequire 'pyroscope'
Pyroscope.configure do |config|
config.application_name = "my.ruby.app"
config.server_address = "http://pyroscope:4040"
config.tags = { hostname: ENV["HOSTNAME"] }
end
Pyroscope.tag_wrapper({ controller: "slow_controller" }) { slow_code }.NET (Linux amd64, .NET 6+)
export PYROSCOPE_APPLICATION_NAME=my.dotnet.app
export PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040
export PYROSCOPE_PROFILING_ENABLED=1
export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER={BD1A650D-AC5D-4896-B64F-D6FA25D6B26A}
export CORECLR_PROFILER_PATH=/dotnet/Pyroscope.Profiler.Native.so
export LD_PRELOAD=/dotnet/Pyroscope.Linux.ApiWrapper.x64.sovar labels = Pyroscope.LabelSet.Empty.BuildUpon().Add("controller", "slow").Build();
Pyroscope.LabelsWrapper.Do(labels, () => SlowCode());Rust
cargo add pyroscope pyroscope_pprofrslet pprof_config = PprofConfig::new().sample_rate(100);
let agent = PyroscopeAgent::builder("http://pyroscope:4040", "my-rust-app")
.backend(pprof_backend(pprof_config))
.tags([("env", "prod"), ("region", "us-east")].to_vec())
.basic_auth(user, password)
.build()?;
let running = agent.start().unwrap();
// ...
let ready = running.stop().unwrap();
ready.shutdown();Profile types by language
| Language | CPU | Memory | Goroutines | Allocations |
|---|---|---|---|---|
| Go | ✓ | ✓ | ✓ | ✓ |
| Java | ✓ | ✓ | ✓ | ✓ |
| Python | ✓ | - | - | - |
| Node.js | ✓ | ✓ | - | ✓ |
| Ruby | ✓ | ✓ | - | - |
| .NET | ✓ | ✓ | - | ✓ |
| Rust | ✓ | - | - | - |
| eBPF | ✓ | - | - | - |
Valid tag chars: [a-zA-Z_][a-zA-Z0-9_]* — periods NOT allowed.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs network access, Needs an API key, Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Grafana Labs
- Publisher
- Grafana Labs (Official)
- Source
- grafana/skills · skills/grafana-lgtm/pyroscope · commit 8fb72a5
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Grafana Labs.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download pyroscope.zip