πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Observability intermediate ⏱ 15 minutes K8s 1.28+

OpenTelemetry Auto-Instrumentation

Configure OpenTelemetry Operator auto-instrumentation to inject tracing into pods without code changes. Supports Java, Python, Node.js, .NET, and Go.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Install OpenTelemetry Operator, create an Instrumentation CR, then annotate pods with instrumentation.opentelemetry.io/inject-java: "true" (or python/nodejs/dotnet/go). The operator injects init containers and sidecars that auto-instrument your app β€” zero code changes needed.

The Problem

Adding distributed tracing to existing applications requires code changes, SDK dependencies, and redeployment. You have dozens of services and need observability quickly without modifying source code.

The Solution

Install OpenTelemetry Operator

# Install cert-manager (required)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

# Install OTel Operator
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

Create Instrumentation CR

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: auto-instrumentation
  namespace: production
spec:
  exporter:
    endpoint: http://otel-collector.observability:4317
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "0.25"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.6.0
  python:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.47b0
  nodejs:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.52.0
  dotnet:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:1.7.0
  go:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-go:0.14.0
  env:
    - name: OTEL_RESOURCE_ATTRIBUTES
      value: "k8s.cluster.name=production,deployment.environment=prod"

Annotate Deployments

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: production
spec:
  template:
    metadata:
      annotations:
        # Pick ONE based on your language:
        instrumentation.opentelemetry.io/inject-java: "true"
        # instrumentation.opentelemetry.io/inject-python: "true"
        # instrumentation.opentelemetry.io/inject-nodejs: "true"
        # instrumentation.opentelemetry.io/inject-dotnet: "true"
        # instrumentation.opentelemetry.io/inject-go: "true"
    spec:
      containers:
        - name: payment
          image: payment-service:v3

What Gets Injected

The operator adds:

  1. Init container β€” Copies instrumentation agent to shared volume
  2. Environment variables β€” JAVA_TOOL_OPTIONS, OTEL_SERVICE_NAME, etc.
  3. Shared volume β€” Agent JAR/libraries mounted into app container
# Verify injection
kubectl get pod payment-service-xxx -o jsonpath='{.spec.initContainers[*].name}'
# Output: opentelemetry-auto-instrumentation

kubectl get pod payment-service-xxx -o jsonpath='{.spec.containers[0].env[?(@.name=="OTEL_SERVICE_NAME")].value}'
# Output: payment-service

Multi-Language Namespace

# Apply to entire namespace
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: auto-instrumentation
  namespace: production
spec:
  exporter:
    endpoint: http://otel-collector.observability:4317
  sampler:
    type: parentbased_traceidratio
    argument: "0.1"
---
# Then annotate each deployment with its language
# Java services:
#   instrumentation.opentelemetry.io/inject-java: "true"
# Python services:
#   instrumentation.opentelemetry.io/inject-python: "true"

Common Issues

IssueCauseFix
Pod stuck in InitInstrumentation image pull failsCheck image registry access
No traces appearingWrong collector endpointVerify exporter.endpoint is reachable
High latency overheadSampling rate too highSet argument: "0.1" (10% sampling)
Go instrumentation not workingRequires CGOBuild with CGO_ENABLED=1
Duplicate tracesBoth SDK and auto-inject activeRemove SDK if using auto-inject

Best Practices

  1. Start with 10-25% sampling β€” Full sampling overwhelms backends
  2. Use parentbased_traceidratio β€” Respects parent span’s sampling decision
  3. Pin instrumentation image versions β€” Avoid surprise breakage
  4. Test in staging first β€” Auto-instrumentation can affect startup time
  5. Combine with manual spans β€” Auto-instrumentation captures HTTP/DB; add custom spans for business logic

Key Takeaways

  • Auto-instrumentation injects tracing without code changes via pod annotations
  • Supports Java, Python, Node.js, .NET, and Go
  • The Operator handles init container injection and environment variable setup
  • Always configure sampling to avoid backend overload
  • Works alongside manual instrumentation (SDK spans complement auto-spans)
#opentelemetry #tracing #auto-instrumentation #observability
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens