<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tempo architecture reference on Grafana Labs</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/</link><description>Recent content in Tempo architecture reference on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/tempo/v3.0.x/reference-tempo-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>About the Tempo architecture</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/about-tempo-architecture/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/about-tempo-architecture/</guid><content><![CDATA[&lt;h1 id=&#34;about-the-tempo-architecture&#34;&gt;About the Tempo architecture&lt;/h1&gt;
&lt;p&gt;Grafana Tempo is a distributed tracing backend designed for high-volume trace ingestion and querying at scale.
Tempo 3.0 introduces a new architecture that decouples the write and read paths.
In 
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/&#34;&gt;microservices mode&lt;/a&gt;, a Kafka-compatible message queue serves as a durable intermediary between the distributor and downstream consumers.
In 
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/&#34;&gt;monolithic mode&lt;/a&gt;, the distributor pushes data directly to the live-store in-process without Kafka.&lt;/p&gt;
&lt;h2 id=&#34;design-philosophy&#34;&gt;Design philosophy&lt;/h2&gt;
&lt;p&gt;The Tempo architecture is built around several key principles.&lt;/p&gt;
&lt;p&gt;Separate components handle writing trace data to storage and serving queries.
You can scale writes and reads independently, and a failure in one path doesn&amp;rsquo;t affect the other.&lt;/p&gt;
&lt;p&gt;In microservices mode, Kafka serves as a durable write-ahead log (WAL) between distributors and downstream consumers.
Once Kafka acknowledges a write, the data is safe.
This replaces the previous in-process ingestion WAL that lived on local disks.
Because Kafka provides durability on the write path, Tempo doesn&amp;rsquo;t need to replicate data across multiple instances.
This replication factor of 1 significantly reduces cost and query complexity.&lt;/p&gt;
&lt;p&gt;In monolithic mode, no Kafka is required. The distributor pushes trace data in-process directly to the live-store and metrics-generator. Live-stores still use a local WAL for quickly available search.&lt;/p&gt;
&lt;p&gt;Tempo uses Apache Parquet as the default columnar block format, storing trace data in a columnar layout that enables efficient querying of specific attributes without reading entire traces.&lt;/p&gt;
&lt;p&gt;Refer to 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/parquet/&#34;&gt;Apache Parquet block format configuration&lt;/a&gt; and 
    &lt;a href=&#34;/docs/tempo/v3.0.x/operations/schema/&#34;&gt;Apache Parquet schema&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id=&#34;write-path&#34;&gt;Write path&lt;/h2&gt;
&lt;p&gt;The write path gets trace data from instrumented applications into long-term object storage. How data flows through the write path depends on the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/&#34;&gt;deployment mode&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;microservices-write-path&#34;&gt;Microservices write path&lt;/h3&gt;
&lt;p align=&#34;center&#34;&gt;&lt;img src=&#34;tempo-write-path.svg&#34; alt=&#34;Tempo write path: Application to Distributor to Kafka to Block-builder to Object storage&#34;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Distributors receive trace data over OTLP or other supported protocols,
validate it against rate limits, shard traces by trace ID, and write records to Kafka partitions.&lt;/li&gt;
&lt;li&gt;Kafka durably stores the records.
The write is acknowledged to the client as soon as Kafka confirms receipt.&lt;/li&gt;
&lt;li&gt;Block-builders consume records from Kafka, organize spans into blocks in Apache Parquet format,
and flush those blocks to object storage.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The block-builder operates on a consumption cycle: it reads a batch of records from Kafka,
builds blocks from them, flushes the blocks to object storage, and commits the offset back to Kafka.
Each cycle produces a clean cut of data.
Traces that span multiple cycles have their spans split across blocks, which the query path handles at query time.&lt;/p&gt;
&lt;h3 id=&#34;monolithic-write-path&#34;&gt;Monolithic write path&lt;/h3&gt;
&lt;p&gt;In monolithic mode, no Kafka or block-builder is involved. The distributor pushes trace data in-process directly to the live-store and metrics-generator. The live-store holds traces in memory, flushes them to a local WAL, cuts them into completed blocks, and flushes those completed blocks to the configured storage backend.&lt;/p&gt;
&lt;h2 id=&#34;read-path&#34;&gt;Read path&lt;/h2&gt;
&lt;p&gt;The read path serves queries by combining recent data from live-stores with historical data from object storage.&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;&lt;img src=&#34;tempo-read-path.svg&#34; alt=&#34;Tempo read path: Client to Query frontend to Querier, branching to Live-store and Object storage&#34;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The query frontend receives a query, shards it into parallel jobs, and distributes them to queriers.&lt;/li&gt;
&lt;li&gt;Queriers execute jobs by fetching data from two sources:
live-stores for recent data, typically the last 30 minutes to 1 hour, and object storage for historical data,
using bloom filters and indexes for efficient block lookups.&lt;/li&gt;
&lt;li&gt;The query frontend merges results from all queriers and returns the response.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;live-stores-and-the-recent-data-window&#34;&gt;Live-stores and the recent data window&lt;/h2&gt;
&lt;p&gt;Live-stores are the read-path component responsible for serving recent trace data.
They hold traces in memory and write them to temporary on-disk blocks, making data available for queries within seconds of ingestion.&lt;/p&gt;
&lt;p&gt;In microservices mode, live-stores consume from Kafka independently of block-builders.
There&amp;rsquo;s a gap between when trace data is written to Kafka and when the block-builder flushes it to object storage.
During this window, the only way to query that data is through the live-store.&lt;/p&gt;
&lt;p&gt;In monolithic mode, live-stores receive trace data directly from the distributor in-process. There is no Kafka consumption or block-builder involved.&lt;/p&gt;
&lt;p&gt;In microservices mode, live-stores own the partition lifecycle within Tempo.
They manage a partition ring that tracks which partitions are active and which live-stores own them.
This is separate from Kafka&amp;rsquo;s internal partition management.
Refer to the &lt;a href=&#34;../partition-ring/&#34;&gt;partition ring&lt;/a&gt; documentation for details.&lt;/p&gt;
&lt;h2 id=&#34;how-the-paths-connect&#34;&gt;How the paths connect&lt;/h2&gt;
&lt;p&gt;The write and read paths connect through object storage. In microservices mode, block-builders write blocks there; in monolithic mode, live-stores write blocks there. Queriers read from object storage in both modes.&lt;/p&gt;
&lt;p&gt;In microservices mode, Kafka also connects the paths. Both block-builders and live-stores consume from the same Kafka partitions, but they track their own consumer offsets independently. Even if a block-builder is down or slow, live-stores continue serving recent data. If a live-store restarts, it replays from Kafka to rebuild its in-memory state.&lt;/p&gt;
&lt;p&gt;In monolithic mode, the connection is simpler. The distributor pushes data directly to the live-store, which flushes blocks to object storage. The querier reads from both the live-store and object storage within the same process.&lt;/p&gt;
&lt;h2 id=&#34;component-summary&#34;&gt;Component summary&lt;/h2&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Component&lt;/th&gt;
              &lt;th&gt;Path&lt;/th&gt;
              &lt;th&gt;Microservices mode&lt;/th&gt;
              &lt;th&gt;Monolithic mode&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/distributor/&#34;&gt;Distributor&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Write&lt;/td&gt;
              &lt;td&gt;Receives traces, validates limits, writes to Kafka&lt;/td&gt;
              &lt;td&gt;Receives traces, validates limits, pushes in-process to live-store&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/kafka/&#34;&gt;Kafka&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Write&lt;/td&gt;
              &lt;td&gt;Durable message queue between distributor and consumers&lt;/td&gt;
              &lt;td&gt;Not used&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/block-builder/&#34;&gt;Block-builder&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Write&lt;/td&gt;
              &lt;td&gt;Consumes from Kafka, builds Parquet blocks, flushes to object storage&lt;/td&gt;
              &lt;td&gt;Not used&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/live-store/&#34;&gt;Live-store&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Read&lt;/td&gt;
              &lt;td&gt;Consumes from Kafka, serves recent data to queriers&lt;/td&gt;
              &lt;td&gt;Receives data from distributor, serves recent data to queriers&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/query-frontend/&#34;&gt;Query frontend&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Read&lt;/td&gt;
              &lt;td&gt;Shards queries into jobs, distributes to queriers, merges results&lt;/td&gt;
              &lt;td&gt;Same&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/querier/&#34;&gt;Querier&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Read&lt;/td&gt;
              &lt;td&gt;Executes query jobs against live-stores and object storage&lt;/td&gt;
              &lt;td&gt;Same&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/compaction/&#34;&gt;Backend scheduler/worker&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Maintenance&lt;/td&gt;
              &lt;td&gt;Compacts and deduplicates blocks, enforces retention&lt;/td&gt;
              &lt;td&gt;Same&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;a href=&#34;../components/metrics-generator/&#34;&gt;Metrics-generator&lt;/a&gt;&lt;/td&gt;
              &lt;td&gt;Optional&lt;/td&gt;
              &lt;td&gt;Consumes from Kafka, derives metrics from traces&lt;/td&gt;
              &lt;td&gt;Receives data from distributor, derives metrics from traces&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;]]></content><description>&lt;h1 id="about-the-tempo-architecture">About the Tempo architecture&lt;/h1>
