Distributed tracing in 2026: revisit Jaeger, Grafana Tempo and SigNoz before you store another span
Picture a bus, train and hotel booking platform in Kochi (an invented example, not a real client). Customers book from an Android app, an iOS app and a web app. Behind them sit about forty Go and Java services on Kubernetes: search, pricing, seat inventory, UPI and card payments, hotel partner APIs and a notification service that sends tickets on WhatsApp and SMS. Tracing exists, but barely. A Jaeger 1.x setup from three years ago writes to a small Elasticsearch cluster with 1 percent head sampling. During the Onam travel rush, the support team reported that “payment went through but no ticket came”, and the one trace that would have explained it had not been sampled. The platform team has now agreed to rebuild tracing properly and keep it self-hosted. One architect wants to move to Jaeger v2 on OpenSearch, because the team knows Jaeger and the UI already works. A second wants Grafana Tempo on object storage, because the company already runs Grafana and Prometheus and storage cost is the real worry. A third wants SigNoz, because it keeps traces, logs and metrics in one ClickHouse database with one UI.
All three are open-source tracing backends that accept OpenTelemetry (OTLP) data and run on your own servers. They are not instrumentation libraries, not log pipelines and not a replacement for metrics. This post compares Jaeger, Grafana Tempo and SigNoz on storage model and cost, sampling, search and query languages, retention, multi-tenancy, scale operations, licences and security. Facts come from release notes, official documentation, source repositories, licence files and CVE records, checked on 13 October 2026. The drill near the end is my own run on this Linux box.
The short version
- A tracing backend has two jobs: keep spans cheaply for a fixed time, and answer two kinds of questions. “Show me trace X” is easy for all three. “Find the traces where something went wrong” is where they differ.
- Jaeger 2.22.0 (6 October 2026, 7:57 PM IST) is now a custom build of the OpenTelemetry Collector. It is Apache 2.0, stores spans in Elasticsearch, OpenSearch, Cassandra, Badger, memory or ClickHouse, and still has the best-known trace UI. Version 1 binaries are no longer published since January 2026.
- Grafana Tempo 3.1.0 (29 September 2026, 10:36 PM IST) stores traces as Parquet blocks in object storage and searches them with TraceQL. Tempo 3.0 replaced ingesters with a Kafka-based write path for the distributed mode; the single-binary mode needs no Kafka. The licence is AGPLv3.
- SigNoz 0.145.0 (5 October 2026, 12:11 PM IST) is a full observability product on ClickHouse: traces, logs, metrics, dashboards and alerts. The core is MIT, the enterprise directories are under a commercial licence, and its ingestion collector is AGPLv3. It had eight CVEs published between June and September 2026, all fixed by 0.143.0.
- Sampling matters more than the backend choice. Tail sampling in the OpenTelemetry Collector keeps every error and every slow trace; head sampling at the SDK decides before anything has gone wrong.
- In my drill, all three answered the same four questions correctly for 12,000 spans. Jaeger search was complete at once. Tempo search caught up after about 35 seconds and never found spans that arrived with timestamps 5 to 10 minutes old, though it found them by trace ID. Tail sampling kept 66 of 66 error traces; simulated 10 percent head sampling kept 7.
Where things stand on 13 October 2026
Dates are from each project’s GitHub release page; times are converted from UTC to IST. Licences are from the LICENSE files at the tagged release.
Project
Release
Date (IST)
Notes
Licence
Jaeger
2.22.0
6 October 2026, 7:57 PM
Current tip; Elasticsearch numeric attribute indexing becomes a config option; native trace summaries gate stable; structured filters gate beta; span ordering in API v3
Apache 2.0
Jaeger
2.21.0
15 September 2026, 4:35 AM
ClickHouse storage feature gate promoted to stable; v1 HTTP endpoints the UI no longer uses removed; search depth capped at 10,000
Apache 2.0
Jaeger
2.14.0
2 January 2026, 7:59 AM
First release after legacy v1 collector, query and ingester binaries stopped being published
Apache 2.0
Jaeger UI
2.22.0
6 October 2026, 7:45 AM
Released alongside the backend
Apache 2.0
Grafana Tempo
3.1.0
29 September 2026, 10:36 PM
Current tip; redaction by TraceQL query; TraceQL metrics arithmetic; extrapolation from sampling probability; vParquet5 default; signed images
AGPLv3 (some directories Apache 2.0)
Grafana Tempo
3.0.3, 2.10.8 and 2.9.5
14 August 2026, 2:07 AM
Patch releases on three lines for Go and dependency CVEs
AGPLv3
Grafana Tempo
3.0.0
28 May 2026, 8:39 PM
Kafka-based ingest replaces ingesters in microservices mode; compactor replaced by backend scheduler and workers; TraceQL metrics generally available
AGPLv3
SigNoz
0.145.0
5 October 2026, 12:11 PM
Current tip of a weekly release train; fine-grained authorisation work, log pipeline and explorer changes
MIT core, Enterprise License for ee/
SigNoz
0.143.0
23 September 2026, 12:57 PM
Fixes for the default JWT secret and session revocation CVEs
MIT core, Enterprise License for ee/
SigNoz OTel Collector
0.144.13
8 October 2026, 10:57 PM
The ingester and schema migrator that write to ClickHouse
AGPLv3
SigNoz Foundry
0.3.0
23 September 2026
Installer that replaces the deprecated Docker Compose and install script paths
AGPLv3
OpenTelemetry Collector (releases)
0.162.0
29 September 2026, 6:04 PM
Core and contrib distributions; tail sampling processor is beta
Apache 2.0
A short timeline helps explain why these three tools feel so different.
Date (IST)
Event
2015
Jaeger created at Uber (per the CNCF graduation announcement)
16 April 2016
Jaeger GitHub repository created
6 December 2017
Jaeger 1.0.0
31 October 2019
Jaeger graduates in the CNCF, the seventh project to do so
24 January 2020
Tempo repository created at Grafana Labs
23 October 2020
Tempo first release (0.2.0)
3 January 2021
SigNoz repository created
20 April 2021
Grafana Labs announces Tempo, Loki and Grafana move from Apache 2.0 to AGPLv3
8 June 2021
Tempo 1.0.0; on the same day SigNoz 0.3.1 makes ClickHouse its default storage
31 January 2023
Tempo 2.0.0
11 November 2024
Jaeger v2.0.0 (“Jaeger v2 is here”), built on the OpenTelemetry Collector
3 December 2025
Jaeger 1.76.0 and 2.13.0, the last paired v1 and v2 release
28 May 2026
Tempo 3.0.0 with the new ingest architecture
June to September 2026
Eight SigNoz CVEs published; the last two fixed in 0.143.0
29 September 2026
Tempo 3.1.0 and OpenTelemetry Collector 0.162.0
6 October 2026
Jaeger 2.22.0
First question: do you need a tracing backend at all?
Tracing earns its cost when one user action crosses several services and one service’s logs cannot explain a slow or failed request. A Kochi booking that touches search, pricing, seat hold, payment and notification qualifies; a monolith with one database usually does not.
Situation
What usually works
One or two services, one database
Structured logs with a request ID, plus RED metrics (rate, errors, duration)
Several services, but issues are mostly capacity
Metrics first; add exemplars or span metrics later
Many services, user complaints about one failed or slow action
A tracing backend with sampling designed around errors and latency
Strict cost limits
Tail sampling plus short retention, then decide the backend
If you need tracing, instrument with OpenTelemetry SDKs and send OTLP to an OpenTelemetry Collector that you control. That collector layer is the part you keep when you change backends. I covered collectors as log shippers in the log pipeline post; the same binary does sampling and fan-out for traces.
What a tracing backend actually stores and serves
A span is a timed operation with a trace ID, a parent span ID, a service name, a status and a bag of attributes. A trace is all spans that share a trace ID, often arriving from different services seconds apart. The backend must:
- accept OTLP (and older Jaeger or Zipkin formats) at a steady rate,
- make fetching one trace by ID cheap,
- index or scan spans so that “errors in payment-service in the last hour” is possible,
- delete old data on schedule and show a readable waterfall.
The big difference is in the third point. Jaeger relies on the indexes of a database you choose, Tempo scans columnar blocks in object storage, and SigNoz queries ClickHouse tables.
Jaeger
Jaeger v2 is a custom distribution of the OpenTelemetry Collector. The same binary takes a role through its configuration: collector, query, ingester or all-in-one. Storage is configured through a jaeger_storage extension, spans are written by a storage exporter, and the UI and APIs come from a jaeger_query extension. Because it is a collector, OTLP receivers, batch processors and the tail sampling processor live in the same binary.
Storage backends in the 2.22 documentation:
Backend
What the docs say
Good fit
Elasticsearch 7.x and 8.x
Index rollover with ILM recommended; a cleaner job or ILM handles retention
Teams that already run Elasticsearch
OpenSearch 1.x, 2.x and 3.x
Recommended over Cassandra for large-scale production
New large deployments
Cassandra 4.x and 5.x
Schema created on start-up since 2.1.0; trace TTL default 48h in the example
Teams with Cassandra skills; native TTL
ClickHouse 26.x
The docs page says experimental and behind the storage.clickhouse feature gate, schema may change; the 2.21.0 release notes say the gate was promoted to stable
Watch, test, do not standardise yet
Badger
Embedded, local disk, single node only
All-in-one for small or edge setups
Memory
Not for production
Demos and tests
Remote storage (gRPC)
Plug in an external storage service
Custom backends
Kafka
A buffer between collectors and ingesters
Burst protection
Note the ClickHouse wording. The release notes and the docs disagree on 13 October 2026, so I would treat Jaeger on ClickHouse as early until the docs page catches up. If you choose OpenSearch, the earlier Elasticsearch and OpenSearch post covers that choice.
Jaeger search is span-based: service, operation, attribute pairs, duration range and time window, returning traces that contain one matching span. That is fast on an indexed store, but it cannot express “a Bengaluru booking whose seat hold child span took over two seconds” unless both facts sit on the same span. Jaeger also offers API v3 over HTTP and gRPC, archive storage for traces you want to keep longer, service performance monitoring from span metrics in a Prometheus-compatible store, and a dependency graph that needs a separate Spark job on the large backends.
Grafana Tempo
Tempo started with one idea: do not index traces, store them cheaply in object storage, and find them by ID from logs or exemplars. TraceQL later added real search, and vParquet made that search practical by storing spans in columns.
In Tempo 3.x the write path changed. In microservices mode, distributors write to a Kafka-compatible log, live-stores serve recent data, block-builders turn the log into Parquet blocks in object storage, and a backend scheduler with workers handles compaction, retention and redaction. Query frontends and queriers search both recent and stored data. Monolithic mode runs all of this in one process without Kafka; the old “scalable single binary” target was removed in 3.0, and 3.0 cannot be downgraded in place.
Storage is S3, GCS, Azure Blob or a local filesystem. The docs say the local backend is for development and testing, and that you cannot mix local and object storage. If you run your own S3-compatible store, the MinIO and its alternatives post is the relevant reading. Retention is block_retention, 336 hours (14 days) by default, with per-tenant overrides.
TraceQL is the main reason to choose Tempo today. It selects spansets and joins them structurally:
{ resource.service.name = "payment-service" && status = error }{ span.booking.route = "Kochi-Bengaluru" } >> { resource.service.name = "seat-service" && duration > 2s }(a descendant relationship){ span.customer.tier = "gold" } && { status = error }(both spansets in the same trace){ status = error } | count_over_time() by (resource.service.name)(TraceQL metrics, generally available since 3.0)
vParquet5 is the default block format in 3.1. Dedicated columns (up to 20 string and 5 integer columns per scope) make frequently searched attributes faster than generic attribute maps. The metrics-generator can produce span metrics and service graphs and remote-write them to Prometheus, Mimir or similar; the Prometheus long-term storage post covers where those metrics land.
SigNoz
SigNoz is a product, not only a trace store. Its architecture has three parts: the SigNoz OpenTelemetry Collector, which receives OTLP, Jaeger, Zipkin or Kafka data and writes to ClickHouse; ClickHouse itself (with ClickHouse Keeper or ZooKeeper); and the SigNoz binary, which bundles the React frontend, API server, an OpAMP server that pushes configuration to the collector, a rule engine and an alert manager. The metadata store (users, dashboards, alert rules) is SQLite by default or PostgreSQL.
Traces land in tables such as signoz_traces.signoz_index_v3, partitioned by day and ordered by time bucket, resource fingerprint, error flag, span name and timestamp, with bloom filter and n-gram indexes on attribute maps. Common attributes such as http.route, db.system and rpc.method are materialised as their own columns. You query through the query builder, PromQL for metrics or plain ClickHouse SQL. If ClickHouse is new to your team, the real-time OLAP post explains the engine’s behaviour.
Retention is a table TTL. The schema my drill created used 1,296,000 seconds (15 days) for traces, which matches the retention guide (15 days for logs and traces, 30 days for metrics). The older Linux install page still says 7 days, so check your own tables. Installation is now through Foundry; the Docker Compose path and the old install script are marked deprecated.
Architecture side by side
Aspect
Jaeger 2.22
Tempo 3.1
SigNoz 0.145
Core idea
Collector distribution plus pluggable span store
Columnar blocks in object storage, scanned in parallel
Observability product on ClickHouse
Signals
Traces (plus span metrics through Prometheus)
Traces (plus TraceQL metrics and generated metrics)
Traces, logs, metrics, alerts, dashboards
Ingest
OTLP gRPC and HTTP, Jaeger, Zipkin, Kafka
OTLP, Jaeger, Zipkin receivers
OTLP and others through its collector
Main storage
OpenSearch, Elasticsearch, Cassandra, ClickHouse (early), Badger
S3, GCS, Azure Blob (local for dev)
ClickHouse (replicated or not)
Extra dependencies
The database you choose; Kafka optional
Kafka-compatible log in microservices mode; none in monolithic
ClickHouse Keeper or ZooKeeper; SQLite or PostgreSQL
Stateless parts
Collector and query
Distributor, query frontend, querier
SigNoz binary (state in DB), collector
Search model
Span attributes, service, operation, duration
TraceQL across spansets and structure
Query builder, ClickHouse SQL
UI
Jaeger UI
Grafana (Explore, Traces Drilldown)
SigNoz UI
Storage model and cost
Trace storage cost is mostly bytes per day multiplied by retention days, and the backend changes the multiplier.
Factor
Jaeger on OpenSearch or Elasticsearch
Tempo on object storage
SigNoz on ClickHouse
Where bytes live
Search cluster disks, with replicas
Object storage, compressed Parquet
ClickHouse disks, compressed columns
What drives cost
Index size, replica count, shard count, hot nodes
Object storage per GB plus requests; compute for queries
Disk per node, replication, merges
Retention mechanism
Rollover plus index cleaner or ILM
block_retention per tenant, enforced by backend workers
Table TTL, changed in settings
Changing retention
Change the cleaner job or ILM policy
Change block_retention or the tenant override
Applies only to newly ingested data, per the docs
Long retention
Expensive unless you add warm tiers
Usually cheapest per GB, because object storage is
Plan ClickHouse disks and merges for it
Tempo’s sizing guide gives starting points for the 3.x distributed mode: one distributor per about 10 MB/s, one live-store per 6 to 10 MB/s with 4 to 20 GB memory, one querier per 1 to 2 MB/s, one block-builder per Kafka partition, about 10 MB/s per partition, and a single backend scheduler. Those are Grafana’s own planning numbers, not measurements of mine.
For the Kochi team, the honest estimate starts with the volume after sampling. Keeping errors, slow traces and a small baseline cuts bytes far more than any storage format.
Sampling: head, tail and adaptive
Method
Where it runs
What it can keep
Cost
Head sampling (TraceIdRatioBased in the SDK)
In each service, when the trace starts
A random fraction, decided before errors or latency are known
Cheapest; misses rare problems
Jaeger remote sampling
SDK fetches per-service or per-endpoint rates from Jaeger
Probabilistic or rate-limited strategies from a file
Central control; still head-based. If no file is set, Jaeger returns a 0.1 percent probabilistic default
Jaeger adaptive sampling
Jaeger calculates rates from traffic towards a target samples per second
Keeps low-traffic endpoints visible
Needs a sampling store; still head-based
Tail sampling processor
OpenTelemetry Collector (also inside the Jaeger binary)
Errors, latency over a threshold, attributes, plus a probabilistic share
Memory for in-flight traces; all spans of a trace must reach the same collector
The tail sampling processor documentation is clear on two operational points. It keeps spans in memory while it waits (decision_wait, 30 seconds by default, and num_traces, 50,000 by default), and every span of a trace must reach the same collector instance. In practice that means two collector layers: a front layer with the load-balancing exporter routing by trace ID, and a tail sampling layer behind it. The processor’s stability is beta for traces.
Tempo 3.1 adds an opt-in TraceQL metrics option to extrapolate counts from the sampling probability carried in W3C tracestate, and the tail sampler has an alpha feature gate to read and write those fields. Both are new; treat them as useful but young.
Query, search and the questions people actually ask
Question
Jaeger
Tempo
SigNoz
Show trace by ID
Yes, from UI or API v3
Yes, from Grafana or API
Yes, waterfall API and UI
Errors in one service in last hour
Service plus error=true attribute
TraceQL selector
Query builder or SQL
Spans slower than 2 seconds
Duration filter
duration > 2s
Duration filter or SQL
Parent with attribute X and child slower than Y
Only if both facts sit on one span
Structural operators such as >> and ~
SQL self-join on trace_id
Count errors per service over time
Service performance monitoring via span metrics
TraceQL metrics
Built-in dashboards and SQL
Search by trace from logs
Through a log tool that stores trace IDs
Grafana links between Loki and Tempo
Same product, logs to traces
The parent and child question matters. Real incidents often look like “the slow seat hold only happens on one route”. Tempo answers that natively, SigNoz with an SQL join, and Jaeger only if the attribute is copied onto the span you filter by.
Self-hosting operations
Area
Jaeger
Tempo
SigNoz
Smallest useful setup
One binary with Badger
One binary, monolithic, local disk (dev) or S3
ClickHouse, Keeper, SigNoz, SigNoz collector
Scale-out
Stateless collectors and query; scale the database
Microservices mode with Kafka-compatible log
Replicated and sharded ClickHouse; more collectors
High availability
Depends on the database cluster
Replication through Kafka; queriers scale out
ClickHouse replication through Keeper
Multi-tenancy
Header-based tenancy exists in code; no current docs page
First class: X-Scope-OrgID, per-tenant limits and retention
An organisation model exists; the pricing page lists multi-tenancy as coming soon
Auth for UI
None built in; put a reverse proxy with SSO in front
Grafana handles users; Tempo itself expects a gateway
Built-in users and roles; SSO and SAML in paid editions
Upgrade risk
Breaking changes appear in minor releases; read notes
3.0 is a one-way migration; 3.1 refuses to write vParquet3
Weekly releases; the schema migrator runs on upgrade
Tempo uses the same tenant header as Mimir and Loki, so a Grafana estate that already works that way gets tenancy almost for free.
Licences and the open-core line
Component
Licence at tag
What it means in practice
Jaeger 2.22.0
Apache 2.0
Permissive; no network copyleft
Tempo 3.1.0
AGPL-3.0-only by default; operations/ and pkg/tempopb are Apache 2.0 (LICENSING.md)
Using it internally is fine; modifying and offering it over a network to others triggers source sharing
SigNoz 0.145.0
MIT Expat, except ee/ and cmd/enterprise/ under the SigNoz Enterprise License
Enterprise code is free for development and testing; production use needs a subscription
SigNoz OTel Collector 0.144.13
AGPLv3
The ingestion path you run is AGPL even if the server is MIT
SigNoz Foundry 0.3.0
AGPLv3
Installer only
OpenTelemetry Collector contrib
Apache 2.0
Permissive
SigNoz publishes separate release archives for the community build and the default build. I used the community archive in the drill. Check which archive or image you deploy, and which features you depend on, before you assume the MIT line covers you.
Security and CVEs
Dates are when the record was published, converted to IST. Scores are those listed by the CNA or NVD.
Project
CVE
Published (IST)
Issue
Fixed in
Jaeger
CVE-2023-36656
17 July 2023
Stored XSS in the Jaeger UI key-values table (5.4)
Jaeger UI 1.31.0
Jaeger
CVE-2020-10750
20 June 2020
Sensitive data written to logs when Kafka storage is used
1.18.1
Related
CVE-2026-59892
8 July 2026
OpenTelemetry JS Jaeger propagator crashes Node.js on a malformed header (7.5)
@opentelemetry/propagator-jaeger 2.9.0
Tempo
CVE-2026-28377
27 March 2026
S3 SSE-C key shown in plain text on /status/config (7.5)
2.10.3
Tempo
CVE-2026-21728
24 April 2026
Queries with large limits cause large memory allocations (7.5); mitigation max_result_limit
2.8.4, 2.9.2, 2.10.2
Tempo
CVE-2026-27878
20 June 2026
Large exemplars hint in TraceQL can cause out-of-memory (6.5)
2.8.4, 2.9.2, 2.10.2 (per release notes)
Related
CVE-2025-2842
2 April 2025
Tempo Operator creates an over-broad cluster role binding when the Jaeger UI monitor tab is enabled (4.3)
Tempo Operator fix (see record)
SigNoz
CVE-2026-57955
29 June 2026
SQL injection in alert history via rule ID (CVSS 4.0: 8.3)
Affects up to 0.130.1
SigNoz
CVE-2026-57956
29 June 2026
Access to other organisations’ alert rules (6.1)
0.133.0
SigNoz
CVE-2026-63094
17 July 2026
Open redirect in SSO flow that can leak session tokens (7.6)
0.134.0
SigNoz
CVE-2026-92729
17 September 2026
Trace funnel endpoints without authorisation (8.8)
0.141.1
SigNoz
CVE-2026-93292
17 September 2026
SQL injection in trace funnel endpoints (8.4)
0.142.1
SigNoz
CVE-2026-93426
18 September 2026
SQL injection through field-key names in the v5 query API (8.4)
0.142.0
SigNoz
CVE-2026-97055
24 September 2026
Empty default JWT signing secret allows forged sessions (9.2)
0.143.0
SigNoz
CVE-2026-97056
24 September 2026
Sessions not revoked on password reset or user delete when the opaque tokenizer is used (7.6)
0.143.0
I found no CVE against the Jaeger server itself after 2023 in NVD keyword searches. Tempo 3.1.0 also fixed a cross-tenant redaction path (the tenant is now taken only from X-Scope-OrgID) and started signing images with cosign and SLSA provenance. SigNoz’s cluster of 2026 records is not a reason to avoid it, but it does mean you must keep up with its weekly releases and never expose an old version.
Practical controls for all three:
- Never expose the trace UI or query API directly to the internet. Put SSO in front of Jaeger and Tempo, and use SigNoz’s own users behind a VPN or gateway.
- Treat span attributes as sensitive data. Strip card numbers, phone numbers and tokens in the collector before they are stored.
- Set query limits (
max_result_limitand related settings in Tempo, search depth in Jaeger) so one heavy query cannot take the backend down. - For SigNoz, set
SIGNOZ_TOKENIZER_JWT_SECRETto a long random value even on 0.143.0 and later, and keep its OpAMP and API ports off public interfaces. - Lock down config and status endpoints such as Tempo’s
/status/config; they may print secrets. - Patch on a schedule and read release notes for security sections.
Prefer each when
Prefer
When
Jaeger
You want a permissive licence, a familiar trace UI, and you already run OpenSearch, Elasticsearch or Cassandra well. Trace volume is moderate or retention is short.
Tempo
Storage cost and long retention matter, you already run Grafana, and you want TraceQL for structural questions. You can run object storage and, at scale, a Kafka-compatible log.
SigNoz
You want one product for traces, logs and metrics, your team is comfortable with ClickHouse, and you accept a weekly upgrade rhythm and the edition split.
None yet
Instrumentation is incomplete or sampling is not designed. Fix that first; the backend will not save you.
Decision guide
If your main concern is
Lean towards
Because
Cheapest storage for 30+ days
Tempo
Object storage and compressed Parquet
“Find the trace where X happened under Y”
Tempo, or SigNoz with SQL
Structural TraceQL; SQL joins
One UI for all signals
SigNoz
Logs, metrics and traces in one product
Permissive licence everywhere
Jaeger
Apache 2.0 server and UI
Existing OpenSearch skills
Jaeger
Recommended production backend
Existing Grafana, Prometheus and Loki
Tempo
Same tenancy, same UI, links between signals
Small team, one VM
Jaeger all-in-one with Badger, or Tempo monolithic
One process each
Many internal teams as tenants
Tempo
Header-based tenancy with per-tenant limits
Search that must be complete within seconds
Jaeger or SigNoz
Indexed writes; see the drill for Tempo’s lag
My recommendation for the Kochi booking platform: put an OpenTelemetry Collector layer in front first, with load-balanced tail sampling that keeps all errors, all traces slower than two seconds and 10 percent of the rest. Then choose Tempo in microservices mode on their existing S3-compatible store, because they already run Grafana and Prometheus, they want 30 days of traces for refund disputes, and TraceQL answers the route-specific questions their support team asks. Keep Jaeger UI only if developers insist; Grafana can show the same waterfalls. Choose SigNoz instead only if the team also wants to retire its log stack and is ready to run ClickHouse with Keeper as a first-class database.
Checklist before you standardise
- Instrument with OpenTelemetry SDKs and send OTLP only to your own collectors.
- Write the sampling policy down: errors, latency threshold, baseline percentage.
- Deploy the load-balancing exporter in front of tail samplers, routed by trace ID.
- Agree on attribute names (route, tenant, customer tier) and put the searchable ones where your backend can filter them.
- Estimate bytes per day after sampling, then multiply by retention.
- Test the question your support team asks most, end to end, in the chosen backend.
- Measure how long a new trace takes to become searchable, not only to be stored.
- Put the UI and APIs behind SSO; scrub sensitive attributes in the collector.
- Practise an upgrade on a copy, including any schema migration.
- Set query limits and alerts on the backend’s own memory and ingestion lag.
Common mistakes
- Choosing the backend before designing sampling, then paying to store spans nobody reads.
- Running tail sampling on several collectors without routing by trace ID, so traces are split and decisions are wrong.
- Assuming “stored” means “searchable”. In Tempo, recent data and late-arriving spans behave differently.
- Leaving the trace UI open on an internal network without authentication.
- Putting customer data in span attributes and then retaining it for months.
- Treating Jaeger with Badger or Tempo with a local disk as production storage.
- Ignoring the licence of the exact component you run, such as an AGPL collector beside an MIT server.
A drill I ran on 13 October 2026
Environment: shared Linux VM, 8 vCPU, about 15 GiB RAM, no root, no Docker, loopback only. Between about 2:30 and 2:52 AM IST I ran:
- Jaeger 2.22.0 all-in-one with Badger storage, and Tempo 3.1.0 in monolithic mode with the local backend, both from official release archives with checksums verified.
- An OpenTelemetry Collector contrib 0.162.0 in front, fanning the same OTLP batches out to both backends.
- A second contrib collector with tail sampling (errors, latency over 2 seconds, 10 percent probabilistic,
decision_wait5 seconds). - SigNoz 0.145.0 community build without Docker: ClickHouse 25.12.5 (the version Foundry pins) as a static binary,
clickhouse keeper, SQLite metadata and SigNoz OTel Collector 0.144.13.
Workload: a seeded Python generator using the OpenTelemetry SDK sent 2,000 booking traces of 6 spans each (booking-api, search, pricing, seat hold, payment, notify), with about 3 percent payment failures and about 5 percent slow seat holds. The generator wrote a ground-truth file so I could check every answer.
Ingest (seed 31, 2,000 traces, 12,000 spans)
fan-out collector: 12,000 spans sent to each backend
Jaeger stored 12,000; Tempo distributor received 12,000
SigNoz: 12,000 spans, 2,000 traces in signoz_index_v3
Trace by ID, 50 samples
Jaeger 50/50 complete, median 1.1 ms
Tempo 50/50 complete, median 2.7 to 5.7 ms
SigNoz waterfall API, 6 spans, about 30 ms
Four searches (expected / Jaeger / Tempo / SigNoz SQL)
payment errors 60 / 60 / 60 / 60
seat holds > 2 s 96 / 96 / 96 / 96
Bengaluru route with slow seat hold 12 / 12 / 12 / 12
gold tier with failed payment 17 / 17 / 17 / 17
Tempo search over time (payment errors)
~8 s after send: 7 of 60
~15 s after send: 41 of 60
~35 s after send: 60 of 60 (first block completed)
Jaeger and SigNoz: complete on the first query
Late spans (1,000 traces stamped 5 to 10 minutes in the past, 22 errors)
by ID: Jaeger 100/100, Tempo 100/100
search: Jaeger 22/22, Tempo 0/22, still 0 after 7 minutes
Sampling (seed 33, 2,000 traces)
tail sampling kept 369 complete traces
errors 66/66, slow 116/116, others 188 of 1,819
simulated SDK head sampling at 10 percent kept 183
errors 7/66, slow 5/116
Footprint (peak RSS, disk after test)
Jaeger 243 MiB, Badger 28 MB allocated (2.1 GiB apparent, sparse prealloc)
Tempo 297 MiB, about 6 MB of blocks and WAL
SigNoz stack about 1.4 GiB: ClickHouse 869 MiB, Keeper 195, collector 315, server 114
signoz_index_v3 830.6 KiB for 12,000 spans
first SigNoz start: schema migrations 34.2 s
restart: Jaeger and Tempo kept all data; Tempo took about 30 s to stop
What this shows:
- All three returned correct answers once data was searchable, and trace by ID was fast everywhere.
- Jaeger answered the Bengaluru question only because my generator put the route and the overall duration on the root span. Tempo expressed the real parent and child question with
>>; SigNoz needed a SQL self-join. Attribute placement decides what Jaeger can find. - Tempo search is eventually consistent. New traces became fully searchable after about 35 seconds on this setup, while lookups by ID worked at once.
- Spans whose timestamps were 5 to 10 minutes old were stored and retrievable by ID in Tempo but never appeared in search. Tempo’s docs note that
ingestion_time_range_slack(2 minutes by default) can make traces outside the slack unfindable. Clock skew, retries from mobile apps and replay from queues can all produce such spans, so measure it before you rely on search. - Tail sampling kept every error and every slow trace for about 18 percent of the volume. Head sampling at 10 percent kept about one error in ten. This was the single most useful result of the drill.
- SigNoz ran without Docker, but it is a real database deployment: ClickHouse, Keeper and more than a hundred tables. Its collector got a no-op configuration through OpAMP until the first admin user existed, and some listeners bound to all interfaces by default.
Limits of the drill: one VM and loopback; one run of each step; Badger and Tempo’s local backend instead of OpenSearch and object storage; no Kafka, so no Tempo microservices mode; no multi-tenancy, UI or Jaeger adaptive sampling tests; small data where fixed overheads dominate disk numbers; timings include Python client overhead and are a smoke test, not a benchmark. Scripts, configs and results are under /workspace/revisit/drill31/.
Unlearn / re-learn
Unlearn
- Tracing means storing every span.
- Head sampling at 1 percent is good enough for incident work.
- If a trace is stored, search will find it.
- The backend’s query language is a detail.
- An open-source tracing tool has one licence.
Re-learn
- Design sampling around errors and latency, then size storage for what you keep.
- The collector layer is the stable part of the stack; backends can change behind it.
- Ask your hardest real question in each tool before choosing, including parent and child conditions.
- Practise late-arriving spans, restarts and upgrades on a small copy before the festival rush.
Closing
Self-hosted tracing in 2026 has three healthy, different answers. Learn how each one stores spans: in a search index for Jaeger, in Parquet blocks on object storage for Tempo, in ClickHouse tables for SigNoz. Unlearn the habit of sampling 1 percent at the start of a request and hoping the bad trace is in it. Re-learn the licence and edition lines, because Tempo and SigNoz’s collector are AGPLv3 and SigNoz keeps some features in a commercial edition. Practise with a small drill like the one above: one seeded workload, a known answer, and a check of how long search takes to catch up. Apply one backend behind your own OpenTelemetry Collectors, with tail sampling in place, and keep the trace UI behind SSO.
Sources
Jaeger
- Jaeger 2.22.0 GitHub release
- Jaeger 2.21.0 GitHub release
- Jaeger 2.14.0 GitHub release
- Jaeger 1.76.0 and 2.13.0 GitHub release
- Jaeger 1.63.0 and 2.0.0 GitHub release
- Jaeger UI 2.22.0 GitHub release
- Jaeger LICENSE at v2.22.0
- Jaeger docs: architecture
- Jaeger docs: storage backends
- Jaeger docs: Elasticsearch
- Jaeger docs: OpenSearch
- Jaeger docs: Cassandra
- Jaeger docs: ClickHouse
- Jaeger docs: Badger
- Jaeger docs: sampling
- Jaeger docs: service performance monitoring
- Jaeger docs: APIs
- Jaeger docs: deployment
- CNCF: Jaeger graduation announcement
Grafana Tempo
- Tempo 3.1.0 GitHub release
- Tempo 3.0.3 GitHub release
- Tempo 3.0.0 GitHub release
- Tempo 2.10.3 GitHub release
- Tempo LICENSE at v3.1.0
- Tempo LICENSING.md at v3.1.0
- Tempo docs: architecture
- Tempo docs: configuration
- Tempo docs: multi-tenancy
- Tempo docs: TraceQL
- Tempo docs: size the cluster
- Grafana blog: Grafana, Loki and Tempo relicensing to AGPLv3
SigNoz
- SigNoz 0.145.0 GitHub release
- SigNoz 0.143.0 GitHub release
- SigNoz 0.3.1 GitHub release
- SigNoz OTel Collector 0.144.13 GitHub release
- SigNoz LICENSE at v0.145.0
- SigNoz Enterprise LICENSE at v0.145.0
- SigNoz OTel Collector LICENSE at v0.144.13
- SigNoz Foundry repository
- SigNoz docs: technical architecture
- SigNoz docs: install on Linux
- SigNoz docs: retention periods
- SigNoz pricing and editions
OpenTelemetry Collector
- OpenTelemetry Collector releases 0.162.0
- Tail sampling processor README at v0.162.0
- Load-balancing exporter README at v0.162.0
- OpenTelemetry docs: sampling concepts
Security (CVE Program records)
- CVE-2023-36656
- CVE-2020-10750
- CVE-2026-59892
- CVE-2026-28377
- CVE-2026-21728
- CVE-2026-27878
- CVE-2025-2842
- CVE-2026-57955
- CVE-2026-57956
- CVE-2026-63094
- CVE-2026-92729
- CVE-2026-93292
- CVE-2026-93426
- CVE-2026-97055
- CVE-2026-97056
Internal Revisit.Tech links used in the body
Releted Posts
Lakehouse table formats in 2026: revisit Apache Iceberg, Delta Lake and Apache Hudi before you pick another format
Picture a payments and lending analytics platform in Hyderabad (an invented example, not a real client). Card authorisations and EMI schedules still land in PostgreSQL 16.
Read moreReal-time OLAP in 2026: revisit ClickHouse, Apache Doris and StarRocks before you build another dashboard warehouse
Picture an ecommerce and last-mile logistics platform in Bengaluru (an invented example, not a real client). Order capture and inventory still run on PostgreSQL 16.
Read moreDurable execution in 2026: revisit Temporal, Restate and DBOS before you hand-roll retries, sagas and cron jobs
Picture an online pharmacy and diagnostics booking company in Ahmedabad (an invented example, not a real client). An order goes through five steps: hold the medicine stock, take the UPI payment, book a courier slot, send the customer an SMS, and, if the courier cannot pick up within four hours, release the stock and refund.
Read more