monitoring Jaeger · ·

Jaeger 2.18 ClickHouse Backend Setup: 8.6x Compression

Set up Jaeger 2.18's new native ClickHouse tracing backend in minutes: the config file, the run command, and the storage savings to expect.

Jaeger 2.18 ClickHouse Backend Setup: 8.6x Compression

The short version

Jaeger 2.18 ships a native ClickHouse storage backend, and you turn it on with a single storage stanza in your Jaeger v2 config file. No sidecar plugin, no gRPC storage process. You point Jaeger at a ClickHouse instance, set create_schema: true, and it builds the tables on startup. On the project’s own 10 million span benchmark it hit 8.6x compression on the spans table while sustaining more than 50k spans per second of ingestion.

One caveat before you wire it into anything important: the ClickHouse backend is alpha in 2.18. Pin the exact version, keep it out of your primary production path for now, and treat it as a serious evaluation rather than a drop-in replacement for Elasticsearch or Cassandra.

Prerequisites

  • A reachable ClickHouse server (a local clickhouse-server container is fine for testing).
  • The Jaeger v2 binary or the jaegertracing/jaeger:2.18.0 image. Jaeger v2 is built on the OpenTelemetry Collector, so its config uses the collector’s extension and pipeline model, not the old v1 flags.
  • An app already exporting OTLP traces, or anything that can send OTLP to port 4317/4318.

The config

Jaeger v2 declares storage as an extension and wires it into the traces pipeline through an exporter. Save this as config-clickhouse.yaml:

extensions:
  jaeger_storage:
    backends:
      clickhouse-storage:
        clickhouse:
          addresses:
            - localhost:9000
          database: jaeger
          auth:
            basic:
              username: default
              password: password
          create_schema: true
  jaeger_query:
    storage:
      traces: clickhouse-storage

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  jaeger_storage_exporter:
    trace_storage: clickhouse-storage

service:
  extensions: [jaeger_storage, jaeger_query]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger_storage_exporter]

Three fields do the real work. addresses is the ClickHouse native protocol endpoint (port 9000, not the 8123 HTTP port). database is the target database, which should already exist. create_schema: true tells Jaeger to create the span and index tables inside that database on first start, so you do not hand-write DDL.

Run it

Start ClickHouse, then start Jaeger against the config. Both run as Docker containers, so a laptop is enough to kick the tires:

$ docker run -d --name clickhouse -p 9000:9000 clickhouse/clickhouse-server:latest
$ docker exec clickhouse clickhouse-client --query "CREATE DATABASE IF NOT EXISTS jaeger"
$ docker run --rm --name jaeger --network host -v "$(pwd)/config-clickhouse.yaml:/etc/jaeger/config.yaml" jaegertracing/jaeger:2.18.0 --config /etc/jaeger/config.yaml

The UI comes up on http://localhost:16686. Send a few OTLP spans, then confirm they landed in ClickHouse rather than trusting the UI alone:

$ docker exec clickhouse clickhouse-client --query "SELECT count() FROM jaeger.spans"

If that count climbs as traffic flows, the pipeline is healthy end to end.

What the compression actually buys you

The headline number comes from the Jaeger team’s published benchmark on a 10 million span dataset. ClickHouse stores spans in columnar form and compresses each column independently, which is why trace data (highly repetitive service names, operation names, and attribute keys) shrinks so well.

Metric Reported result
Spans table compression 8.6x
Sustained ingestion 50k+ spans/sec
Trace-by-ID retrieval ~100 ms
Typical search query under 50 ms

Those are the project’s figures on their hardware, not a promise for your cluster. Your ratio moves with attribute cardinality and how many unique tag values you carry per span. Still, an 8.6x reduction on the largest table is the kind of change that turns a storage line item you dread into one you stop thinking about, which is the same pressure driving teams toward real-time cost observability elsewhere in the stack.

Two things worth knowing

Service Performance Monitoring works without a second datastore. In v2.18 Jaeger can compute latency, call rate, and error rate directly from the spans stored in ClickHouse, so you do not need a separate Prometheus-backed metrics path just to light up the Monitor tab.

Set retention with ClickHouse TTL, not a Jaeger flag. Because the tables are plain ClickHouse tables, you control retention with a TTL clause on the spans table (for example TTL timestamp + INTERVAL 30 DAY). That keeps trace retention a database concern instead of an application one.

If you are running the collector tier that feeds this backend, the same OpenTelemetry foundations apply on the ingest side. See managing OTel collectors at scale for the fleet story, and setting up observability with OpenTelemetry if you are still standing up the tracing pipeline itself.

Start it on a staging Jaeger, point one service’s traces at it, and watch the compression ratio on your own data before you commit. Alpha or not, the storage math is hard to argue with.

Get the next article in your inbox

Practical DevOps tips, tutorials, and guides. No spam, unsubscribe anytime.