# grafana/pyroscope architecture

> Architecture diagram of the GitHub repository grafana/pyroscope, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-01). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/grafana/pyroscope
- Repository: https://github.com/grafana/pyroscope

## Overview

#### Purpose and entry points
Grafana Pyroscope is a Go-based, multi-tenant continuous-profiling backend. It receives pprof/Pyroscope and OTLP profiles, persists and compacts them, and serves profile-analysis queries for Grafana and its embedded UI. The primary server binary is `cmd/pyroscope/main.go`, which initializes the module graph in `pkg/pyroscope/pyroscope.go` and `pkg/pyroscope/modules.go`. `cmd/profilecli/main.go` is the operational/query CLI.

#### Deployment and module composition
One binary supports single-binary and microservices deployment modes; Helm renders both, including v2 variants: `operations/pyroscope/helm/pyroscope/templates/unified-services.yaml`, `operations/pyroscope/helm/pyroscope/rendered/micro-services-v2.yaml`, and `operations/pyroscope/helm/pyroscope/rendered/single-binary-v2.yaml`. Modules expose HTTP and Connect/gRPC-style APIs, use tenant middleware, rings/service discovery where required, and can be independently scaled in microservice mode.

#### Ingestion and v2 write path
Clients/Alloy/OpenTelemetry send profiles through API handlers and the distributor. The distributor applies tenancy, validation/limits, sampling/aggregation, and routes writes by service: `pkg/distributor/distributor.go`, `pkg/distributor/writepath/router.go`, `pkg/distributor/aggregator/aggregator.go`. In default v2, profiles are sent to segment writers rather than resident ingesters; segment writers maintain a short-lived in-memory segment and write it directly to object storage: `pkg/segmentwriter/service.go`, `pkg/segmentwriter/segment.go`, `pkg/segmentwriter/memdb/head.go`.

#### Storage, metadata, and compaction
The v2 durable boundary is object storage. Storage adapters support filesystem, S3, GCS, Azure, Swift, and COS through `pkg/objstore/client/factory.go`, `pkg/objstore/providers/s3/bucket_client.go`, and `pkg/objstore/providers/gcs/bucket_client.go`. Segments and compacted blocks use a block format with profile data, TSDB indexing, Parquet, and symbol databases: `pkg/block/block.go`, `pkg/phlaredb/schemas/v1/profiles.go`, `pkg/phlaredb/symdb/symdb.go`.

The metastore maintains distributed metadata/index/compaction state using Raft and BoltDB-backed FSM persistence: `pkg/metastore/metastore.go`, `pkg/metastore/raftnode/node.go`, `pkg/metastore/fsm/boltdb.go`. It schedules work; compaction workers merge segments into larger blocks and publish metadata: `pkg/metastore/compaction/scheduler/scheduler.go`, `pkg/compactionworker/worker.go`, `pkg/block/compaction.go`.

#### Read path and query execution
The query frontend accepts profile, labels, time-series, diff, heatmap, and stacktrace requests, splits/routs work and merges responses: `pkg/frontend/readpath/read_path.go`, `pkg/frontend/readpath/queryfrontend/query_frontend.go`, `pkg/frontend/frontend_select_merge_profile.go`. The query backend obtains relevant blocks from metastore metadata, reads object-store blocks, executes parallel plans, and aggregates profile trees/reports: `pkg/querybackend/backend.go`, `pkg/querybackend/queryplan/query_plan.go`, `pkg/querybackend/block_reader.go`. Store gateway provides sharded/cached access to persisted blocks: `pkg/storegateway/gateway.go`, `pkg/storegateway/query.go`.

#### Legacy v1 compatibility
The repository retains the v1 ingestion/query architecture for migration and compatibility. Distributors route to replicated, ring-managed ingesters; ingesters retain/query local head data and ship blocks; queriers combine ingester and store-gateway results. Core paths are `pkg/ingester/ingester.go`, `pkg/querier/querier.go`, and `pkg/phlaredb/shipper/shipper.go`. This is architecturally distinct from v2’s direct-to-object-store write path.