&lt;p>Grafana Tempo is a distributed tracing backend designed for high-volume trace ingestion and querying at scale.
Tempo 3.0 introduces a new architecture that decouples the write and read paths.
In
&lt;a href="/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/">microservices mode&lt;/a>, a Kafka-compatible message queue serves as a durable intermediary between the distributor and downstream consumers.
In
&lt;a href="/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/">monolithic mode&lt;/a>, the distributor pushes data directly to the live-store in-process without Kafka.&lt;/p></description></item><item><title>Components</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/components/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/components/</guid><content><![CDATA[&lt;h1 id=&#34;components&#34;&gt;Components&lt;/h1&gt;
&lt;p&gt;Tempo is composed of several components, each responsible for a specific part of the trace lifecycle.
All components are compiled into the same binary, and the &lt;code&gt;-target&lt;/code&gt; parameter controls which component runs.&lt;/p&gt;
&lt;p&gt;This section documents each component in detail, including its responsibilities, configuration, failure modes,
and relevant metrics.&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/distributor/&#34;&gt;Distributor&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/kafka/&#34;&gt;Kafka&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/block-builder/&#34;&gt;Block-builder&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/live-store/&#34;&gt;Live-store&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/query-frontend/&#34;&gt;Query frontend&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/querier/&#34;&gt;Querier&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/compaction/&#34;&gt;Compaction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/reference-tempo-architecture/components/metrics-generator/&#34;&gt;Metrics-generator&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
]]></content><description>&lt;h1 id="components">Components&lt;/h1>
&lt;p>Tempo is composed of several components, each responsible for a specific part of the trace lifecycle.
All components are compiled into the same binary, and the &lt;code>-target&lt;/code> parameter controls which component runs.&lt;/p></description></item><item><title>Partition ring</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/partition-ring/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/partition-ring/</guid><content><![CDATA[&lt;h1 id=&#34;partition-ring&#34;&gt;Partition ring&lt;/h1&gt;
&lt;p&gt;The partition ring is the mechanism Tempo uses to track which partitions exist, their current state, and which components own them.
By default, the partition ring propagates across the cluster via memberlist gossip and is central to how distributors, live-stores, and block-builders coordinate.&lt;/p&gt;
&lt;h2 id=&#34;tempo-partitions-vs-kafka-partitions&#34;&gt;Tempo partitions vs Kafka partitions&lt;/h2&gt;
&lt;p&gt;Tempo maintains its own concept of partitions that are logically distinct from Kafka partitions.
While there&amp;rsquo;s typically a 1:1 mapping,
the partition ring gives Tempo independent control over partition states (pending, active, inactive),
ownership (which live-store owns each partition),
and lifecycle management (creating, activating, and deactivating partitions without modifying Kafka&amp;rsquo;s configuration).&lt;/p&gt;
&lt;h2 id=&#34;partition-states&#34;&gt;Partition states&lt;/h2&gt;
&lt;p&gt;Each partition in the ring has one of three states.&lt;/p&gt;
&lt;h3 id=&#34;pending&#34;&gt;Pending&lt;/h3&gt;
&lt;p&gt;Pending is the initial state when a new partition is created. No reads or writes occur.&lt;/p&gt;
&lt;p&gt;A partition enters pending state when a new live-store starts and creates a partition that doesn&amp;rsquo;t yet exist in the ring.
It stays in pending until enough owners have registered and a minimum waiting period has elapsed,
at which point the owning live-store automatically promotes it to active.&lt;/p&gt;
&lt;h3 id=&#34;active&#34;&gt;Active&lt;/h3&gt;
&lt;p&gt;Active is the normal operating state. Distributors write data to active partitions, and queriers read from them.&lt;/p&gt;
&lt;p&gt;A partition transitions from pending to active once enough owners have registered for that partition and a configurable waiting duration has elapsed.
This ensures that all availability zones have had time to register their live-store instances before traffic starts flowing.&lt;/p&gt;
&lt;h3 id=&#34;inactive&#34;&gt;Inactive&lt;/h3&gt;
&lt;p&gt;Inactive is the read-only state. Distributors stop writing to inactive partitions, but queriers can still read from them.&lt;/p&gt;
&lt;p&gt;A partition is marked inactive when scaling down.
It must remain in this state long enough for the block-builder to flush all remaining data for this partition to object storage,
and for queriers to stop relying on the live-store for this partition&amp;rsquo;s recent data.&lt;/p&gt;
&lt;p&gt;After this grace period, you can safely remove the partition and its owning live-store.&lt;/p&gt;
&lt;h2 id=&#34;ownership-model&#34;&gt;Ownership model&lt;/h2&gt;
&lt;h3 id=&#34;live-stores&#34;&gt;Live-stores&lt;/h3&gt;
&lt;p&gt;Each Tempo partition is owned by one live-store per availability zone.
In a zone-aware deployment with two zones, each partition has two owners—one per zone.
Both consume the same Kafka partition independently.&lt;/p&gt;
&lt;p&gt;When a live-store starts, it checks the ring for its assigned partition.
If the partition exists, it joins as an owner.
If not, it creates the partition in pending state and waits for enough owners to register.&lt;/p&gt;
&lt;h3 id=&#34;distributors&#34;&gt;Distributors&lt;/h3&gt;
&lt;p&gt;Distributors read the partition ring to determine which partitions are active.
They only send data to active partitions. The ring tells distributors which Kafka partitions to write to.&lt;/p&gt;
&lt;h3 id=&#34;block-builders&#34;&gt;Block-builders&lt;/h3&gt;
&lt;p&gt;Each block-builder instance computes which Kafka partitions it owns based on its ordinal ID and the &lt;code&gt;partitions_per_instance&lt;/code&gt; setting.
The partition ring indirectly affects block-builders because it determines which partitions receive data from distributors.&lt;/p&gt;
&lt;h2 id=&#34;scaling&#34;&gt;Scaling&lt;/h2&gt;
&lt;h3 id=&#34;scaling-up&#34;&gt;Scaling up&lt;/h3&gt;
&lt;p&gt;To scale up, deploy a new live-store instance.
The live-store creates a new partition in the ring (pending state).
After enough owners register and the waiting period elapses, the partition transitions to active,
and distributors begin writing to the new partition.&lt;/p&gt;
&lt;p&gt;A corresponding Kafka partition must exist. Add Kafka partitions first if needed.&lt;/p&gt;
&lt;h3 id=&#34;scaling-down&#34;&gt;Scaling down&lt;/h3&gt;
&lt;p&gt;To scale down, mark the target partition as inactive while the live-store is still running.
Distributors stop writing to it.
Wait for the block-builder to flush remaining data to object storage, then remove the live-store instance.
The partition is eventually cleaned up from the ring.&lt;/p&gt;
&lt;p&gt;Skipping the inactive step and abruptly removing a live-store causes recent data for that partition to become temporarily unavailable (unless a zone-aware replica exists).&lt;/p&gt;
&lt;h2 id=&#34;memberlist-propagation&#34;&gt;Memberlist propagation&lt;/h2&gt;
&lt;p&gt;The partition ring state is propagated using memberlist, which uses a gossip protocol.
Changes to the ring (new partitions, state transitions) propagate across the cluster within seconds under normal conditions.&lt;/p&gt;
&lt;p&gt;During network partitions or high cluster churn, propagation may be delayed.
This can cause brief inconsistencies where different components have different views of the ring.
Tempo handles this gracefully: distributors write to a partition that a live-store hasn&amp;rsquo;t yet seen results in data that&amp;rsquo;s picked up after the live-store catches up,
and queriers contacting a live-store for a partition it doesn&amp;rsquo;t own yet get an empty response,
with the data eventually available from another live-store or from object storage.&lt;/p&gt;
&lt;h2 id=&#34;related-resources&#34;&gt;Related resources&lt;/h2&gt;
&lt;p&gt;Refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/#memberlist&#34;&gt;memberlist configuration&lt;/a&gt; for ring propagation settings
and the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/#ingest&#34;&gt;ingest configuration&lt;/a&gt; for partition-related settings.&lt;/p&gt;
]]></content><description>&lt;h1 id="partition-ring">Partition ring&lt;/h1>
&lt;p>The partition ring is the mechanism Tempo uses to track which partitions exist, their current state, and which components own them.
By default, the partition ring propagates across the cluster via memberlist gossip and is central to how distributors, live-stores, and block-builders coordinate.&lt;/p></description></item><item><title>Deployment modes</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/deployment-modes/</guid><content><![CDATA[&lt;h1 id=&#34;deployment-modes&#34;&gt;Deployment modes&lt;/h1&gt;
&lt;p&gt;Tempo can be deployed in monolithic or microservices mode. Microservices mode requires a Kafka-compatible system. Monolithic mode doesn&amp;rsquo;t use Kafka.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Monolithic mode&lt;/em&gt; was previously called &lt;em&gt;single binary mode&lt;/em&gt;.&lt;/p&gt;


&lt;div class=&#34;admonition admonition-note&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Note&lt;/p&gt;&lt;p&gt;The previous &lt;em&gt;scalable monolithic mode&lt;/em&gt;, also known as &lt;em&gt;scalable single binary mode&lt;/em&gt; or SSB, has been removed in v3.0.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;p&gt;All components are compiled into the same binary. The &lt;code&gt;-target&lt;/code&gt; command-line parameter, or &lt;code&gt;target&lt;/code&gt; in configuration, determines which components run in a given process. The default target is &lt;code&gt;all&lt;/code&gt;, which is the monolithic deployment mode.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;tempo -target=all&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/set-up-for-tracing/setup-tempo/command-line-flags/&#34;&gt;command line flags&lt;/a&gt; documentation for more information on the &lt;code&gt;-target&lt;/code&gt; flag.&lt;/p&gt;
&lt;h2 id=&#34;monolithic-mode&#34;&gt;Monolithic mode&lt;/h2&gt;
&lt;p&gt;In monolithic mode, the required components run in a single process using &lt;code&gt;-target=all&lt;/code&gt;, which is the default. Components that are only needed in microservices mode, such as the block-builder, are excluded.&lt;/p&gt;
&lt;p&gt;No Kafka is required. The distributor pushes trace data in-process directly to the live-store and metrics-generator. Traces are flushed to the configured storage backend without an intermediate message queue. Object storage is recommended for production deployments.&lt;/p&gt;
&lt;h3 id=&#34;when-to-use-monolithic-mode&#34;&gt;When to use monolithic mode&lt;/h3&gt;
&lt;p&gt;Monolithic mode is suitable for getting started, development environments, and low to moderate trace volumes where operational simplicity matters more than independent scaling.&lt;/p&gt;
&lt;h3 id=&#34;limitations&#34;&gt;Limitations&lt;/h3&gt;
&lt;p&gt;Components share the same resource pool. A spike in query load can affect write throughput and vice versa. There is no independent scaling. You can run multiple monolithic instances, but each instance runs the same set of components. At higher volumes, memory pressure from collocated components, particularly the live-store and querier, can cause out-of-memory issues.&lt;/p&gt;
&lt;h3 id=&#34;resource-considerations&#34;&gt;Resource considerations&lt;/h3&gt;
&lt;p&gt;Monolithic instances need enough memory to handle the live-store&amp;rsquo;s in-memory trace buffer, the querier&amp;rsquo;s concurrent job execution, and the backend worker&amp;rsquo;s memory for block merging. As volume increases, the instance is limited by whichever component is most resource-hungry.&lt;/p&gt;
&lt;h3 id=&#34;example&#34;&gt;Example&lt;/h3&gt;
&lt;p&gt;Refer to &lt;a href=&#34;https://github.com/grafana/tempo/tree/main/example/docker-compose/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Docker Compose examples in the Tempo repository&lt;/a&gt; for sample deployments.&lt;/p&gt;
&lt;p&gt;For an annotated example configuration for Tempo, refer to the &lt;a href=&#34;https://github.com/grafana/intro-to-mltp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Introduction to MLTP&lt;/a&gt; repository, which includes a &lt;a href=&#34;https://github.com/grafana/intro-to-mltp/blob/main/tempo/tempo.yaml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;sample &lt;code&gt;tempo.yaml&lt;/code&gt;&lt;/a&gt; for a monolithic instance.&lt;/p&gt;
&lt;h2 id=&#34;microservices-mode&#34;&gt;Microservices mode&lt;/h2&gt;
&lt;p&gt;In microservices mode, each component runs as a separate process with its own &lt;code&gt;-target&lt;/code&gt;. This is the recommended mode for production.&lt;/p&gt;
&lt;p&gt;The configuration associated with each component&amp;rsquo;s deployment specifies a &lt;code&gt;target&lt;/code&gt;. For example, to deploy a &lt;code&gt;querier&lt;/code&gt;, the configuration would contain &lt;code&gt;target: querier&lt;/code&gt;. A command-line deployment may specify the &lt;code&gt;-target=querier&lt;/code&gt; flag.&lt;/p&gt;
&lt;h3 id=&#34;when-to-use-microservices-mode&#34;&gt;When to use microservices mode&lt;/h3&gt;
&lt;p&gt;Use microservices mode for production deployments, high trace volumes requiring independent scaling, and environments where high availability is important.&lt;/p&gt;
&lt;h3 id=&#34;advantages&#34;&gt;Advantages&lt;/h3&gt;
&lt;p&gt;Microservices mode provides independent scaling. You can scale block-builders for write throughput, queriers for query performance, and live-stores for recent data capacity, all independently. Failure domains are isolated: a querier OOM doesn&amp;rsquo;t affect data ingestion, and a block-builder restart doesn&amp;rsquo;t affect query availability. Live-stores can be deployed across availability zones for high availability. Each component gets exactly the resources it needs, avoiding the over-provisioning required in monolithic mode.&lt;/p&gt;
&lt;h3 id=&#34;component-scaling-guidelines&#34;&gt;Component scaling guidelines&lt;/h3&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Component&lt;/th&gt;
              &lt;th&gt;Scaling strategy&lt;/th&gt;
              &lt;th&gt;Notes&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Distributor&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Stateless. Scale based on ingestion rate.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Block-builder&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Bounded by Kafka partition count. Scale based on data volume.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Live-store&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Bounded by Kafka partition count. Scale based on recent data query volume and memory.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Query frontend&lt;/td&gt;
              &lt;td&gt;Vertical&lt;/td&gt;
              &lt;td&gt;Keep to 2 replicas. Scale up CPU/RAM rather than adding replicas.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Querier&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Scale based on query concurrency and latency requirements.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Backend worker&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Handles compaction and retention. Scale based on block count and compaction lag.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Metrics-generator&lt;/td&gt;
              &lt;td&gt;Horizontal&lt;/td&gt;
              &lt;td&gt;Scale based on trace volume and number of generated series.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h3 id=&#34;kafka-as-the-connecting-fabric&#34;&gt;Kafka as the connecting fabric&lt;/h3&gt;
&lt;p&gt;In microservices mode, Kafka is the primary communication channel for the write path. Components don&amp;rsquo;t communicate directly for data transfer. Distributors write to Kafka. Block-builders, live-stores, and metrics-generators each consume from Kafka independently. Queriers contact live-stores over gRPC for recent data. All components access object storage for block data.&lt;/p&gt;
&lt;p&gt;Adding or removing instances of any component doesn&amp;rsquo;t require reconfiguring other components, aside from Kafka partition management.&lt;/p&gt;
&lt;h3 id=&#34;example-1&#34;&gt;Example&lt;/h3&gt;
&lt;p&gt;Refer to the &lt;a href=&#34;https://github.com/grafana/tempo/tree/main/example/docker-compose/distributed&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;distributed Docker Compose example&lt;/a&gt; in the Tempo repository.&lt;/p&gt;
&lt;h2 id=&#34;components-by-deployment-mode&#34;&gt;Components by deployment mode&lt;/h2&gt;
&lt;p&gt;Not all components and configuration blocks apply to both modes. The following table summarizes which components run in each mode and how shared components differ.&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Component&lt;/th&gt;
              &lt;th&gt;Config block&lt;/th&gt;
              &lt;th&gt;Monolithic&lt;/th&gt;
              &lt;th&gt;Microservices&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Distributor&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Pushes data in-process to the live-store and metrics-generator&lt;/td&gt;
              &lt;td&gt;Writes data to Kafka&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Ingest&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;ingest&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Not used&lt;/td&gt;
              &lt;td&gt;Kafka connection settings for the write path&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Block-builder&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;block_builder&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Not used&lt;/td&gt;
              &lt;td&gt;Consumes from Kafka, builds Parquet blocks, flushes to object storage&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Live-store&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;live_store&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Receives data directly from the distributor&lt;/td&gt;
              &lt;td&gt;Consumes from Kafka&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Live-store client&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;live_store_client&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Querier-to-live-store client (runs in-process)&lt;/td&gt;
              &lt;td&gt;gRPC client for querier-to-live-store communication&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Query-frontend&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;query_frontend&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Runs in-process&lt;/td&gt;
              &lt;td&gt;Runs as a separate process&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Querier&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;querier&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Runs in-process&lt;/td&gt;
              &lt;td&gt;Runs as a separate process&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Backend scheduler&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;backend_scheduler&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Runs in-process&lt;/td&gt;
              &lt;td&gt;Runs as a separate process&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Backend worker&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;backend_worker&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Runs in-process&lt;/td&gt;
              &lt;td&gt;Runs as a separate process&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Metrics-generator&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;metrics_generator&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Optional, runs in-process&lt;/td&gt;
              &lt;td&gt;Optional, runs as a separate process&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Storage&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;storage&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Storage backend for trace data (object storage recommended; local supported for dev/test)&lt;/td&gt;
              &lt;td&gt;Object storage for trace data&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Memberlist&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;memberlist&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Cluster membership&lt;/td&gt;
              &lt;td&gt;Cluster membership&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Overrides&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;overrides&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Per-tenant limits&lt;/td&gt;
              &lt;td&gt;Per-tenant limits&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;For full configuration details, refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/&#34;&gt;configuration reference&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;migrating-between-modes&#34;&gt;Migrating between modes&lt;/h2&gt;
&lt;p&gt;Moving from monolithic to microservices mode involves deploying individual components with appropriate &lt;code&gt;-target&lt;/code&gt; flags, pointing all components at the same Kafka cluster, object storage, and memberlist, then scaling down the monolithic instances.&lt;/p&gt;
&lt;p&gt;Since Kafka provides durability, no data is lost during the transition. Live-stores replay from Kafka on startup, and block-builders continue from their last committed offset.&lt;/p&gt;
&lt;h2 id=&#34;related-resources&#34;&gt;Related resources&lt;/h2&gt;
&lt;p&gt;Refer to 
    &lt;a href=&#34;/docs/tempo/v3.0.x/set-up-for-tracing/setup-tempo/plan/&#34;&gt;Plan your Tempo deployment&lt;/a&gt; for deployment planning and sizing guidance.&lt;/p&gt;
]]></content><description>&lt;h1 id="deployment-modes">Deployment modes&lt;/h1>
&lt;p>Tempo can be deployed in monolithic or microservices mode. Microservices mode requires a Kafka-compatible system. Monolithic mode doesn&amp;rsquo;t use Kafka.&lt;/p>
&lt;p>&lt;em>Monolithic mode&lt;/em> was previously called &lt;em>single binary mode&lt;/em>.&lt;/p></description></item><item><title>Block format</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/block-format/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/block-format/</guid><content><![CDATA[&lt;h1 id=&#34;block-format&#34;&gt;Block format&lt;/h1&gt;
&lt;p&gt;Tempo stores trace data in &lt;a href=&#34;https://parquet.apache.org&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Apache Parquet&lt;/a&gt; format.
Parquet is a columnar storage format that enables efficient querying of specific attributes without reading entire traces.&lt;/p&gt;
&lt;h2 id=&#34;why-parquet&#34;&gt;Why Parquet&lt;/h2&gt;
&lt;p&gt;Columnar storage is a natural fit for trace data.
A TraceQL query like &lt;code&gt;{ span.http.status_code = 500 }&lt;/code&gt; only needs to read the &lt;code&gt;http.status_code&lt;/code&gt; column, not the entire trace.
This dramatically reduces the amount of data read from storage.
Columnar data also compresses well because values in a column tend to be similar (for example, all service names or all status codes).&lt;/p&gt;
&lt;h2 id=&#34;block-structure&#34;&gt;Block structure&lt;/h2&gt;
&lt;p&gt;A block is a directory in object storage containing several files.&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;File&lt;/th&gt;
              &lt;th&gt;Purpose&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;meta.json&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Block metadata: time range, tenant, block ID, &lt;code&gt;replaces&lt;/code&gt; field for atomic replacement&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;data.parquet&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Trace data in columnar format&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Bloom filters&lt;/td&gt;
              &lt;td&gt;Probabilistic data structures for efficient trace ID lookups&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Index&lt;/td&gt;
              &lt;td&gt;Maps trace IDs to row groups within &lt;code&gt;data.parquet&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;nocompact.flg&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Temporary flag preventing compaction (present during block-builder flushes)&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;Blocks are stored under &lt;code&gt;&amp;lt;tenant-id&amp;gt;/&amp;lt;block-id&amp;gt;/&lt;/code&gt; in object storage.&lt;/p&gt;
&lt;h3 id=&#34;metajson&#34;&gt;&lt;code&gt;meta.json&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;meta.json&lt;/code&gt; file makes a block visible to the read path.
It contains the block ID, tenant ID, start and end timestamps, total number of objects (traces).&lt;/p&gt;
&lt;p&gt;A block is invisible to queriers until &lt;code&gt;meta.json&lt;/code&gt; exists.
The block-builder uses this property to ensure atomicity during flushes.&lt;/p&gt;
&lt;h2 id=&#34;schema&#34;&gt;Schema&lt;/h2&gt;
&lt;p&gt;Tempo uses a span-oriented heavily nested Parquet schema.
Each row represents a span, with columns for intrinsic fields (trace ID, span ID, parent span ID, span name, span kind, span status, duration, start time, root service name, root span name),
resource attributes (for example, &lt;code&gt;service.name&lt;/code&gt;, &lt;code&gt;deployment.environment&lt;/code&gt;), and span attributes (for example, &lt;code&gt;http.method&lt;/code&gt;, &lt;code&gt;http.status_code&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&#34;intrinsic-fields-vs-generic-attributes&#34;&gt;Intrinsic fields vs generic attributes&lt;/h3&gt;
&lt;p&gt;A small number of fields are stored as top-level columns in the schema.
These include &lt;code&gt;service.name&lt;/code&gt; on resources and &lt;code&gt;status.code&lt;/code&gt; on spans.
Querying these fields is always efficient because they have their own Parquet columns.&lt;/p&gt;
&lt;p&gt;All other attributes (both resource and span) are stored in a generic &lt;code&gt;Attrs&lt;/code&gt; column as key-value pairs.
Querying these requires scanning the attribute map, which is slower.&lt;/p&gt;
&lt;h3 id=&#34;dedicated-attribute-columns&#34;&gt;Dedicated attribute columns&lt;/h3&gt;
&lt;p&gt;You can promote frequently queried attributes from the generic column to their own dedicated Parquet columns for better query performance.
This is configured centrally in &lt;code&gt;storage.trace.block&lt;/code&gt; and applies to all block-producing components (live-store and block-builder).
Dedicated columns are assigned dynamically per block based on the configuration at the time the block is built.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;storage:
  trace:
    block:
      parquet_dedicated_columns:
        - name: http.method
          type: string
          scope: span
        - name: deployment.environment
          type: string
          scope: resource&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Refer to 
    &lt;a href=&#34;/docs/tempo/v3.0.x/operations/dedicated_columns/&#34;&gt;Dedicated attribute columns&lt;/a&gt; for configuration details and recommendations.&lt;/p&gt;
&lt;h2 id=&#34;block-format-versions&#34;&gt;Block format versions&lt;/h2&gt;
&lt;p&gt;Tempo uses versioned block formats.&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Version&lt;/th&gt;
              &lt;th&gt;Status&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;vParquet3&lt;/td&gt;
              &lt;td&gt;Deprecated in 2.10, removed in 3.0&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;vParquet4&lt;/td&gt;
              &lt;td&gt;Default and latest in Tempo 3.0&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;vParquet5&lt;/td&gt;
              &lt;td&gt;Production-ready, opt-in&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;The block format is configured in:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;storage:
  trace:
    block:
      version: vParquet4&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Existing blocks in older formats remain readable. New blocks are always written in the configured version.&lt;/p&gt;
&lt;h2 id=&#34;related-resources&#34;&gt;Related resources&lt;/h2&gt;
&lt;p&gt;Refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/operations/schema/&#34;&gt;Apache Parquet schema&lt;/a&gt; documentation for the full schema details
and the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/parquet/&#34;&gt;Apache Parquet block format configuration&lt;/a&gt; for configuration options.&lt;/p&gt;
]]></content><description>&lt;h1 id="block-format">Block format&lt;/h1>
&lt;p>Tempo stores trace data in &lt;a href="https://parquet.apache.org" target="_blank" rel="noopener noreferrer">Apache Parquet&lt;/a> format.
Parquet is a columnar storage format that enables efficient querying of specific attributes without reading entire traces.&lt;/p></description></item><item><title>Object storage</title><link>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/object-storage/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/reference-tempo-architecture/object-storage/</guid><content><![CDATA[&lt;h1 id=&#34;object-storage&#34;&gt;Object storage&lt;/h1&gt;
&lt;p&gt;Object storage is the long-term storage backend for all trace data in Tempo. Block-builders write blocks to it, queriers read from it, and backend workers maintain it.&lt;/p&gt;
&lt;h2 id=&#34;supported-backends&#34;&gt;Supported backends&lt;/h2&gt;
&lt;p&gt;Tempo supports three major object storage APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Amazon S3 (and S3-compatible systems, for example, MinIO)&lt;/li&gt;
&lt;li&gt;Google Cloud Storage (GCS)&lt;/li&gt;
&lt;li&gt;Microsoft Azure Blob Storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A local filesystem backend is also available for development and testing.&lt;/p&gt;
&lt;h2 id=&#34;storage-layout&#34;&gt;Storage layout&lt;/h2&gt;
&lt;p&gt;Data in object storage is organized by tenant and block:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;&amp;lt;bucket&amp;gt;/
  &amp;lt;tenant-id&amp;gt;/
    &amp;lt;block-id&amp;gt;/
      meta.json
      data.parquet
      bloom-0
      bloom-1
      ...
      index&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Each tenant has its own directory. Within a tenant, each block is a directory containing the block&amp;rsquo;s files.&lt;/p&gt;
&lt;h3 id=&#34;blocklist&#34;&gt;Blocklist&lt;/h3&gt;
&lt;p&gt;The blocklist is the set of all known blocks for a tenant. Backend workers maintain it by periodically scanning object storage for &lt;code&gt;meta.json&lt;/code&gt; files and writing a per-tenant block index.&lt;/p&gt;
&lt;p&gt;Queriers and query frontends read this tenant index to determine which blocks to search for a given query. They fall back to scanning object storage for &lt;code&gt;meta.json&lt;/code&gt; files only when the tenant index is unavailable or too stale. The blocklist is distributed across the cluster so that not every component needs to poll storage directly.&lt;/p&gt;
&lt;h3 id=&#34;tenant-isolation&#34;&gt;Tenant isolation&lt;/h3&gt;
&lt;p&gt;Tenants are fully isolated at the storage level. Each tenant&amp;rsquo;s blocks are in a separate directory prefix. There&amp;rsquo;s no cross-tenant data sharing or block merging.&lt;/p&gt;
&lt;h2 id=&#34;durability-model&#34;&gt;Durability model&lt;/h2&gt;
&lt;p&gt;With Tempo 3.0&amp;rsquo;s Kafka-based architecture, durability works in layers.&lt;/p&gt;
&lt;p&gt;Kafka provides immediate durability. Once data is acknowledged by Kafka, it&amp;rsquo;s safe even if all Tempo components crash. Object storage provides long-term durability. Once the block-builder flushes a block, the data is durably stored and independent of Kafka. Kafka retention bridges the gap: Kafka retains data long enough for block-builders to consume and flush it. If a block-builder is slow or restarting, Kafka holds the data until it&amp;rsquo;s processed.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no single point of failure for data durability. Kafka and object storage together provide end-to-end safety.&lt;/p&gt;
&lt;h2 id=&#34;performance-considerations&#34;&gt;Performance considerations&lt;/h2&gt;
&lt;h3 id=&#34;read-path&#34;&gt;Read path&lt;/h3&gt;
&lt;p&gt;Query performance depends on how efficiently queriers can access blocks. Larger blocks (from compaction) reduce the number of blocks to search but increase individual block read time. Caching bloom filters, Parquet pages, and footers at the querier level significantly reduces object storage reads. Promoting frequently queried attributes to dedicated Parquet columns reduces the amount of data read per query.&lt;/p&gt;
&lt;h3 id=&#34;write-path&#34;&gt;Write path&lt;/h3&gt;
&lt;p&gt;Block-builder write performance is generally bounded by Kafka consumption rate, local disk speed (blocks are built on scratch disk before upload), and upload bandwidth to object storage.&lt;/p&gt;
&lt;h3 id=&#34;cost&#34;&gt;Cost&lt;/h3&gt;
&lt;p&gt;Object storage costs are primarily driven by storage volume (total data retained, controlled by retention period and compaction efficiency) and API operations (GET/PUT/LIST calls). Compaction reduces LIST costs by consolidating blocks. Caching reduces GET costs.&lt;/p&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;storage:
  trace:
    backend: s3  # or gcs, azure, local
    s3:
      bucket: tempo-traces
      endpoint: s3.amazonaws.com&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;related-resources&#34;&gt;Related resources&lt;/h2&gt;
&lt;p&gt;Refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/#storage&#34;&gt;storage configuration&lt;/a&gt; for the full list of options.&lt;/p&gt;
]]></content><description>&lt;h1 id="object-storage">Object storage&lt;/h1>
&lt;p>Object storage is the long-term storage backend for all trace data in Tempo. Block-builders write blocks to it, queriers read from it, and backend workers maintain it.&lt;/p></description></item></channel></rss>