Switch to OpenTelemetry

This guide helps you move from classic Elastic APM agents, Beats, or Elastic Agent to OpenTelemetry-based collection with Elastic OpenTelemetry. Use it to replace classic APM agents, shift log and metric collection to OpenTelemetry, and plan for the data model changes that affect dashboards and queries.

If you're setting up Elastic Observability for the first time with OpenTelemetry, refer to Start using OpenTelemetry with Elastic instead.

Review the version requirements, what you gain, what you trade off, and how the data model changes before you replace agents or collection.

EDOT SDKs and Elastic Agent in OTel mode require Elastic Stack 8.16 or later for basic compatibility. For a supported configuration, use:

  • Elastic Stack 9.x, or
  • Elastic Stack 8.18 or 8.19 with Elastic Agent version 9.x. You have to keep your configuration aligned to your Stack version, not the Elastic Agent 9.x defaults.

Elastic Cloud Serverless has no version requirements. On Elastic Cloud Hosted, the Elastic Cloud Managed OTLP Endpoint requires a deployment version 9.0 or later.

Refer to Elastic Agent and Elastic Stack compatibility for the full matrix.

Switching to Elastic OpenTelemetry brings several advantages over classic Elastic APM agents. You can:

  • Use standardized OpenTelemetry APIs and conventions, which means you're not tied to a single vendor's instrumentation API.
  • Leverage the OpenTelemetry community's growing library of instrumentation packages.
  • Use a single Elastic Agent instance to run OTel-native receivers alongside existing Beat-based inputs, collecting traces, metrics, and logs through one process.
  • Optimize storage. OTel-native data is stored in LogsDB (logs and traces) and Time Series Data Streams (metrics), which are designed for scalable observability workloads.

Not every feature from the classic stack is available with Elastic OpenTelemetry yet. Review these gaps before switching:

Feature Status Notes
Real User Monitoring (RUM) / Browser Preview For production RUM, continue using the classic Elastic APM browser agent.
Universal Profiling Not available Only supported with classic Elastic ingestion (using the classic Elastic Agent).
Span compression Not available
Breakdown metrics Not available The Time spent by span type chart on the transaction views doesn't populate.
Managed tail-based sampling Not available You can run TBS in a self-managed Elastic Agent in OTel mode or any OTel-compatible Collector, with reduced metric accuracy, service map coverage, and SLO precision. Refer to Limitations.
Language runtime metrics Available with changes Metric names and attributes change. Existing dashboards built on classic metric names need updates.
Central and dynamic configuration Partial Central configuration manages a limited set of EDOT SDK settings from Kibana and propagates changes at runtime through OpAMP. EDOT .NET applies changes at startup. Settings from classic APM agent central configuration don't carry over.
Centralized log parsing using ingest pipelines Not available Process logs in the Collector instead. Refer to Limitations.
Agent health and overhead metrics Not available Metrics such as agent.events.* have no equivalent.

Consider waiting if:

  • You need production real user monitoring. EDOT Browser is in technical preview.
  • You depend on breakdown metrics to power the Time spent by span type chart on your transaction views.
  • You need managed tail-based sampling without additional operational complexity.
  • You have many custom dashboards or alerts built on classic APM field names (labels.*, numeric_labels.*) and can't absorb the query update work yet.

Migrating to Elastic OpenTelemetry changes how your data is stored in Elasticsearch, which affects existing queries, dashboards, and alerts. The key differences are:

  • Custom span and transaction attributes move from labels.* and numeric_labels.* (dots replaced by underscores) to attributes.* (dots preserved). For example, labels.customer_id becomes attributes.customer.id.
  • Resource attributes such as host name and service name move under resource.attributes.*. Many remain queryable with their ECS names (for example, service.name), but alias coverage isn't complete.
  • Data streams change from apm.app.<service> to generic.otel.
  • Runtime metric names change. For Java, jvm.memory.heap.used becomes jvm.memory.used filtered by jvm.memory.type = heap. Dashboards that target the old names don't show the relevant data.

For a complete comparison of field names and storage structures, refer to OpenTelemetry data streams compared to classic APM.

Replace each classic APM agent with the corresponding EDOT SDK. Dedicated migration guides cover package replacement, manual instrumentation API changes, and configuration mapping. For languages without a dedicated guide, use the EDOT SDK setup docs.

