Instana

Instana

The community for performance and observability professionals to learn, to share ideas, and to connect with others.

 View Only

Integrating OpenTelemetry with Instana for Distributed Tracing

By Angith Jayan posted 06/15/26 02:24 PM

  

Integrating OpenTelemetry with Instana for Distributed Tracing

When working with distributed systems, you need visibility into what's happening across services. OpenTelemetry has emerged as the standard for generating telemetry data, while Instana provides a powerful backend to analyse and visualise that data.

When I started exploring OpenTelemetry with Instana, my primary goal was simple: get traces flowing end‑to‑end with minimal friction, and then refine the setup as complexity increased. I began with the simplest possible approach, exporting traces directly from the application to Instana. Once that was working, I experimented with routing traces through an OpenTelemetry Collector using both HTTP and gRPC exporters. That second setup added more flexibility, especially when testing configuration changes locally without modifying application code.

In this post, I'll walk through both approaches, explain when each makes sense, and highlight the practical differences based on hands‑on experience.

Why Instana with OpenTelemetry?

OpenTelemetry and Instana complement each other well. OpenTelemetry provides a vendor‑neutral way to generate telemetry data, while Instana offers a production‑ready observability backend with deep insights into distributed systems.

Using them together gives several practical advantages:

  • Enterprise readiness - Instana provides automatic service mapping, dependency analysis, and backend insights that help teams quickly identify performance bottlenecks in production environments
  • Vendor neutrality - instrumentation stays the same even if the backend changes
  • Standardised telemetry - consistent tracing across services and languages
  • Strong ecosystem - OpenTelemetry libraries are widely available and actively maintained
  • Native OTLP support - Instana supports OTLP directly, without custom adapters

This makes it easy to start simple and evolve your observability setup over time.

OTLP: HTTP vs gRPC

The OpenTelemetry Protocol (OTLP) defines how telemetry data is transmitted. It supports two main transport options: OTLP/HTTP - Uses HTTP/1.1 or HTTP/2 with Protocol Buffers OTLP/gRPC - Uses gRPC (HTTP/2) with Protocol Buffers

You can use either HTTP or gRPC exporter in a local setup. Since both function equivalently in this scenario, select the exporter that best aligns with your existing infrastructure and preferred configuration approach.

Security Considerations

When configuring OpenTelemetry exporters, keep these security practices in mind:

  • API Key Handling: Store Instana API keys in environment variables or secrets management systems, never hardcode them in source code
  • TLS by Default: HTTPS endpoints use TLS automatically. The OpenTelemetry SDK enables TLS by default for secure connections
  • Local Testing Only: The WithInsecure() option disables TLS and should only be used for local development with the collector. Never use it in production or when connecting directly to Instana

Sending traces directly from your application to Instana


image

This diagram shows the direct export flow where your application sends traces straight to Instana's OTLP endpoint without any intermediate components.

This is the simplest possible setup: your application sends traces directly to Instana's OTLP endpoint. I started with this approach because it provides the fastest way to verify that the instrumentation is correct, traces are being generated, and data is successfully reaching Instana.

Environment Variable Configuration

The easiest setup uses standard OpenTelemetry environment variables:

export OTEL_EXPORTER_OTLP_ENDPOINT="https://<instana-otlp-endpoint>"
export OTEL_EXPORTER_OTLP_HEADERS="x-instana-key=<instana-agent-key>"

The OpenTelemetry SDK reads these automatically and configures the exporter. No code changes needed.

Programmatic Configuration

You can also configure the exporter directly in code. Below is an example using OTLP over HTTP in Go:

import (
    "context"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
    // Create OTLP HTTP exporter
    exporter, err := otlptracehttp.New(
        ctx,
        otlptracehttp.WithEndpoint("instana-otlp-endpoint"),
        otlptracehttp.WithHeaders(map[string]string{
            "x-instana-key": "instana-agent-key",
        }),
        // TLS is enabled by default for https:// endpoints
    )
    if err != nil {
        return nil, err
    }

    // Define resource attributes
    res, err := resource.New(
        ctx,
        resource.WithAttributes(
            semconv.ServiceName("my-service"),
            semconv.ServiceVersion("1.0.0"),
        ),
    )
    if err != nil {
        return nil, err
    }

    // Create tracer provider
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
    )

    otel.SetTracerProvider(tp)
    return tp, nil
}

Routing traces through an OpenTelemetry collector


image

This diagram illustrates the collector-based flow where traces are routed through an OpenTelemetry Collector before reaching Instana, enabling centralized telemetry processing and configuration.


With an OpenTelemetry Collector, traces pass through an intermediate telemetry pipeline before reaching Instana. While this adds another component, it becomes valuable when:

  • multiple services are involved
  • you want to transform, filter, or batch telemetry
  • configuration changes should not require redeploying applications
  • you may want to switch or add backends later