#### Interfaces and presentation
Public contracts are protobuf-first and generated into Connect bindings/OpenAPI documents: `api/push/v1/push.proto`, `api/query/v1/query.proto`, and `api/gen/proto/go/query/v1/queryv1connect/query.connect.go`. Server route registration and legacy HTTP surface are centralized in `pkg/api/api.go`. The React/TypeScript UI is built separately, embedded into the Go server, and calls these APIs: `ui/src/main.tsx`, `ui/src/api/client.ts`, and `ui/assets_embedded.go`. Optional source lookup integrates with GitHub and debuginfod-based symbol resolution: `pkg/frontend/vcs/github.go`, `pkg/symbolizer/debuginfod_client.go`.

Main entry points: [`pkg/api/api.go`](https://github.com/grafana/pyroscope/blob/main/pkg/api/api.go) (API routes), [`pkg/objstore/client/factory.go`](https://github.com/grafana/pyroscope/blob/main/pkg/objstore/client/factory.go) (Object storage), [`pkg/metastore/metastore.go`](https://github.com/grafana/pyroscope/blob/main/pkg/metastore/metastore.go) (Metastore), [`pkg/distributor/distributor.go`](https://github.com/grafana/pyroscope/blob/main/pkg/distributor/distributor.go) (Distributor)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_entry["Entrypoints"]
  node_server["Pyroscope server<br/>Go binary<br/>[main.go]"]
  node_modules{{"Module graph<br/>module wiring<br/>[modules.go]"}}
  node_profilecli["profilecli<br/>operations CLI<br/>[main.go]"]
  node_deployment["Helm deployment modes<br/>single binary and microservices"]
end

subgraph group_v2write["V2 write and storage"]
  node_distributor["Distributor<br/>tenant write router<br/>[distributor.go]"]
  node_write_router{{"V2 write router<br/>write-path routing<br/>[router.go]"}}
  node_segment_writer["Segment writer<br/>short-lived writer<br/>[service.go]"]
  node_object_store[("Object storage<br/>durable block store<br/>[factory.go]")]
  node_block_format["Block format<br/>profiles, index, Parquet, symbols<br/>[block.go]"]
  node_metastore[("Metastore<br/>Raft metadata service<br/>[metastore.go]")]
  node_compaction["Compaction workers<br/>block compaction<br/>[worker.go]"]
end

subgraph group_query["Read and query"]
  node_query_frontend["Query frontend<br/>request split and merge<br/>[query_frontend.go]"]
  node_query_backend["Query backend<br/>parallel query execution<br/>[backend.go]"]
  node_store_gateway["Store gateway<br/>sharded block reader<br/>[gateway.go]"]
end

subgraph group_legacy["V1 compatibility"]
  node_v1_ingester["V1 ingester<br/>ring-managed ingester<br/>[ingester.go]"]
  node_v1_querier["V1 querier<br/>legacy query service<br/>[querier.go]"]
end

subgraph group_interfaces["Contracts and UI"]
  node_api["API routes<br/>HTTP and Connect API<br/>[api.go]"]
  node_contracts["Profile query contracts<br/>protobuf and Connect<br/>[query.proto]"]
  node_ui["Embedded React UI<br/>TypeScript frontend<br/>[main.tsx]"]
end

node_deployment -->|"deploys"| node_server
node_server -->|"initializes"| node_modules
node_modules -->|"registers"| node_api
node_profilecli -->|"queries"| node_api
node_ui -->|"calls"| node_api
node_contracts -->|"defines"| node_api
node_api -->|"ingest requests"| node_distributor
node_distributor -->|"routes v2 writes"| node_write_router
node_write_router -->|"assigns"| node_segment_writer
node_segment_writer -->|"writes segments"| node_object_store
node_segment_writer -->|"publishes metadata"| node_metastore
node_object_store -->|"stores"| node_block_format
node_metastore -->|"schedules"| node_compaction
node_compaction -->|"writes compacted blocks"| node_object_store
node_compaction -->|"publishes blocks"| node_metastore
node_api -->|"analysis requests"| node_query_frontend
node_query_frontend -->|"splits and routes"| node_query_backend
node_query_backend -->|"looks up blocks"| node_metastore
node_query_backend -->|"reads blocks"| node_store_gateway
node_store_gateway -->|"loads cached shards"| node_object_store
node_distributor -.->|"legacy replicated writes"| node_v1_ingester
node_v1_ingester -.->|"ships blocks"| node_object_store
node_v1_querier -.->|"reads head data"| node_v1_ingester
node_v1_querier -.->|"reads persisted data"| node_store_gateway
node_api -.->|"legacy queries"| node_v1_querier

click node_server "https://github.com/grafana/pyroscope/blob/main/cmd/pyroscope/main.go"
click node_modules "https://github.com/grafana/pyroscope/blob/main/pkg/pyroscope/modules.go"
click node_profilecli "https://github.com/grafana/pyroscope/blob/main/cmd/profilecli/main.go"
click node_api "https://github.com/grafana/pyroscope/blob/main/pkg/api/api.go"
click node_contracts "https://github.com/grafana/pyroscope/blob/main/api/query/v1/query.proto"
click node_ui "https://github.com/grafana/pyroscope/blob/main/ui/src/main.tsx"
click node_distributor "https://github.com/grafana/pyroscope/blob/main/pkg/distributor/distributor.go"
click node_write_router "https://github.com/grafana/pyroscope/blob/main/pkg/distributor/writepath/router.go"
click node_segment_writer "https://github.com/grafana/pyroscope/blob/main/pkg/segmentwriter/service.go"
click node_object_store "https://github.com/grafana/pyroscope/blob/main/pkg/objstore/client/factory.go"
click node_block_format "https://github.com/grafana/pyroscope/blob/main/pkg/block/block.go"
click node_metastore "https://github.com/grafana/pyroscope/blob/main/pkg/metastore/metastore.go"
click node_compaction "https://github.com/grafana/pyroscope/blob/main/pkg/compactionworker/worker.go"
click node_query_frontend "https://github.com/grafana/pyroscope/blob/main/pkg/frontend/readpath/queryfrontend/query_frontend.go"
click node_query_backend "https://github.com/grafana/pyroscope/blob/main/pkg/querybackend/backend.go"
click node_store_gateway "https://github.com/grafana/pyroscope/blob/main/pkg/storegateway/gateway.go"
click node_v1_ingester "https://github.com/grafana/pyroscope/blob/main/pkg/ingester/ingester.go"
click node_v1_querier "https://github.com/grafana/pyroscope/blob/main/pkg/querier/querier.go"
click node_deployment "https://github.com/grafana/pyroscope/blob/main/operations/pyroscope/helm/pyroscope/templates/unified-services.yaml"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_server,node_modules,node_profilecli,node_deployment toneBlue
class node_distributor,node_write_router,node_segment_writer,node_object_store,node_block_format,node_metastore,node_compaction toneAmber
class node_query_frontend,node_query_backend,node_store_gateway toneMint
class node_v1_ingester,node_v1_querier toneRose
class node_api,node_contracts,node_ui toneIndigo
```

## Components

### Entrypoints

- [Pyroscope server](https://github.com/grafana/pyroscope/blob/main/cmd/pyroscope/main.go): Go binary · Runs selected modules in monolithic or microservice mode. · `cmd/pyroscope/main.go`
- [Module graph](https://github.com/grafana/pyroscope/blob/main/pkg/pyroscope/modules.go): module wiring · `pkg/pyroscope/modules.go`
- [profilecli](https://github.com/grafana/pyroscope/blob/main/cmd/profilecli/main.go): operations CLI · `cmd/profilecli/main.go`
- [Helm deployment modes](https://github.com/grafana/pyroscope/blob/main/operations/pyroscope/helm/pyroscope/templates/unified-services.yaml): single binary and microservices · `operations/pyroscope/helm/pyroscope/templates/unified-services.yaml`

### V2 write and storage

- [Distributor](https://github.com/grafana/pyroscope/blob/main/pkg/distributor/distributor.go): tenant write router · Applies limits, sampling, aggregation, and service routing. · `pkg/distributor/distributor.go`
- [V2 write router](https://github.com/grafana/pyroscope/blob/main/pkg/distributor/writepath/router.go): write-path routing · `pkg/distributor/writepath/router.go`
- [Segment writer](https://github.com/grafana/pyroscope/blob/main/pkg/segmentwriter/service.go): short-lived writer · Builds an in-memory segment and writes it directly to object storage. · `pkg/segmentwriter/service.go`
- [Object storage](https://github.com/grafana/pyroscope/blob/main/pkg/objstore/client/factory.go): durable block store · Filesystem and cloud bucket providers implement the durable boundary. · `pkg/objstore/client/factory.go`
- [Block format](https://github.com/grafana/pyroscope/blob/main/pkg/block/block.go): profiles, index, Parquet, symbols · `pkg/block/block.go`
- [Metastore](https://github.com/grafana/pyroscope/blob/main/pkg/metastore/metastore.go): Raft metadata service · Persists distributed block metadata, index state, and compaction state. · `pkg/metastore/metastore.go`
- [Compaction workers](https://github.com/grafana/pyroscope/blob/main/pkg/compactionworker/worker.go): block compaction · Merge segments into blocks and publish metadata. · `pkg/compactionworker/worker.go`

### Read and query

- [Query frontend](https://github.com/grafana/pyroscope/blob/main/pkg/frontend/readpath/queryfrontend/query_frontend.go): request split and merge · Routes profile-analysis requests and merges partial responses. · `pkg/frontend/readpath/queryfrontend/query_frontend.go`
- [Query backend](https://github.com/grafana/pyroscope/blob/main/pkg/querybackend/backend.go): parallel query execution · Plans block reads and aggregates profile trees and reports. · `pkg/querybackend/backend.go`
- [Store gateway](https://github.com/grafana/pyroscope/blob/main/pkg/storegateway/gateway.go): sharded block reader · Provides cached access to persisted blocks. · `pkg/storegateway/gateway.go`

### V1 compatibility

- [V1 ingester](https://github.com/grafana/pyroscope/blob/main/pkg/ingester/ingester.go): ring-managed ingester · Retains local head data and ships blocks. · `pkg/ingester/ingester.go`
- [V1 querier](https://github.com/grafana/pyroscope/blob/main/pkg/querier/querier.go): legacy query service · Combines ingester and store-gateway results. · `pkg/querier/querier.go`

### Contracts and UI

- [API routes](https://github.com/grafana/pyroscope/blob/main/pkg/api/api.go): HTTP and Connect API · `pkg/api/api.go`
- [Profile query contracts](https://github.com/grafana/pyroscope/blob/main/api/query/v1/query.proto): protobuf and Connect · `api/query/v1/query.proto`
- [Embedded React UI](https://github.com/grafana/pyroscope/blob/main/ui/src/main.tsx): TypeScript frontend · `ui/src/main.tsx`

## Connections

- Helm deployment modes → Pyroscope server: deploys
- Pyroscope server → Module graph: initializes
- Module graph → API routes: registers
- profilecli → API routes: queries
- Embedded React UI → API routes: calls
- Profile query contracts → API routes: defines
- API routes → Distributor: ingest requests
- Distributor → V2 write router: routes v2 writes
- V2 write router → Segment writer: assigns
- Segment writer → Object storage: writes segments
- Segment writer → Metastore: publishes metadata
- Object storage → Block format: stores
- Metastore → Compaction workers: schedules
- Compaction workers → Object storage: writes compacted blocks
- Compaction workers → Metastore: publishes blocks
- API routes → Query frontend: analysis requests
- Query frontend → Query backend: splits and routes
- Query backend → Metastore: looks up blocks
- Query backend → Store gateway: reads blocks
- Store gateway → Object storage: loads cached shards
- Distributor → V1 ingester: legacy replicated writes
- V1 ingester → Object storage: ships blocks
- V1 querier → V1 ingester: reads head data
- V1 querier → Store gateway: reads persisted data
- API routes → V1 querier: legacy queries
