This document provides an overview of the major components that comprise Tempo. Please refer to the examples to see some deployment options.

Tempo Architecture



Accepts spans in multiple formats including Jaeger, OpenTelemetry, Zipkin. Routes spans to ingesters by hashing the traceID and using a distributed consistent hash ring.

The distributor uses the receiver layer from the OpenTelemetry Collector. For best performance it is recommended to ingest OTel Proto. For this reason the Grafana Agent uses the otlp exporter/receiver to send spans to Tempo.


Batches traces into blocks, blooms, indexes and flushes to backend. Blocks in the backend are generated in the following layout.

<bucketname> / <tenantID> / <blockID> / <meta.json>
.                                     / <bloom>
.                                     / <index>
.                                     / <data>

Query Frontend

Responsible for sharding the search space for an incoming query.

Traces are exposed via a simple HTTP endpoint: GET /api/traces/<traceID>

Internally, the Query Frontend splits the blockID space into a configurable number of shards and queues these requests. Queriers connect to the Query Frontend via a streaming gRPC connection to process these sharded queries.


The querier is responsible for finding the requested trace id in either the ingesters or the backend storage. It begins by querying the ingesters to see if the id is currently stored there, if not it proceeds to use the bloom and indexes to find the trace in the storage backend.

The querier exposes an HTTP endpoint at: GET /querier/api/traces/<traceID>, but its not expected to be used directly.

Queries should be sent to the Query Frontend.


Compactors stream blocks to and from the backend storage to reduce the total number of blocks.

Grafana 7.4.x

When using older versions of Grafana you must also use tempo-query in order to visualize traces. tempo-query is Jaeger Query with a GRPC Plugin that allows it to query Tempo. See this example and these docs for help.