Migrating from proprietary agents to OpenTelemetry: step-by-step

Inventory, dual-shipping with the Collector, auto-instrumentation per language, semantic conventions, dashboard and alert migration, validation and cutover.

Marco RossiHead of Developer Relations, Obseria
Summary
  • A safe OpenTelemetry migration runs old and new pipelines in parallel: the Collector dual-ships telemetry to your current vendor and to the new backend until both agree.
  • Most services move with zero-code auto-instrumentation and a handful of standard OTEL_* environment variables; consistent resource attributes matter more than any SDK choice.
  • Dashboards and alerts are the long pole: migrate them by use case, validate against the old system and cut over service by service, not in one big bang.

Proprietary agents made sense when every vendor had its own data model. Today OpenTelemetry covers traces, metrics and logs with one set of SDKs, one wire protocol (OTLP) and one set of naming rules, and most observability backends accept it natively. Migrating to OpenTelemetry turns instrumentation from a vendor contract into an asset you own: you can change or add backends by editing a Collector config instead of re-deploying every service.

This guide describes a step-by-step OpenTelemetry migration that we have seen work for teams replacing proprietary agents such as the Datadog agent, from inventory to final cutover. It assumes you want zero blind spots during the migration, so the core technique is dual-shipping: sending the same telemetry to the old and the new backend in parallel. If you need a refresher on the building blocks, start with what OpenTelemetry is.

Step 1: Inventory what the current agent actually does

A proprietary agent usually does more than anyone remembers. Before you touch anything, write down per environment:

  • Signals: host metrics, container and Kubernetes metrics, APM traces, log tailing, custom metrics (often via StatsD or DogStatsD), process monitoring, network checks, profiling.
  • Integrations: database, queue and web server checks the agent runs, and cloud integrations that pull metrics through provider APIs rather than through the agent.
  • Consumers: dashboards, monitors and alerts, SLOs, scheduled reports, and anything that reads data through an API, such as autoscalers or deployment gates.
  • Tags and naming: the tag conventions your teams rely on, for example env, service and version, plus team or cost-center tags.

Export the list of monitors and dashboards with their last-viewed or last-triggered dates. In most organizations a significant share has not been used for months, and every unused dashboard you delete is one you do not need to migrate.

Step 2: Deploy the Collector and dual-ship

The OpenTelemetry Collector is the pivot of the migration. Deploy it as an agent per node (a DaemonSet in Kubernetes) and, for larger setups, a gateway tier. Applications send OTLP to the local Collector, and the Collector exports to both backends. The example below uses components from the Collector contrib distribution, including the Datadog exporter and connector, so the old vendor keeps receiving data while the new backend fills up:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  hostmetrics:
    collection_interval: 30s
    scrapers:
      cpu: {}
      memory: {}
      disk: {}
      filesystem: {}
      network: {}
      load: {}

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  resourcedetection:
    detectors: [env, system]
  batch:
    timeout: 5s

connectors:
  datadog/connector: {}

exporters:
  datadog:
    api:
      key: ${env:DD_API_KEY}
      site: ${env:DD_SITE}
  otlp/obseria:
    endpoint: ${env:OBSERIA_OTLP_ENDPOINT}
    headers:
      authorization: "Bearer ${env:OBSERIA_TOKEN}"
    compression: zstd

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resourcedetection, batch]
      exporters: [datadog/connector, datadog, otlp/obseria]
    metrics:
      receivers: [otlp, hostmetrics, datadog/connector]
      processors: [memory_limiter, resourcedetection, batch]
      exporters: [datadog, otlp/obseria]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, resourcedetection, batch]
      exporters: [datadog, otlp/obseria]

The connector computes the trace-derived metrics the old backend expects, so existing APM views keep working while traces come from OpenTelemetry. If you cannot re-instrument some services yet, the contrib distribution also provides receivers that accept data from existing proprietary tracers and StatsD clients, which lets you route legacy traffic through the same Collector. Before this Collector carries production traffic, apply the hardening from our Collector production best practices: memory limits, persistent queues and self-monitoring.

Move log collection and infrastructure metrics

Application telemetry is only half of what the old agent collected. For logs, the filelog receiver tails container and application log files, parses JSON or regex formats and maps severity and timestamps to the OpenTelemetry log data model. In Kubernetes, point it at /var/log/pods and combine it with the k8sattributes processor so every record carries pod, namespace and workload attributes. For infrastructure, the hostmetrics receiver in the example covers CPU, memory, disk and network; the kubeletstats and k8s_cluster receivers add node, pod and cluster-level metrics. Many database and middleware checks have Collector receivers as well, for example for PostgreSQL, MySQL, Redis or Kafka, and anything that already exposes Prometheus metrics can be scraped with the prometheus receiver. Map each agent check from your inventory to a receiver before you remove the agent, and note the few that have no equivalent yet; those stay on the old agent until you find a replacement or decide the data is not needed.

Step 3: Switch services to OpenTelemetry auto-instrumentation

Most services can move without code changes. Each language has a zero-code option that instruments common frameworks, HTTP clients and database drivers:

  • Java: attach the OpenTelemetry Java agent, for example with JAVA_TOOL_OPTIONS=-javaagent:/otel/opentelemetry-javaagent.jar. Remove the old agent flag in the same change; two bytecode agents instrumenting the same classes cause duplicate spans and overhead. Details for JVM services are in our Java monitoring guide.
  • Python: install opentelemetry-distro, run opentelemetry-bootstrap -a install and start the service with opentelemetry-instrument python app.py.
  • Node.js: install @opentelemetry/auto-instrumentations-node and set NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register".
  • .NET: use the OpenTelemetry .NET automatic instrumentation, which is configured through the same environment variables plus its own OTEL_DOTNET_AUTO_* settings.
  • Go: there is no runtime agent in the classic sense; use the instrumentation libraries for your HTTP and gRPC frameworks, or evaluate the eBPF-based zero-code instrumentation for services you cannot change.

