Skip to main content
skill CX Codex

pyroscope — Claude Code skill: continuous profiling

A skill for continuous profiling with Grafana Pyroscope: CPU, memory and allocation flame graphs read straight from the agent chat.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add grafana/skills --skill pyroscope -a claude-code
Open on GitHub

What pyroscope does

The skill lays out three instrumentation paths. A language SDK (Go, Java, Python, Ruby, Node, .NET, Rust) pushes samples to Pyroscope directly. Alloy with eBPF collects profiles with no code change. Or the SDK posts to pyroscope.receive_http and Alloy forwards them. Inside you get a Python SDK config example, a config.alloy block for cluster-wide eBPF profiling, ProfileQL queries, and the profile types: CPU, memory, allocations, goroutines, mutex.

Alloy eBPF needs root, host PID access and Linux kernel 5.8+ with BTF (or RHEL 4.18+). Grafana Cloud Profiles needs a numeric Pyroscope user (stack id) and an API key. On an older kernel or without BTF the pyroscope.ebpf component goes unhealthy with BPF errors, so check ls /sys/kernel/btf/vmlinux before rolling collection out across a cluster.

Span Profiles link a slow Tempo trace to its profile, which points at the code path behind the latency. There is a troubleshooting section too: an SDK that starts but produces no flame graph, a Cloud 401 from a wrong basic_auth_username, and profiles arriving with no frames — Java wants PYROSCOPE_FORMAT=jfr, Python on Alpine needs procfs and glibc compatibility.

What it helps with

  • find what is burning CPU from a flame graph
  • compare two profiles to catch a regression
  • deploy cluster-wide profiling with Alloy, no code change
  • correlate a slow Tempo trace to its profile
  • track down high memory and allocation use

Example prompts for the agent

  • add Pyroscope profiling to this Python service
  • set up Alloy as an eBPF profiler for the whole cluster
  • compare these two CPU profiles and show me the regression

How to install pyroscope

1Skills CLI (Vercel)

npx skills add grafana/skills --skill pyroscope -a claude-code

2Manually: put the files in place

~/.claude/skills/pyroscope/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/pyroscope/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about pyroscope

How do I install pyroscope in Claude?

Run npx skills add grafana/skills --skill pyroscope -a claude-code. The skill lands at ~/.claude/skills/pyroscope/SKILL.md.

What does the pyroscope skill do?

It helps you wire up continuous profiling with Grafana Pyroscope and read the output as flame graphs. It covers SDKs, eBPF via Alloy, ProfileQL queries and trace-to-profile linking.

What does eBPF profiling with Alloy require?

Root, host PID access and Linux kernel 5.8+ with BTF (or RHEL 4.18+). Without those the pyroscope.ebpf component reports unhealthy.

Source text

SKILL.md · Apache-2.0
---
name: pyroscope
license: Apache-2.0
description: Continuously profile applications with Grafana Pyroscope and read the result as flame graphs. Covers three instrumentation paths — language SDK push (Go / Java / Python / Ruby / Node / .NET / Rust), Alloy eBPF auto-instrumentation (no code change, requires kernel 5.8+ with BTF), and SDK → Alloy receiver — plus ProfileQL queries, profile types (CPU / memory / allocations / goroutines / mutex), Grafana Cloud Profiles endpoint, and Span Profiles trace-to-profile linking. Use when adding profiling to a service, deploying Alloy as a cluster-wide eBPF profiler, hunting CPU / memory hotspots from a flame graph, comparing two profiles to find a regression, or correlating a slow Tempo trace to its profile — even when the user says "find what's burning CPU", "flame graph this app", "continuous profiling", "heap hotspots", or "why is allocation so high" without naming Pyroscope.
---

# Grafana Pyroscope

> **Docs**: https://grafana.com/docs/pyroscope/latest/

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

1. **Alloy eBPF** (preferred) — auto-instrument, no code change
2. **SDK direct push** — application calls Pyroscope API
3. **SDK → Alloy** — SDK posts to `pyroscope.receive_http`, Alloy forwards

## Common Workflows

### 1. Instrument an app with the SDK (representative: Python)

```bash
pip install pyroscope-io==1.0.11
```

```python
import 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()
```

```bash
# 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`](references/sdks.md).

### 2. Cluster-wide eBPF profiling with Alloy

```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") }
  }
}
```

```bash
# 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`](references/ebpf-and-query.md).

## Troubleshooting

- SDK starts but no flame graph → check the app actually called `start()` / `configure()` (some SDKs are lazy); check `server_address` reachable from inside the container
- Alloy eBPF component `unhealthy` with BPF errors → kernel < 5.8 or BTF missing; `ls /sys/kernel/btf/vmlinux`
- Cloud 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, ensure `procfs` and `glibc` compatibility

## Resources

- [Pyroscope docs](https://grafana.com/docs/pyroscope/latest/)
- [Grafana Cloud Profiles](https://grafana.com/docs/grafana-cloud/monitor-applications/profiles/)
- [`references/sdks.md`](references/sdks.md) — Java/Node/Ruby/.NET/Rust install + config + env-var table + profile-type matrix
- [`references/ebpf-and-query.md`](references/ebpf-and-query.md) — full Alloy eBPF pipeline + ProfileQL

© grafana · Apache-2.0 · grafana/skills

Similar skills

All skills for DevOps and infrastructure →

From the same set51

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor