Choosing a Jaeger Storage Backend for Production Tracing
Choose between Elasticsearch, Cassandra, and in-memory storage for Jaeger based on span volume, retention, and operational capacity.
03 Jul 2025, 14:50 UTC

The decision and your constraints
Before deploying Jaeger in production, choose a storage backend for the collector. The backend determines whether trace data survives a restart, how fast queries return, and how much infrastructure your team must operate. The Jaeger All-In-One image uses in-memory storage by default, which is convenient for local testing but unsuitable for production retention because data is lost when the container stops.
Evaluate these constraints first:
- Write throughput: how many spans per second your services generate.
- Query latency: how quickly the Jaeger UI can find and display traces.
- Data retention: how long you need to keep traces for debugging and SLO review.
- Operational footprint: cluster size, scaling, backups, and upgrade effort.
Supported backends compared
Jaeger supports pluggable storage through the collector. The three common options are in-memory, Elasticsearch, and Apache Cassandra. Backend support varies by Jaeger release, so verify that your installed version exposes the storage plugin you plan to use.
| Backend | Persistence | Write throughput | Query latency | Operational complexity | Best fit |
|---|---|---|---|---|---|
| In-Memory | None; data lost on restart | Low to medium | Very low | Low | Local development only |
| Elasticsearch | Replicated shards | Medium | Low to medium | Medium | Teams needing rich search and aggregations |
| Cassandra | Distributed, tunable consistency | High | Medium | Medium to high | High-span-volume production workloads |
Trade-offs by workload
In-Memory
In-memory storage keeps spans in the collector process RAM. It has the lowest query latency because there is no network call to a separate database, but a restart or crash wipes all stored traces. Do not use it for production trace retention beyond a few minutes.
Elasticsearch
Elasticsearch stores spans as documents in replicated shards. It supports full-text search and aggregations, which helps when querying traces by service, operation, or tag. The main risk is shard mis-sizing: too few shards can create write bottlenecks, while too many small shards increase heap usage and query overhead. You must operate and size an Elasticsearch cluster alongside Jaeger.
Cassandra
Cassandra is a wide-column store designed for high write throughput and linear scaling. It suits environments generating millions of spans per day. Trade-offs include tunable consistency levels, compaction tuning, and more operational overhead than Elasticsearch. Query latency is typically higher than Elasticsearch for ad-hoc searches because Cassandra is optimized for writes and known access patterns.
Deploy and validate the collector
Set the storage backend through environment variables on the Jaeger Collector. The examples below show where to run the commands and what to check. Replace placeholder values with your own endpoints.
Elasticsearch backend
Run this on a host with Docker access and network reachability to an Elasticsearch cluster. The user running Docker needs permission to start containers and bind ports.
docker run --rm --name jaeger-collector-es \
-e COLLECTOR_STORAGE_TYPE=elasticsearch \
-e COLLECTOR_ELASTICSEARCH_ENDPOINTS=http://:9200 \
-e COLLECTOR_ELASTICSEARCH_USERNAME= \
-e COLLECTOR_ELASTICSEARCH_PASSWORD= \
-p 14268:14268 \
jaegertracing/jaeger-collector:
Expected check: the container starts without connection errors in the logs. Send a sample trace using an OTLP exporter configured to send to the collector endpoint, then open the Jaeger Query UI at http://:16686 and confirm the trace appears. Measure query latency from the UI response time.
Cassandra backend
Run this where the host can reach Cassandra contact points. Required permissions are the same: container runtime access and port binding.
docker run --rm --name jaeger-collector-cassandra \
-e COLLECTOR_STORAGE_TYPE=cassandra \
-e COLLECTOR_CASSANDRA_CONTACT_POINTS=:9042,:9042 \
-e COLLECTOR_CASSANDRA_KEYSPACE=jaeger_v1 \
-e COLLECTOR_CASSANDRA_USERNAME= \
-e COLLECTOR_CASSANDRA_PASSWORD= \
-p 14268:14268 \
jaegertracing/jaeger-collector:
Expected check: the collector logs show a successful schema or connection handshake. Query the Jaeger UI after sending a test trace. If queries time out, check Cassandra consistency settings and compaction status.
In-Memory backend (validation only)
docker run --rm --name jaeger-all-in-one \
-p 16686:16686 \
jaegertracing/jaeger-all-in-one:
Send a trace, confirm it appears in the UI, then restart the container. The trace will no longer be queryable, confirming that in-memory storage does not persist data across restarts.
Verification and limitations
To validate your choice, compare write throughput, query latency, and collector resource usage across backends using a representative trace volume. Start with a small load test, then increase spans per second until the collector or storage backend shows saturation.
Limitations:
- In-memory storage is not safe for production retention.
- Elasticsearch and Cassandra require separate cluster management; misconfiguration can cause data loss or high latency.
- Collector CPU and memory limits must be tuned for the chosen backend to avoid becoming a bottleneck.
Practical check: after deployment, query a trace by service name and operation in the Jaeger UI. If results return within your latency SLA and the collector logs show no storage errors, the backend is accepting and serving spans. Re-test after any Jaeger or storage cluster upgrade because plugin support can change between releases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.