How to Install Jaeger for Distributed Tracing on RHEL 7

As systems evolve from monoliths to microservices, understanding the path of a single request across dozens of services becomes increasingly difficult using traditional logs and metrics alone. Jaeger is an open-source distributed tracing platform originally developed by Uber Technologies and now hosted by the Cloud Native Computing Foundation (CNCF). It allows you to instrument your services, collect trace data, and visualise request latency, service dependencies, and errors through an intuitive web interface. This tutorial covers installing Jaeger on RHEL 7 using the all-in-one binary for development and testing, creating a systemd service for it, and configuring a production-ready setup backed by Elasticsearch.

Prerequisites

  • RHEL 7 server with root or sudo access
  • At least 1 GB of RAM (2 GB recommended for the Elasticsearch backend)
  • curl and wget available
  • Applications instrumented with the Jaeger client SDK or OpenTelemetry (sending OTLP or Jaeger Thrift format)
  • Elasticsearch 7.x installed and running (for production setup — Step 9)

Step 1: Download the Jaeger All-in-One Binary

The Jaeger project publishes a single self-contained binary called jaeger-all-in-one that bundles the collector, query service, and UI. It stores traces in memory by default, making it ideal for development and experimentation.

JAEGER_VERSION="1.55.0"
cd /tmp
wget https://github.com/jaegertracing/jaeger/releases/download/v${JAEGER_VERSION}/jaeger-${JAEGER_VERSION}-linux-amd64.tar.gz
tar -xzf jaeger-${JAEGER_VERSION}-linux-amd64.tar.gz
cd jaeger-${JAEGER_VERSION}-linux-amd64

Install the binaries to a system-wide location:

sudo mv jaeger-all-in-one /usr/local/bin/
sudo mv jaeger-agent /usr/local/bin/
sudo mv jaeger-collector /usr/local/bin/
sudo mv jaeger-query /usr/local/bin/
sudo chmod +x /usr/local/bin/jaeger-*

Verify the binary runs correctly:

jaeger-all-in-one --version

Step 2: Understand Jaeger Port Assignments

Jaeger uses a well-defined set of ports for its various components. Understanding these is essential before opening firewall rules and configuring clients.

  • 16686 — Jaeger UI (HTTP, web browser access)
  • 14268 — Collector HTTP endpoint (Jaeger Thrift format from clients)
  • 14250 — Collector gRPC endpoint (used by the agent and OpenTelemetry Collector)
  • 6831/UDP — Agent compact Thrift (most Jaeger client libraries default to this)
  • 6832/UDP — Agent binary Thrift
  • 5778 — Agent HTTP for sampling strategies and configuration
  • 9411 — Zipkin-compatible HTTP receiver (optional, for Zipkin-instrumented apps)
  • 4317 — OTLP gRPC receiver (when configured)
  • 4318 — OTLP HTTP receiver (when configured)

Step 3: Create the Jaeger System User

Run Jaeger as a dedicated non-privileged user:

sudo useradd -r -s /sbin/nologin -d /var/lib/jaeger jaeger
sudo mkdir -p /var/lib/jaeger /etc/jaeger /var/log/jaeger
sudo chown jaeger:jaeger /var/lib/jaeger /var/log/jaeger

Step 4: Create a systemd Service for Jaeger All-in-One

Create a systemd unit file so Jaeger starts automatically and is managed like any other system service:

sudo tee /etc/systemd/system/jaeger.service <<'EOF'
[Unit]
Description=Jaeger All-in-One Distributed Tracing
Documentation=https://www.jaegertracing.io
After=network.target

[Service]
Type=simple
User=jaeger
Group=jaeger
ExecStart=/usr/local/bin/jaeger-all-in-one 
  --memory.max-traces=100000 
  --log-level=info 
  --http-server.host-port=:16686 
  --collector.http.host-port=:14268 
  --collector.grpc-server.host-port=:14250 
  --processor.jaeger-compact.server-host-port=:6831 
  --processor.jaeger-binary.server-host-port=:6832 
  --query.ui-config=/etc/jaeger/ui-config.json
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

Create a minimal UI config file:

sudo tee /etc/jaeger/ui-config.json <<'EOF'
{
  "monitor": {
    "menuEnabled": true
  },
  "dependencies": {
    "menuEnabled": true
  }
}
EOF
sudo chown jaeger:jaeger /etc/jaeger/ui-config.json

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable jaeger
sudo systemctl start jaeger
sudo systemctl status jaeger