Language Migration guide Key caveats
Java Migrate to EDOT Java Breakdown metrics, span compression, and remote attach not available. JVM runtime metric names changed. LDAP client instrumentation missing. Micrometer off by default.
Python Migrate to EDOT Python Breakdown metrics and span compression not available. Custom AWS Lambda layer not available. Several libraries missing (aiobotocore, Sanic, pyodbc, and others). No structlog integration.
Node.js Migrate to EDOT Node.js Requires Node.js ^18.19.0 || >=20.6.0 (classic agent supports >=14.17.0). No built-in AWS Lambda or Azure Functions instrumentation. Span compression not available.
.NET Migrate to EDOT .NET Stacktrace capture and span compression not available. Central configuration available since EDOT .NET 1.4.0; configuration changes apply at startup, not at runtime.
PHP Migrate to EDOT PHP Span compression, breakdown metrics, capture_errors, and sanitize_field_names not available.
iOS No dedicated guide Use the EDOT iOS setup docs to replace the classic Elastic iOS APM agent.
Android No dedicated guide Use the EDOT Android setup docs to replace the classic Elastic Android APM agent.
Browser / RUM No dedicated guide Use the EDOT Browser setup docs. For production RUM, continue using the classic Elastic APM browser agent.

If your applications already use contrib (upstream) OpenTelemetry SDKs that send data to the APM Server OTLP intake, you don't need to change your instrumentation. That intake is a legacy path: point your OTLP exporter at the Elastic Cloud Managed OTLP Endpoint or an Elastic Agent gateway instead, as described in Ingestion path change. You can optionally move to the corresponding EDOT SDK to get Elastic support and opinionated defaults.

If you send traces through the deprecated Jaeger integration, migrate your applications to OpenTelemetry SDKs. Jaeger clients are deprecated upstream in favor of OpenTelemetry.

EDOT SDKs use standard OpenTelemetry environment variables to replace the classic APM agent's connection and identity settings. Most mappings are the same in every language. Exceptions are called out in the table:

Classic APM agent setting OpenTelemetry equivalent
server_url / SERVER_URL OTEL_EXPORTER_OTLP_ENDPOINT
secret_token OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <token>
api_key OTEL_EXPORTER_OTLP_HEADERS=Authorization=ApiKey <key>
service_name OTEL_SERVICE_NAME
service_version OTEL_RESOURCE_ATTRIBUTES=service.version=<version>
environment OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=<env>
global_labels OTEL_RESOURCE_ATTRIBUTES=key1=value1,key2=value2
hostname OTEL_RESOURCE_ATTRIBUTES=host.name=<hostname>
service_node_name / serviceNodeName OTEL_RESOURCE_ATTRIBUTES=service.instance.id=<id>
enabled / active OTEL_SDK_DISABLED — set to true to turn the SDK off (enabled=false / active=false). Applies to Python and Node.js. Java uses OTEL_JAVAAGENT_ENABLED=false; PHP uses OTEL_PHP_ENABLED=false.

For detailed configuration mappings, refer to your language's migration guide.

Classic APM agents send data directly to APM Server. EDOT SDKs use OTLP and must send data to one of these endpoints:

  • Elastic Cloud Serverless: Set OTEL_EXPORTER_OTLP_ENDPOINT to the Managed OTLP endpoint for your Serverless project. You can copy the endpoint and generate a pre-configured API key from your project's Add data wizard. Refer to Elastic Cloud Managed OTLP Endpoint for details.
  • Elastic Cloud Hosted: Set OTEL_EXPORTER_OTLP_ENDPOINT to the Managed OTLP endpoint for your deployment (requires version 9.0 or later). Elastic Agent is not required for application telemetry. You can copy the endpoint from the Application endpoints, cluster and component IDs section of your deployment in the Elastic Cloud Console. Refer to Elastic Cloud Managed OTLP Endpoint for details.
  • Self-managed, ECE, or ECK: Deploy Elastic Agent in OTel mode as a gateway, then set OTEL_EXPORTER_OTLP_ENDPOINT to the gateway's OTLP receiver address.
Important

Direct APM Server ingestion of OTel-native data from EDOT SDKs is not supported. EDOT SDKs work only when sending data through Elastic Agent in OTel mode or the Elastic Cloud Managed OTLP Endpoint.

If you previously used unmapped resource attributes that APM Server stored under labels.*, those attributes are not automatically mapped by Elastic Agent in OTel mode. To preserve mappings for queries or filters, use the resource processor to rename or insert resource attributes. For span, metric, or log attributes, use the attributes processor.

How you switch logs and metrics depends on whether you collect with Fleet-managed Elastic Agent, standalone Elastic Agent, Beats, or Elastic Agent in OTel mode.

If you use Fleet-managed Elastic Agent to collect logs and metrics, you don't need to replace your setup to get the benefits of the OTel architecture. Starting with Elastic Agent 9.2, Elastic Agent runs an embedded OTel Collector. Beat inputs are migrated to run as Beat receivers inside that Collector incrementally across releases (self-monitoring data in 9.2, some metrics inputs in 9.3, all metrics inputs in 9.4). Log inputs continue to use the previous architecture until a future release.

