Configure Jaeger distributed tracing on Kubernetes cluster with Helm charts and Elasticsearch backend

Intermediate 45 min Jun 06, 2026 105 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Deploy Jaeger distributed tracing on Kubernetes using Helm charts with Elasticsearch backend storage. Configure ingress, SSL certificates, and Prometheus integration for production-ready distributed tracing observability.

Prerequisites

  • Kubernetes cluster with kubectl access
  • Ingress controller (nginx-ingress recommended)
  • At least 8GB RAM available for Elasticsearch cluster
  • Storage class configured for persistent volumes
  • DNS configured for ingress hostname

What this solves

Jaeger distributed tracing helps you monitor and troubleshoot microservices by tracking requests across multiple services. This tutorial sets up Jaeger on Kubernetes with Helm charts, using Elasticsearch for persistent storage and includes production configuration with SSL certificates and Prometheus monitoring. You'll get a complete distributed tracing solution that can handle production workloads.

Step-by-step configuration

Install Helm package manager

Install Helm to manage Kubernetes packages and dependencies. Helm simplifies deploying complex applications like Jaeger on Kubernetes clusters.

curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

Add Jaeger Helm repository

Add the official Jaeger Helm chart repository and update the local cache to access the latest chart versions.

helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm repo update

Create namespace for Jaeger

Create a dedicated Kubernetes namespace to isolate Jaeger components from other applications running in your cluster.

kubectl create namespace jaeger-system

Deploy Elasticsearch for storage

Deploy Elasticsearch as the backend storage for Jaeger traces. This provides persistent storage and enables complex queries on trace data.

helm repo add elastic https://helm.elastic.co
helm repo update
replicas: 3
resources:
  requests:
    cpu: 1000m
    memory: 2Gi
  limits:
    cpu: 2000m
    memory: 4Gi
volumeClaimTemplate:
  storageClassName: "standard"
  resources:
    requests:
      storage: 50Gi
esConfig:
  elasticsearch.yml: |
    cluster.name: jaeger-elasticsearch
    network.host: 0.0.0.0
    bootstrap.memory_lock: false
    discovery.seed_hosts: "elasticsearch-master-headless"
    cluster.initial_master_nodes: "elasticsearch-master-0,elasticsearch-master-1,elasticsearch-master-2"
helm install elasticsearch elastic/elasticsearch -n jaeger-system -f elasticsearch-values.yaml

Configure Jaeger with Elasticsearch backend

Create Jaeger configuration that connects to Elasticsearch and includes production-ready settings with resource limits and replicas.

provisionDataStore:
  cassandra: false
  elasticsearch: true

storage:
  type: elasticsearch
  elasticsearch:
    host: elasticsearch-master
    port: 9200
    scheme: http
    user: ""
    password: ""
    nodesWanOnly: false
    extraEnv:
      - name: ES_SERVER_URLS
        value: http://elasticsearch-master:9200
    cmdlineParams:
      es.num-shards: 5
      es.num-replicas: 1
      es.index-prefix: jaeger

query:
  enabled: true
  replicaCount: 2
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 256m
      memory: 256Mi
  service:
    type: ClusterIP
    port: 16686

collector:
  enabled: true
  replicaCount: 2
  resources:
    limits:
      cpu: 1
      memory: 1Gi
    requests:
      cpu: 500m
      memory: 512Mi
  service:
    type: ClusterIP
    grpc:
      port: 14250
    http:
      port: 14268
    zipkin:
      port: 9411

agent:
  enabled: true
  daemonset:
    useHostPort: true
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
    requests:
      cpu: 128m
      memory: 128Mi

Deploy Jaeger with Helm

Install Jaeger using the Helm chart with your custom configuration. This deploys all Jaeger components including collector, query, and agent.

helm install jaeger jaegertracing/jaeger -n jaeger-system -f jaeger-values.yaml

Configure ingress for Jaeger UI

Set up ingress to expose the Jaeger UI externally with SSL termination. This allows secure access to the Jaeger web interface.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jaeger-ingress
  namespace: jaeger-system
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - jaeger.example.com
    secretName: jaeger-tls
  rules:
  - host: jaeger.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jaeger-query
            port:
              number: 16686
kubectl apply -f jaeger-ingress.yaml