Step 5: Configure the Firewall

Open the necessary ports for agents and the UI. Limit collector ports to trusted network ranges in production:

sudo firewall-cmd --permanent --add-port=16686/tcp
sudo firewall-cmd --permanent --add-port=14268/tcp
sudo firewall-cmd --permanent --add-port=14250/tcp
sudo firewall-cmd --permanent --add-port=6831/udp
sudo firewall-cmd --permanent --add-port=6832/udp
sudo firewall-cmd --permanent --add-port=5778/tcp
sudo firewall-cmd --reload

Step 6: Configure Your Application to Send Traces

Applications can send traces to Jaeger directly or via the Jaeger agent acting as a local UDP sidecar. For a Python application using the Jaeger client:

pip install jaeger-client opentracing

# Python tracing setup
from jaeger_client import Config

config = Config(
    config={
        'sampler': {'type': 'const', 'param': 1},
        'local_agent': {
            'reporting_host': '127.0.0.1',
            'reporting_port': 6831,
        },
        'logging': True,
    },
    service_name='my-python-service',
    validate=True,
)
tracer = config.initialize_tracer()

For applications using the OpenTelemetry SDK with OTLP export directly to Jaeger’s gRPC endpoint:

export OTEL_EXPORTER_OTLP_ENDPOINT="http://192.168.1.100:4317"
export OTEL_SERVICE_NAME="my-service"

Step 7: Configure Sampling Strategies

In production, tracing every single request is usually not cost-effective. Jaeger supports several sampling strategies. The simplest are configured per service in a JSON file served from the agent’s /sampling endpoint.

sudo tee /etc/jaeger/sampling.json <<'EOF'
{
  "service_strategies": [
    {
      "service": "api-gateway",
      "type": "probabilistic",
      "param": 0.1
    },
    {
      "service": "payment-service",
      "type": "probabilistic",
      "param": 1.0
    }
  ],
  "default_strategy": {
    "type": "probabilistic",
    "param": 0.05
  }
}
EOF

Add the sampling file to the Jaeger service in ExecStart:

--sampling.strategies-file=/etc/jaeger/sampling.json

Reload and restart after any configuration change:

sudo systemctl daemon-reload
sudo systemctl restart jaeger

Step 8: Explore the Jaeger UI

Open http://<your-server-ip>:16686 in a browser. The Jaeger UI consists of three main sections:

  • Search — Find traces by service name, operation name, tags, duration range, and time range. The tag filter accepts key=value pairs such as http.status_code=500 to find all error traces instantly.
  • Trace View — Click any trace result to open a Gantt-chart breakdown showing each span’s start time, duration, and parent-child relationships. Hover over a span to see all attached attributes (service name, HTTP method, database query text, etc.).
  • System Architecture / DAG — The Dependencies tab generates a directed acyclic graph of service-to-service call relationships based on trace data, helping visualise your microservices topology.

Step 9: Production Setup with Elasticsearch Backend

The in-memory store is lost on restart and is limited by available RAM. For production, configure Jaeger to use Elasticsearch 7 as a durable backend. First install Elasticsearch (or point to an existing cluster), then update the Jaeger service:

sudo tee /etc/systemd/system/jaeger-collector.service <<'EOF'
[Unit]
Description=Jaeger Collector with Elasticsearch
After=network.target

[Service]
Type=simple
User=jaeger
Group=jaeger
ExecStart=/usr/local/bin/jaeger-collector 
  --es.server-urls=http://localhost:9200 
  --es.index-prefix=jaeger 
  --es.num-shards=3 
  --es.num-replicas=1 
  --collector.http.host-port=:14268 
  --collector.grpc-server.host-port=:14250 
  --log-level=info
Restart=on-failure
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

Create the Elasticsearch index template before starting the collector:

docker run --rm 
  -e SPAN_STORAGE_TYPE=elasticsearch 
  -e ES_SERVER_URLS=http://localhost:9200 
  jaegertracing/jaeger-es-index-cleaner:latest 
  init

Distributed tracing with Jaeger fundamentally changes how you investigate latency problems and service dependencies in microservice architectures. Rather than correlating log lines across multiple systems by hand, a single trace ID links every operation from the initial HTTP request through every downstream service call, database query, and cache lookup. On RHEL 7, the systemd-managed deployment ensures Jaeger remains available across reboots and integrates with your existing operational tooling for log collection and service monitoring.