Running the Collector Locally

# pull the image
docker pull otel/opentelemetry-collector:<version>

# run the container
# create the config file before running the container
docker run -d \
--name otel-collector \
-p 4317:4317 \
-p 4318:4318 \
-v $(pwd)/collector-config.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector:<version>

The collector exposes:

  • 4317 for OTLP/gRPC
  • 4318 for OTLP/HTTP

Collector Configuration

Create collector-config.yaml. Below is a minimal collector configuration that:

  • receives traces via gRPC and HTTP
  • batches them
  • exports them to Instana using OTLP/HTTP
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 10s

exporters:
otlphttp:
endpoint: "https://<instana-otlp-endpoint>:443"
headers:
x-instana-key: "<instana-agent-key>"
tls:
insecure: false

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]

If you prefer OTLP/gRPC to Instana, you can change the exporters configuration to:

exporters:
otlp:
endpoint: "<instana-otlp-endpoint>:443"
headers:
x-instana-key: "<instana-agent-key>"
tls:
insecure: false

Application Configuration with Collector

Once the collector is running, point your application to it instead of Instana directly.

Environment Variable Configuration

# For gRPC
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"

# For HTTP
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"

Programmatic Configuration (gRPC)

import (
    "context"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func newExporter(ctx context.Context) (sdktrace.SpanExporter, error) {
    return otlptracegrpc.New(
        ctx,
        otlptracegrpc.WithEndpoint("localhost:4317"),
        otlptracegrpc.WithInsecure(), // Collector is local
    )
}

Programmatic Configuration (HTTP)

import (
    "context"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func newExporter(ctx context.Context) (sdktrace.SpanExporter, error) {
    return otlptracehttp.New(
        ctx,
        otlptracehttp.WithEndpoint("localhost:4318"),
        otlptracehttp.WithInsecure(), // Collector is local
    )
}

You can use either HTTP or gRPC exporter in a local setup. Since both function equivalently in this scenario, select the exporter that best aligns with your existing infrastructure and preferred configuration approach.


Conclusion

There are two straightforward ways to send OpenTelemetry traces to Instana:

Direct export works well for:

  • Development and testing
  • Single-service applications
  • Simple deployments where you don't need telemetry processing

Collector-based export becomes useful when:

  • You have multiple services sending traces
  • You need to transform or filter telemetry data
  • You want to switch backends without redeploying applications
  • You're running in production with multiple environments

Start with direct export to validate instrumentation; introduce the collector once observability becomes a shared, multi-service concern. Direct export is suitable for simple deployments. For environments involving multiple services or requiring greater flexibility, an OpenTelemetry Collector provides a more scalable solution.



#OpenTelemetry
#Tracing

2 comments
62 views

Permalink

Comments

12 days ago

If the Application is running in a Kubernetes environment a K8 processor is needed

Here you are a typical configuration in oerder to send metrics and traces to Instana;

  collector.yaml: |
    receivers:
      otlp:
        protocols:
          http:
            endpoint:
0.0.0.0:4318
    exporters:
      debug:
        verbosity:
detailed
     
otlp/metric:
        endpoint: http://instana-agent.instana-agent.svc:4317
        tls:
          insecure:
true
     
otlp/traces:
        endpoint: http://instana-agent.instana-agent.svc:4317
        tls:
          insecure:
true
    processors:
      k8sattributes:
        auth_type:
serviceAccount
        extract:
          metadata:
            - k8s.namespace.name
            - k8s.deployment.name
            - k8s.statefulset.name
            - k8s.daemonset.name
            - k8s.cronjob.name
            - k8s.job.name
            - k8s.node.name
            - k8s.pod.name
            - k8s.pod.uid
            - k8s.pod.start_time
            - container.id
        passthrough:
false
        pod_association:
          - sources:
              - from:
resource_attribute
                name:
k8s.pod.ip
          - sources:
              - from:
resource_attribute
                name:
k8s.pod.uid
          - sources:
              - from:
connection
     
probabilistic_sampler/traces:
        sampling_percentage:
100
      resource:
        attributes:
          - action:
insert
            from_attribute:
k8s.pod.uid
            key:
service.instance.id
    extensions:
      zpages:
        endpoint: localhost:55679
    service:
      telemetry:
        metrics:
          address:
0.0.0.0:8888
      pipelines:
        metrics:
          exporters:
            - otlp/metric
            - debug
          processors:
            - k8sattributes
            - resource
          receivers:
            - otlp
        traces:
          exporters:
            - otlp/traces
            - debug
          processors:
            - probabilistic_sampler/traces
            - k8sattributes
            - resource
          receivers:
            - otlp

06/17/26 05:38 AM

It would also be great to see a recommended OpenTelemetry Collector configuration for correlation included in the blog.