Set up cert-manager for SSL certificates

Install cert-manager to automatically provision and manage SSL certificates for the Jaeger ingress. This ensures secure HTTPS access.

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
kubectl apply -f letsencrypt-issuer.yaml

Configure Prometheus monitoring

Set up Prometheus ServiceMonitor to collect metrics from Jaeger components. This enables monitoring of Jaeger performance and health.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: jaeger-collector
  namespace: jaeger-system
  labels:
    app: jaeger
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: jaeger
      app.kubernetes.io/component: collector
  endpoints:
  - port: admin-http
    interval: 30s
    path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: jaeger-query
  namespace: jaeger-system
  labels:
    app: jaeger
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: jaeger
      app.kubernetes.io/component: query
  endpoints:
  - port: admin-http
    interval: 30s
    path: /metrics
kubectl apply -f jaeger-servicemonitor.yaml

Configure Jaeger production settings

Apply production-ready configuration including resource limits, health checks, and logging settings for optimal performance.

apiVersion: v1
kind: ConfigMap
metadata:
  name: jaeger-production-config
  namespace: jaeger-system
data:
  collector.yaml: |
    receivers:
      jaeger:
        protocols:
          grpc:
            endpoint: 0.0.0.0:14250
          thrift_http:
            endpoint: 0.0.0.0:14268
          thrift_compact:
            endpoint: 0.0.0.0:6831
          thrift_binary:
            endpoint: 0.0.0.0:6832
      zipkin:
        endpoint: 0.0.0.0:9411
    processors:
      batch:
        timeout: 1s
        send_batch_size: 1024
        send_batch_max_size: 2048
      memory_limiter:
        limit_mib: 512
    exporters:
      elasticsearch:
        endpoints: [http://elasticsearch-master:9200]
        index: jaeger-span
        mapping:
          enabled: false
    service:
      pipelines:
        traces:
          receivers: [jaeger, zipkin]
          processors: [memory_limiter, batch]
          exporters: [elasticsearch]
      extensions: [health_check]
kubectl apply -f jaeger-production-config.yaml

Verify your setup

Check that all Jaeger components are running correctly and can collect traces.

kubectl get pods -n jaeger-system
kubectl get svc -n jaeger-system
kubectl logs -n jaeger-system deployment/jaeger-collector
kubectl logs -n jaeger-system deployment/jaeger-query

Test the Jaeger UI is accessible:

kubectl port-forward -n jaeger-system svc/jaeger-query 16686:16686

Open your browser to http://localhost:16686 to access the Jaeger UI. You can also test trace ingestion:

curl -X POST http://localhost:14268/api/traces \
  -H "Content-Type: application/json" \
  -d '{"data":[{"traceID":"1","spanID":"1","operationName":"test-span","startTime":1234567890,"duration":1000,"process":{"serviceName":"test-service","tags":[]},"tags":[]}]}'

Common issues

SymptomCauseFix
Elasticsearch pods failing to startInsufficient memory or storageIncrease resources in elasticsearch-values.yaml and redeploy
Jaeger collector can't connect to ElasticsearchNetwork policy or service name mismatchCheck service names with kubectl get svc -n jaeger-system
SSL certificate not provisionedDNS not pointing to cluster or cert-manager issuesCheck cert-manager logs: kubectl logs -n cert-manager deployment/cert-manager
High memory usage in collectorLarge trace volumes without proper batchingAdjust batch processor settings in production config
Traces not appearing in UIApplication not configured to send tracesConfigure applications with Jaeger endpoint: http://jaeger-collector:14268
Ingress returns 502 errorJaeger query service not readyCheck query pod status: kubectl get pods -n jaeger-system -l app.kubernetes.io/component=query
Storage considerations: Elasticsearch requires significant storage for trace data. Monitor disk usage and configure index lifecycle policies to prevent storage exhaustion. A busy cluster can generate gigabytes of trace data daily.
Performance tip: For high-throughput environments, consider increasing collector replicas and configuring sampling strategies to reduce trace volume while maintaining observability coverage.

Next steps

Running this in production?

Want this handled for you? Setting up Jaeger once is straightforward. Keeping it patched, monitored, backed up and tuned across environments is the harder part. See how we run infrastructure like this for European teams building distributed systems.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.