In Kubernetes, the OpenTelemetry Operator can inject these agents automatically through an Instrumentation resource and a pod annotation such as instrumentation.opentelemetry.io/inject-java: "true". Configuration is the same across languages:

OTEL_SERVICE_NAME=checkout
OTEL_RESOURCE_ATTRIBUTES=service.version=1.42.0,deployment.environment.name=production
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=1.0
OTEL_PROPAGATORS=tracecontext,baggage

Pay attention to context propagation during the transition. If a migrated service calls one that still runs the old tracer, both sides must understand a common header format, otherwise traces break in the middle. W3C Trace Context is the default for OpenTelemetry; several proprietary tracers can be configured to read and write it as well. Migrate along call chains where possible, starting at the edge.

Step 4: Get semantic conventions and resource attributes right

The migration is your chance to fix naming once. OpenTelemetry semantic conventions define standard attribute names, and backends that follow them can build service maps, RED metrics and correlations automatically. Map your old tags deliberately:

Typical proprietary tagOpenTelemetry attributeNotes
serviceservice.nameRequired; set via OTEL_SERVICE_NAME
versionservice.versionSet from your CI build
envdeployment.environment.nameOlder SDKs use deployment.environment
hosthost.nameAdded by the resourcedetection processor
pod, namespacek8s.pod.name, k8s.namespace.nameAdded by the k8sattributes processor
http.status_codehttp.response.status_codeCurrent HTTP semantic conventions

Keep custom tags such as team or cost center as resource attributes with a consistent prefix, and document them. Inconsistent resource attributes are the number one reason why distributed tracing views look incomplete after a migration.

Step 5: Migrate dashboards and alerts by use case

Do not translate dashboards one to one. Metric names and units differ between proprietary agents and OpenTelemetry (for example, OpenTelemetry uses seconds for durations and dotted names such as http.server.request.duration), so a mechanical translation produces broken panels. Instead:

  1. Group dashboards and alerts by use case: service health (RED metrics), infrastructure (USE metrics), business KPIs, SLOs.
  2. Rebuild one golden dashboard per use case on OpenTelemetry data, using templated variables for service and environment instead of one dashboard per service.
  3. Recreate alerts starting with those that page people. Prefer symptom-based alerts and SLO burn rates over dozens of cause-based thresholds.
  4. Store the new dashboards and alerts as code so they are reviewed and reproducible.

Step 6: Validate, then cut over service by service

Dual-shipping gives you two views of the same reality. Use them. For each service, compare request rate, error rate and latency percentiles between old and new backend over at least one full business cycle (often a week). Differences of a few percent are normal because of sampling and aggregation differences; large gaps indicate missing instrumentation or broken propagation. Also confirm that every paging alert has fired at least once in the new system, either naturally or through a controlled test.

When a service passes, remove the old agent or tracer from it and drop its data from the old exporter. When all services have passed, remove the old exporter from the Collector config. That is the cutover: a config change, not a weekend war room.

Common pitfalls in an OpenTelemetry migration

  • Two agents in one process: running a proprietary bytecode agent and the OpenTelemetry agent together leads to duplicate spans and higher overhead. Switch per service, not per library.
  • Broken traces at mixed boundaries: align propagators before migrating callers and callees separately.
  • Cardinality surprises: auto-instrumentation can emit attributes such as full URLs. Use the Collector to drop or normalize high-cardinality attributes before export.
  • Logs without trace context: configure your logging library bridge so log records carry trace_id and span_id; otherwise you lose log-to-trace correlation.
  • Unhardened Collector: without memory limits and queues, the Collector becomes a new single point of failure.
  • Forgotten consumers: autoscalers, deployment gates and reports that query the old vendor's API break silently at cutover.

A realistic OpenTelemetry migration timeline

The timeline below fits an organization with a few dozen services on Kubernetes. Large estates repeat phases 3 to 5 in waves.

PhaseDurationOutcome
1. Inventory and cleanup1 weekList of signals, integrations, dashboards and alerts to migrate
2. Collector and dual-shipping1 weekHardened Collector exporting to old and new backend
3. Auto-instrumentation2–4 weeksServices emitting OTLP with consistent resource attributes
4. Dashboards and alerts2–3 weeksGolden dashboards and paging alerts rebuilt as code
5. Validation1–2 weeksSigned-off comparison per service
6. Cutover and decommissioning1 weekOld agents and exporters removed, contract wound down

How Obseria helps with the switch

Obseria is OpenTelemetry-native: it ingests OTLP directly, follows semantic conventions and lets you export your data at any time, so the instrumentation you build in this migration stays yours. Teams with existing OpenTelemetry SDKs can see their first trace in about 15 minutes. If you are comparing options, our Obseria vs Datadog comparison covers pricing and data models. Teams moving from Datadog, Dynatrace or New Relic can apply for the switch bonus, which credits usage fees for up to three months, and our migration service helps with Collector configuration, dashboard rebuilds and the validation phase.

Share

Marco Rossi

Head of Developer Relations, Obseria

Observability that delivers answers.

Start for free with OpenTelemetry, no credit card required.