What this means in practice:

  • Existing Fleet-managed integrations continue to work without any configuration changes. Beat receivers run the same inputs and produce ECS-formatted data. Assets such as dashboards, alerts, and ingest pipelines remain unchanged.
  • Data collected by Beat receivers remains ECS-formatted, not OTel-native. If you want OTel-native log and metric collection, you need to replace Beat inputs with OTel-native receivers.
  • You can run both in the same Elastic Agent instance. A single elastic-agent.yml can contain an inputs/outputs section for Beat-based data alongside receivers/exporters/service.pipelines sections for OTel-native data.

For a practical reference on the Elastic Agent OTel architecture, the Beat receiver rollout across versions, and the collector type comparison, refer to Elastic Agent as an OpenTelemetry Collector.

For OTel-native collection through Elastic Agent integrations (preview), refer to Collect OpenTelemetry data with Elastic Agent integrations.

Beats (Filebeat, Metricbeat, and others) are not replaced by Elastic Agent in OTel mode in a single step. The recommended path is to migrate from Beats to Fleet-managed Elastic Agent, which then uses Beat receivers internally. This preserves your existing data structure and integrations while positioning you to adopt OTel-native receivers incrementally.

If you run standalone Elastic Agent (not Fleet-managed) and want to switch to OTel-native receivers, refer to Elastic Agent as an OpenTelemetry Collector for standalone configuration options. You can migrate incrementally: a single elastic-agent.yml can run your existing Beat-based inputs alongside OTel-native pipelines.

If you're already running Elastic Agent in OTel mode (formerly the standalone EDOT Collector) and need to update deprecated components or older Collector configurations, refer to Components included in Elastic Agent.

After migration, confirm that data is flowing correctly and that key views in Kibana are working as expected.

If a migrated service keeps the same service name (OTEL_SERVICE_NAME matching the previous service.name), it appears as the same service in the Applications UI, and its history spans both instrumentation periods.

Note

The service's Metrics tab shows callouts when it detects an instrumentation change or overlapping classic and OpenTelemetry data in the selected time range. Refer to Instrumentation changes during migration.

  1. Check signal ingestion

    1. In Kibana, go to Elastic Observability → Applications → Service inventory (or use the global search to find Service inventory) and confirm your service appears.
    2. Open the service and verify that traces, metrics, and logs are present in each tab.
    3. Check the service's Metrics tab for runtime metrics. Metric names have changed. For example, for Java, jvm.memory.heap.used is now jvm.memory.used with a jvm.memory.type = heap attribute filter.
  2. Check dashboards and saved searches

    Review custom dashboards, alerts, and saved searches that query APM or log data. Fields stored under labels.* or numeric_labels.* in classic APM are now under attributes.* in OTel-native data. Not all ECS fields have aliases in the OTel data streams, so some queries might need updating.

  3. Check alerts and SLOs

    If you have alerts or SLOs based on request volume, error rates, or metric thresholds, verify them after migration. Metric renames and sampling differences can affect baseline values and alert behavior.

If data is missing, refer to Troubleshoot Elastic OpenTelemetry.

You can run classic and EDOT instrumentation in parallel during a phased rollout, then remove the classic agents after you've verified the new path.

You can run a classic APM agent and an EDOT SDK simultaneously during a phased rollout, as long as they serve different service instances or use distinct OTEL_SERVICE_NAME values. Both ingestion paths (APM Server for classic agents, and the Elastic Cloud Managed OTLP Endpoint or Elastic Agent in OTel mode for EDOT SDKs) can be active at the same time, so you can migrate one service at a time and verify before switching the rest.

Note

Don't run both a classic APM agent and an EDOT SDK inside the same process instance. Use one or the other per service instance.

Once you've verified that your migrated services are sending data correctly:

  1. Remove the classic APM agent packages

    Remove the classic APM agent packages and startup arguments from your applications.

  2. Remove APM agent configuration

    Remove your previous APM agent configuration, including environment variables, configuration files, and language-specific configuration sections such as appsettings.json for .NET or elasticapm sections for Python.

  3. Update saved searches, dashboards, and alerts

    Review and update Kibana saved searches, dashboards, and alerts that referenced classic APM field names (labels.*, numeric_labels.*, apm.app.* data streams).

  4. Confirm the OTLP endpoint

    If you were previously sending OTel data directly to APM Server (not supported), confirm the OTEL_EXPORTER_OTLP_ENDPOINT now points to the Elastic Cloud Managed OTLP Endpoint or Elastic Agent in OTel mode.

  5. Decommission APM Server when nothing depends on it

    Keep APM Server or the Fleet-managed APM integration running while any classic APM agent still sends data to it, including the classic browser agent if you kept it for production RUM. After the last classic agent is gone, remove the APM integration from your Fleet policies or shut down your standalone APM Server.