Integrate Jaeger with Istio service mesh for distributed tracing

Advanced 60 min Jul 26, 2026 246 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Learn how to integrate Jaeger with Istio service mesh to get end-to-end distributed tracing across Kubernetes microservices, including sidecar injection, trace sampling, and ingress gateway access to the Jaeger UI.

Prerequisites

  • A running Kubernetes cluster version 1.27 or later with at least 3 worker nodes
  • kubectl configured with cluster-admin access
  • At least 4 vCPU and 8GB RAM available for Istio and Jaeger components
  • Helm 3 installed
  • Basic familiarity with Kubernetes networking and Istio traffic management concepts

What this solves

When requests flow through dozens of microservices behind an Istio service mesh, pinpointing latency or failures without distributed tracing is nearly impossible. This tutorial integrates Jaeger with Istio so every Envoy sidecar automatically emits spans, giving you a full request timeline across services without touching application code.

You will install Istio with tracing enabled, deploy Jaeger as the tracing backend, configure sampling and telemetry, and verify trace propagation end to end through the ingress gateway.

Prerequisites and Kubernetes cluster preparation

Confirm cluster access and resources

You need a running Kubernetes cluster (1.27+) with at least 3 worker nodes, 4 vCPU and 8GB RAM free for Istio control plane, Jaeger, and demo workloads. Verify kubectl access before continuing.

kubectl version --short
kubectl get nodes
kubectl cluster-info

Install prerequisite tools

You need curl, istioctl, and Helm to install Istio and Jaeger components.

sudo apt update && sudo apt install -y curl tar unzip
sudo dnf install -y curl tar unzip

Download and install istioctl

The istioctl binary manages the Istio installation profile and lets you enable tracing at install time.

curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.23.2 sh -
cd istio-1.23.2
export PATH=$PWD/bin:$PATH
istioctl version --remote=false

Install Helm

Helm is required to deploy Jaeger with the official chart in a repeatable way.

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Installing Istio service mesh with tracing enabled

Create the istio-system namespace

All Istio control plane components and observability tools live in this dedicated namespace.

kubectl create namespace istio-system

Install Istio with tracing configuration

The demo profile enables the ingress gateway and sets a default trace sampling percentage. Production deployments should use a custom IstioOperator manifest instead.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  meshConfig:
    enableTracing: true
    defaultConfig:
      tracing:
        sampling: 10.0
    extensionProviders:
    - name: jaeger
      opentelemetry:
        service: jaeger-collector.istio-system.svc.cluster.local
        port: 4317
  values:
    global:
      proxy:
        tracer: "openelemetry"
istioctl install -f istio-config.yaml -y

Enable automatic sidecar injection

Label the namespace where your microservices run so Istio automatically injects the Envoy sidecar into every pod.

kubectl create namespace demo-app
kubectl label namespace demo-app istio-injection=enabled
Note: If you already manage RBAC for this cluster, review configuring Kubernetes RBAC with service accounts and cluster roles before granting broader access to the istio-system namespace.

Deploying Jaeger and configuring the tracing backend

Add the Jaeger Helm repository

The official Jaegertracing chart deploys the collector, query service, and UI as separate components, which scales better than the all-in-one image.

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

Deploy Jaeger with production values

This example uses in-memory storage for a quick start. For a durable backend with retention and encryption, see the Elasticsearch backend guide linked below.

provisionDataStore:
  cassandra: false
  elasticsearch: false

storage:
  type: memory

collector:
  service:
    otlp:
      grpc:
        name: otlp-grpc
        port: 4317
      http:
        name: otlp-http
        port: 4318

query:
  service:
    type: ClusterIP
helm install jaeger jaegertracing/jaeger \
  -n istio-system \
  -f jaeger-values.yaml

Confirm Jaeger components are running

You should see collector, query, and agent pods (if enabled) in a Running state.

kubectl get pods -n istio-system -l app.kubernetes.io/name=jaeger
Warning: In-memory storage loses all trace data on pod restart. Do not use this in production. Use Elasticsearch or Cassandra as documented in configuring Jaeger with Elasticsearch backend security and encryption.

Configuring Istio telemetry and trace sampling rates

Create a Telemetry resource

Istio's Telemetry API controls sampling rate and trace propagation headers mesh-wide or per namespace. A 100 percent sample rate is useful for testing but too expensive for production traffic volumes.

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: mesh-tracing
  namespace: istio-system
spec:
  tracing:
  - providers:
    - name: jaeger
    randomSamplingPercentage: 10.00
kubectl apply -f telemetry.yaml

Override sampling for a specific namespace

You can apply a higher sampling rate temporarily in a staging or debugging namespace without affecting production traffic elsewhere in the mesh.

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: debug-tracing
  namespace: demo-app
