﻿---
title: Switch to OpenTelemetry
description: Migrate from classic Elastic APM agents or Beats-based data collection to Elastic OpenTelemetry.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/solutions/observability/get-started/opentelemetry/switch-to-otel
products:
  - Elastic Agent
  - Elastic Cloud Hosted
  - Elastic Cloud Serverless
  - Elastic Observability
applies_to:
  - Serverless Observability projects: Generally available
  - Elastic Stack: Generally available
  - Elastic Agent: Generally available
---

# 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](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/solutions/observability/get-started/opentelemetry/start-with-otel) instead.

## Before you begin

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

### Version requirements

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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/collectors) for the full matrix.

### What you gain

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.


### What you lose or trade off

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 <applies-to>Elastic Distribution of OpenTelemetry Browser (RUM): Preview</applies-to>                                    | 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                                                                                                                    | <applies-to>Elastic Agent: Preview since 9.2</applies-to> 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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/limitations#tail-based-sampling-tbs). |
| 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 <applies-to>Elastic Stack: Preview since 9.1</applies-to> <applies-to>Elastic Cloud Serverless: Unavailable</applies-to> | [Central configuration](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/limitations#centralized-parsing-and-processing-of-data).                                                                                                                                                                       |
| Agent health and overhead metrics              | Not available                                                                                                                    | Metrics such as `agent.events.*` have no equivalent.                                                                                                                                                                                                                                                                                                                                     |


### When to wait

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.


### Data model impact

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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/data-streams).

## Migrate app instrumentation

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 guides


| Language      | Migration guide                                                                                                                     | Key caveats                                                                                                                                                                                                                                                                                                                                                                              |
|---------------|-------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Java          | [Migrate to EDOT Java](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-java/tree/main/reference/edot-java/migration)       | 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](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-python/tree/main/reference/edot-python/migration) | 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](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-node/tree/main/reference/edot-node/migration)    | 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](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-dotnet/tree/main/reference/edot-dotnet/migration)   | 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](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-php/tree/main/reference/edot-php/migration)          | Span compression, breakdown metrics, `capture_errors`, and `sanitize_field_names` not available.                                                                                                                                                                                                                                                                                         |
| iOS           | No dedicated guide                                                                                                                  | Use the [EDOT iOS](https://docs-v3-preview.elastic.dev/elastic/apm-agent-ios/tree/main/reference/edot-ios) setup docs to replace the classic Elastic iOS APM agent.                                                                                                                                                                                                                      |
| Android       | No dedicated guide                                                                                                                  | Use the [EDOT Android](https://docs-v3-preview.elastic.dev/elastic/apm-agent-android/tree/main/reference/edot-android) setup docs to replace the classic Elastic Android APM agent.                                                                                                                                                                                                      |
| Browser / RUM | No dedicated guide                                                                                                                  | <applies-to>Elastic Distribution of OpenTelemetry Browser (RUM): Preview</applies-to> Use the [EDOT Browser](https://docs-v3-preview.elastic.dev/elastic/elastic-otel-rum-js/tree/main/reference/edot-browser) setup docs. For production RUM, continue using the [classic Elastic APM browser agent](https://docs-v3-preview.elastic.dev/elastic/apm-agent-rum-js/tree/main/reference). |


### If you use contrib OpenTelemetry SDKs or Jaeger

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](#switch-to-otel-ingestion). 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](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/solutions/observability/apm/ingest/jaeger), migrate your applications to OpenTelemetry SDKs. Jaeger clients are deprecated upstream in favor of OpenTelemetry.

### Configuration changes that apply to all languages

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.

### Ingestion path change

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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/managed-inputs/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](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/managed-inputs/managed-otlp-endpoint) for details.
- **Self-managed, ECE, or ECK**: Deploy Elastic Agent in OTel mode as a [gateway](https://docs-v3-preview.elastic.dev/elastic/elastic-agent/tree/main/reference/edot-collector/modes), 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](/elastic/docs-content/pull/8194/solutions/observability/apm/opentelemetry/attributes#elastic-distribution-of-opentelemetry-collector-edot-collector) to rename or insert resource attributes. For span, metric, or log attributes, use the [attributes processor](https://docs-v3-preview.elastic.dev/elastic/elastic-agent/tree/main/reference/edot-collector/components/attributesprocessor).
</important>


## Migrate log and metric collection

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 Elastic Agent (Fleet-managed)

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](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/reference/fleet/elastic-agent-as-otel-collector).
For OTel-native collection through Elastic Agent integrations (preview), refer to [Collect OpenTelemetry data with Elastic Agent integrations](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/reference/fleet/otel-integrations).

### If you use Beats directly

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 use standalone Elastic Agent

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](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/reference/fleet/elastic-agent-as-otel-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 already run Elastic Agent in OTel mode

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](https://docs-v3-preview.elastic.dev/elastic/elastic-agent/tree/main/reference/edot-collector/components).

## Verify your migrated data

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 applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Generally available since 9.3">
  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](/elastic/docs-content/pull/8194/solutions/observability/apm/metrics-ui#instrumentation-change).
</note>

<stepper>
  <step title="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.
  </step>

  <step title="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.
  </step>

  <step title="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.
  </step>
</stepper>

If data is missing, refer to [Troubleshoot Elastic OpenTelemetry](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/troubleshoot/ingest/opentelemetry).

## After the migration

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.

### Running both agents during transition

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.
</note>


### Clean up after migration

Once you've verified that your migrated services are sending data correctly:
<stepper>
  <step title="Remove the classic {{apm-agent}} packages">
    Remove the classic APM agent packages and startup arguments from your applications.
  </step>

  <step title="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.
  </step>

  <step title="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).
  </step>

  <step title="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.
  </step>

  <step title="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.
  </step>
</stepper>


## Related pages

- [Limitations of Elastic OpenTelemetry](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/limitations) — full list of gaps compared to classic Elastic ingestion, including when to prefer the classic stack
- [Elastic features available with Elastic OpenTelemetry](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/features) — feature compatibility matrix
- [OpenTelemetry data streams compared to classic APM](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/compatibility/data-streams) — how field names and storage structures differ
- [Elastic Agent as an OpenTelemetry Collector](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/reference/fleet/elastic-agent-as-otel-collector) — Fleet-managed and standalone OTel collection architecture
- [Elastic Agent in OpenTelemetry mode](https://docs-v3-preview.elastic.dev/elastic/elastic-agent/tree/main/reference/edot-collector) — setup and configuration
- [Managed OTLP endpoint](https://docs-v3-preview.elastic.dev/elastic/opentelemetry/tree/main/reference/managed-inputs/managed-otlp-endpoint) — Serverless and Elastic Cloud Hosted ingestion reference
- [Troubleshoot Elastic OpenTelemetry](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/8194/troubleshoot/ingest/opentelemetry) — troubleshooting for EDOT SDKs and Elastic Agent in OTel mode