Observability

What You Need to Know About Jaeger

Distributed tracing is one of those things that seems optional — until production breaks and you have no idea which service is at fault. Here's how we deploy Jaeger and the real challenges we ran into along the way.

October 2, 2023
5 min read

The purpose of this article is to demonstrate how to quickly deploy Jaeger and explain the deployment nuances we encountered. We'll cover what Jaeger actually is, why it matters, and the specific configuration decisions that made our setup production-ready.

What is Jaeger and why is it needed?

Jaeger is a distributed tracing system designed to help developers monitor and troubleshoot microservices-based applications. It tracks requests as they move through multiple services, provides a comprehensive view of your application's behaviour, and lets you pinpoint exactly where latency or failures originate.

  • Performance bottleneck identification across service boundaries
  • Root cause analysis during incidents — no more guessing which service failed
  • Release validation — confirm a new deployment didn't introduce regressions
  • Capacity planning based on real request flow data
  • Security compliance through full request audit trails

It's particularly well-suited for cloud-native and containerised environments where requests touch dozens of services before returning a response.

Our deployment approach

We rely on officially maintained Helm charts for all our Jaeger deployments. Despite the chart's size, it's straightforward to adjust for your specific needs. Here's the minimal setup to get started:

bash
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update
helm install my-jaeger jaegertracing/jaeger --version 0.71.14

We pin to a specific chart version rather than tracking 'latest'. This makes deployments reproducible and prevents surprise breaking changes in production.

Configuration challenges and how we solved them

The first real challenge was storage. Traces accumulate fast in a busy system, and Jaeger needs a backend that can handle high write throughput without becoming a bottleneck. We chose Cassandra, running two instances per deployment.

Each Cassandra instance runs with conservative resources that still handle production load comfortably:

  • 1 vCPU per instance
  • 2 GB memory per instance
  • 20 GB persistent storage per instance

The key lesson here: solutions provided in official charts don't always work out of the box. We had to configure Jaeger to start cleaning up old traces once storage crossed 80% utilisation. Without this, Cassandra would fill up and Jaeger would stop accepting spans entirely — silent data loss in production.

yaml
# values.yaml — storage cleanup configuration
storage:
  type: cassandra
  cassandra:
    keyspace: jaeger_v1_dc1
    # Trigger TTL-based cleanup before storage fills
    maxTraceAge: 168h  # 7 days
    host: cassandra-service
    port: 9042

What we learned

Jaeger is straightforward to get running but requires careful attention to storage limits and data retention policies before going to production. Start with a small retention window and expand it as you understand your actual trace volume. Two Cassandra nodes give you redundancy without significant resource overhead.