spec:
  tracing:
  - providers:
    - name: jaeger
    randomSamplingPercentage: 100.00
kubectl apply -f telemetry-debug.yaml

For high-volume production environments, tune sampling with head-based and tail-based strategies as covered in setting up Jaeger sampling strategies for high-volume production tracing.

Enabling automatic sidecar trace propagation

Deploy a sample multi-service application

Istio's Envoy sidecars generate spans automatically, but your application code must forward the trace context headers on outbound calls, otherwise spans from different services will not link together.

kubectl apply -n demo-app -f https://raw.githubusercontent.com/istio/istio/release-1.23/samples/bookinfo/platform/kube/bookinfo.yaml

Verify header propagation in application code

Each service in the call chain must propagate these headers from incoming requests to any outgoing requests it makes: x-request-id, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags, and b3 or traceparent for W3C trace context.

kubectl logs -n demo-app deploy/productpage-v1 -c istio-proxy | grep -i trace

Expose the sample app through the ingress gateway

This lets you generate real traffic through the mesh entry point to validate end-to-end tracing.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
  namespace: demo-app
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
  namespace: demo-app
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    route:
    - destination:
        host: productpage
        port:
          number: 9080
kubectl apply -f bookinfo-gateway.yaml

Verifying distributed traces across microservices

Generate traffic

Send repeated requests through the ingress gateway to generate a trace chain across productpage, details, reviews, and ratings services.

export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
for i in $(seq 1 20); do curl -s -o /dev/null http://$INGRESS_HOST/productpage; done

Port-forward the Jaeger query UI

Access Jaeger locally to confirm traces are arriving with all expected spans.

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

Open http://localhost:16686, select the productpage.demo-app service, and search for recent traces. Each trace should show connected spans from productpage through details, reviews, and ratings.

Integrating Jaeger UI with Istio ingress gateway

Create a dedicated gateway for Jaeger

Exposing Jaeger through the Istio ingress gateway avoids running a separate load balancer just for observability tools.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: jaeger-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http-jaeger
      protocol: HTTP
    hosts:
    - "jaeger.example.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: jaeger-vs
  namespace: istio-system
spec:
  hosts:
  - "jaeger.example.com"
  gateways:
  - jaeger-gateway
  http:
  - route:
    - destination:
        host: jaeger-query
        port:
          number: 16686
kubectl apply -f jaeger-gateway.yaml

Point DNS and test access

Add an A record for jaeger.example.com pointing to the ingress gateway's external IP, then confirm the UI loads.

curl -s -o /dev/null -w "%{http_code}\n" -H "Host: jaeger.example.com" http://203.0.113.10/
Warning: Do not expose the raw Jaeger UI without authentication on a public IP. Add OAuth2 and RBAC as described in configuring Jaeger authentication with OAuth2 and RBAC for enterprise security before exposing it beyond your internal network.

Verify your setup

kubectl get pods -n istio-system
kubectl get telemetry -A
istioctl proxy-config log deploy/productpage-v1.demo-app
kubectl exec -n demo-app deploy/productpage-v1 -c istio-proxy -- curl -s localhost:15000/stats | grep tracing

A healthy setup shows all Istio and Jaeger pods Running, at least one Telemetry resource applied, and non-zero tracing counters on the sidecar's admin stats endpoint.

Common issues

SymptomCauseFix
Traces show single spans with no parent-child linksApplication not propagating trace headers on outbound callsForward incoming x-request-id, x-b3-*, and traceparent headers on every outbound HTTP client call
No traces appear in Jaeger UI at allSampling percentage set too low or Telemetry resource not applied to the right namespaceCheck kubectl get telemetry -A and temporarily raise randomSamplingPercentage to 100 for testing
Jaeger collector pod crashloopingOTLP gRPC port misconfigured or storage backend unreachableCheck kubectl logs -n istio-system deploy/jaeger-collector and confirm storage.type matches your backend
Sidecar not injected into podsNamespace missing the istio-injection labelRun kubectl label namespace demo-app istio-injection=enabled then restart the deployment
Jaeger UI returns 502 through ingress gatewayVirtualService pointing to wrong service name or portConfirm the Jaeger query service name with kubectl get svc -n istio-system and match the port in the VirtualService
High trace volume overwhelming storage backendSampling rate too high for production trafficLower randomSamplingPercentage and use tail-based sampling for error and slow-request capture

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning for the tracing pipeline, storage retention, sampling tuning as traffic grows, and on-call coverage when the collector falls behind. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Vous ne voulez pas gérer cela vous-même ?

Nous gérons l'infrastructure des entreprises qui dépendent de leur disponibilité. Entièrement infogéré, avec un interlocuteur fixe qui connaît votre environnement.

Vous avez un interlocuteur fixe qui connaît votre installation

Rotterdam 19:52 · joignable par message, sans formulaire de ticket