OpsRunway Talk to an engineer

What is OpenTelemetry, how to set it up on AWS, and what it costs a small team.

What is OpenTelemetry? The open standard for producing traces, metrics and logs from your code. It is not a monitoring product: you instrument once with its SDKs, and send the data to whichever backend you pick, CloudWatch, Grafana Cloud, Honeycomb, Datadog or your own. On AWS it is now the default way to trace, through AWS Distro for OpenTelemetry (ADOT) and CloudWatch Application Signals, because the X-Ray SDKs and daemon went into maintenance mode on 25 February 2026. Below: what each piece does, how long setup takes on ECS Fargate, EKS and Lambda, a working collector config, and what tracing costs an illustrative startup on seven backends.

Docs, prices and product status checked: 5 Nov 2026, on opentelemetry.io, AWS documentation, the AWS Price List files and each vendor's pricing page. AWS prices are us-east-1.

01Short answer

Should a small team on AWS use OpenTelemetry?

Yes, for anything new. Instrument with OpenTelemetry (or AWS's distribution of it, ADOT), send the data to CloudWatch Application Signals to start, and keep the option of moving it to another backend later by changing collector config instead of code.

QuestionAnswer on 5 Nov 2026
What is it?A standard API, SDKs, auto-instrumentation, a wire protocol (OTLP) and a collector. Not a backend: it stores and shows nothing.
Is AWS X-Ray deprecated?The X-Ray service is not. The X-Ray SDKs and daemon have been in maintenance mode, security fixes only, since 25 Feb 2026. AWS recommends moving to OpenTelemetry.
Easiest start on AWSCloudWatch Application Signals: a console toggle on Lambda, an add-on on EKS, a sidecar plus an init container on ECS. Java, Python, Node.js and .NET.
What ADOT costsNothing. You pay for the data you send and the CPU and memory the collector uses.
What tracing costs (illustrative, 20M requests a month)About $17.50 a month on Application Signals with Transaction Search; $0 to $49 on Grafana Cloud, New Relic or SigNoz; $310 on Datadog APM for 10 hosts. Section 10 has the arithmetic.
Setup effort (our estimate)Lambda: an hour or two. EKS with the CloudWatch add-on: half a day to a day. ECS Fargate: one to two days for the first service, then an hour or two each.

Before product-market fit, CloudWatch Logs, a handful of alarms and an error tracker (see what Sentry costs a startup) are usually enough; turn on tracing when a request crosses more than two services or you can't explain a slow endpoint from logs. Which monitoring vendor fits which team is in our APM tools comparison. This page is about the instrumentation layer underneath all of them.

What is OpenTelemetry, and what is it used for?

OpenTelemetry is used to instrument your code once and get traces, metrics and logs out in one format, OTLP, that any backend can read. OpenTelemetry (OTel) is a Cloud Native Computing Foundation project, formed from the merger of OpenTracing and OpenCensus. Its own docs put the main point bluntly: "OpenTelemetry is not an observability backend itself." It produces and moves telemetry; something else stores it and draws the graphs. The parts you will meet:

  • API and SDK. The API is what your code calls to start a span or record a metric. The SDK, one per language, implements it: sampling, batching, exporting.
  • Auto-instrumentation ("zero-code"). An agent or wrapper that patches common libraries, HTTP servers and clients, database drivers, AWS SDK calls, so you get traces without writing span code. For a typical Node.js or Python API this covers most of what you need.
  • OTLP. The OpenTelemetry Protocol, the format every part speaks. Every backend in this guide accepts it.
  • The Collector. A separate process that receives OTLP (and Prometheus, StatsD and others), processes it (batching, sampling, dropping attributes) and exports it to one or more backends.
  • Semantic conventions. Standard attribute names, such as http.response.status_code, so every backend understands the same data.

There are three main signals: traces (one request's path through your services, made of spans), metrics (numbers over time) and logs. Their maturity differs by language, and that matters more than the marketing suggests:

LanguageTracesMetricsLogs
JavaScript (Node.js)StableStableDevelopment
PythonStableStableDevelopment
JavaStableStableStable
.NETStableStableStable
GoStableStableRelease candidate
PHPStableStableStable
RubyStableDevelopmentDevelopment
RustBetaStableStable

So for the two most common startup stacks, Node.js and Python, traces and metrics are stable and logs are not. Our default there: keep writing logs to stdout as JSON, ship them the usual way (the awslogs driver on ECS, Fluent Bit on EKS), and put the trace ID in each line so you can jump from a trace to its logs. Use OTel logs when your language's logs SDK is stable.

Is OpenTelemetry worth it for a startup?

Yes, mainly because it makes leaving a vendor cheap. The expensive part of monitoring is not the first vendor's invoice, it's the cost of leaving. With a vendor's own tracing library, the instrumentation is welded to that vendor; moving means touching every service. With OpenTelemetry, the code emits OTLP and the backend is a few lines of collector config. That is what makes the two-to-four-week side-by-side run in our Datadog cost guide possible before you switch.

What actually stays portable, and what doesn't:

  • Portable: auto-instrumentation, any spans and metrics you add by hand through the OTel API, resource attributes such as service.name, and the collector pipeline.
  • Not quite: vendor switches in the environment. Application Signals on ECS sets OTEL_AWS_APPLICATION_SIGNALS_ENABLED, the xray sampler and the xray propagator; Datadog says OTel-instrumented data can't be used in some of its products, including Continuous Profiler and App and API Protection. These are configuration, not code, but list them before a migration.
  • Never portable: dashboards, alerts, saved queries and SLOs. They live in the backend and are rebuilt by hand. Keep them in Terraform where the backend allows it.

What is AWS Distro for OpenTelemetry (ADOT), and where do X-Ray and Application Signals fit?

ADOT is AWS's free, supported build of the OpenTelemetry SDKs, agents, Lambda layer and Collector. Around it sit X-Ray for trace storage, Application Signals for APM, CloudWatch's OTLP endpoints, and Managed Prometheus and Managed Grafana. AWS added these pieces over years and they overlap; this is where each one stands today.

PieceWhat it isStatus on 5 Nov 2026
AWS Distro for OpenTelemetry (ADOT)AWS's build of the OTel SDKs, auto-instrumentation agents, Lambda layer and Collector. AWS calls it "upstream first", not a fork.Current. Free to use; supported through AWS Support for production issues.
CloudWatch agentAWS describes it as "an AWS-managed OpenTelemetry Collector with CloudWatch components pre-built". Collects logs, metrics and traces.Current, and AWS's recommended path into CloudWatch. Version 1.300025.0 and later also accepts X-Ray SDK traces, so it can replace the X-Ray daemon.
X-Ray SDKs and daemonThe original tracing libraries and the UDP relay on port 2000.Maintenance mode since 25 Feb 2026: security fixes only, no new features. AWS's timeline lists no end date.
X-Ray (the service)Trace storage, trace map, sampling rules.Current. Accepts OTel traces through the OTLP endpoint.
CloudWatch Application SignalsAPM in CloudWatch: service map, latency, errors and faults per operation, SLOs, built on ADOT instrumentation.Current. Java, Python, Node.js, .NET. Every commercial Region except Canada West (Calgary).
Transaction SearchStores every span as structured logs in the aws/spans log group, and indexes a share (1% included) as X-Ray trace summaries.Current. Must be enabled before you send traces to the OTLP endpoint.
CloudWatch OTLP endpointsxray.<region>.amazonaws.com/v1/traces, monitoring.<region>.amazonaws.com/v1/metrics, logs.<region>.amazonaws.com/v1/logsCurrent. HTTP only, no gRPC. SigV4 auth, or a bearer API key for workloads outside AWS.
Amazon Managed Service for Prometheus (AMP)Hosted Prometheus. The ADOT Collector scrapes Prometheus endpoints and remote-writes to it with SigV4.Current. Keeps metrics 150 days by default, configurable up to 1,095 days.
Amazon Managed Grafana (AMG)Hosted Grafana for dashboards over AWS data sources such as CloudWatch and AMP.Current. Priced per editor and viewer.

One practical consequence: on AWS you now choose between two collectors. The CloudWatch agent gives the most integrated CloudWatch experience, including Application Signals and Container Insights. The ADOT Collector or the upstream OTel Collector can send the same data to CloudWatch and to any other backend at once. AWS's own FAQ says to keep the CloudWatch agent for logs and infrastructure metrics if you already run it; the two work side by side.

Is AWS X-Ray deprecated? The SDK maintenance mode, and what to do about it

No, but its SDKs are on the way out. On 25 February 2026 the X-Ray SDKs and the X-Ray daemon entered maintenance mode. AWS's timeline says releases are limited "to address security issues only" and the SDKs "will not receive new feature enhancements". The SDK READMEs on GitHub add critical bug fixes to that list. AWS's own docs page shows no end-of-support date as of today; we have seen third-party articles quote one, but couldn't confirm it in AWS documentation, so plan as if one will come.

What to do, by situation:

  • New service: don't add the X-Ray SDK. Use ADOT or upstream OTel auto-instrumentation.
  • Existing services on the X-Ray SDK: nothing breaks today; X-Ray keeps accepting their traces. Move them when you next touch the service. AWS publishes per-language migration guides for Java, Go, Node.js, .NET, Python and Ruby. Auto-instrumented services take hours; services with lots of hand-written subsegments take a day or two each, on our estimate.
  • The X-Ray daemon: replace it with the CloudWatch agent or an OTel Collector. Stop the daemon first; AWS warns about port conflicts.

Application Signals vs X-Ray vs OpenTelemetry: what's the difference?

OpenTelemetry is the instrumentation; X-Ray is one place its traces can go, so "X-Ray vs OpenTelemetry" is not either-or. X-Ray is trace storage and a trace map. Application Signals sits on top: it turns the same instrumentation into per-service and per-operation metrics (call volume, availability, latency, faults, errors), a service map and SLOs, without dashboards to build. With the ADOT SDK and the CloudWatch agent, AWS says Application Signals gets metrics on 100% of traffic even when traces are sampled; with the plain OTel SDK and Collector it only does at a 100% sampling rate, and AWS's OTLP feature table lists Application Signals as unsupported on the upstream Collector (supported on the CloudWatch agent, a custom-built Collector and the ADOT SDK sending direct). If you want Application Signals, use the CloudWatch agent. For a small team, Application Signals is the part you look at every day, and X-Ray is where you click through to a single slow request.

What is the OpenTelemetry Collector, and how do you configure it?

The Collector is a small Go binary with pipelines: receivers take data in, processors change it, exporters send it out. You don't strictly need one (SDKs can export straight to a backend), but on AWS you almost always want it, for SigV4 signing, batching, retries, and the ability to change backends without redeploying apps. This config receives OTLP from your apps and sends traces to CloudWatch and to Honeycomb at the same time, the shape you'd use while trialling a second backend:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 15
  batch:
    timeout: 5s
    send_batch_size: 1000
    send_batch_max_size: 1000

extensions:
  sigv4auth/traces:
    region: "us-east-1"
    service: "xray"

exporters:
  otlphttp/cloudwatch:
    traces_endpoint: https://xray.us-east-1.amazonaws.com/v1/traces
    compression: gzip
    auth:
      authenticator: sigv4auth/traces
  otlphttp/honeycomb:
    endpoint: https://api.honeycomb.io:443
    headers:
      x-honeycomb-team: ${env:HONEYCOMB_API_KEY}

service:
  extensions: [sigv4auth/traces]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/cloudwatch, otlphttp/honeycomb]

Notes that save an afternoon:

  • Use a distribution that has sigv4auth. It lives in the contrib repository, not the core Collector. The ADOT Collector and the contrib build both include it.
  • Exporter name. Recent upstream releases renamed otlphttp to otlp_http and keep the old name as a deprecated alias. We used the old name above because AWS's examples still do; switch when your Collector version supports the new one.
  • memory_limiter goes first, batch after it and after any sampling, as both READMEs recommend. Set GOMEMLIMIT to about 80% of the container's memory.
  • Turn on Transaction Search first. AWS's docs require it before you send traces to the OTLP endpoint. The endpoint takes up to 10,000 spans or 5 MB uncompressed per request, which is why the batch size above is capped; the batch processor's default trigger is 8,192 spans with no upper limit.
  • This sends traces to X-Ray and Transaction Search, not Application Signals. AWS's feature table lists Application Signals as unsupported with the upstream Collector. For Application Signals, put the CloudWatch agent in this position instead (section 08).
  • The task role needs permissions, for example the CloudWatchAgentServerPolicy managed policy, and never an access key baked into the image.

How do you add OpenTelemetry to a Node.js or Python app without code changes?

Add a package, set environment variables, and start the app through the instrumentation wrapper. For Node.js:

npm install --save @opentelemetry/api @opentelemetry/auto-instrumentations-node

# in the ECS task definition or Dockerfile
NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
OTEL_SERVICE_NAME=orders-api
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.2

For Python:

pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

OTEL_SERVICE_NAME=orders-api \
OTEL_TRACES_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
opentelemetry-instrument python app.py

Two Python traps. opentelemetry-distro defaults the OTLP protocol to gRPC, so without OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf it would talk gRPC to the collector's HTTP port 4318 (or point it at 4317 instead). And behind Gunicorn or uWSGI with several workers, the batch span processor is not fork-safe; the OpenTelemetry Python docs say to set up tracing in a post_fork hook, and AWS's Application Signals docs have a troubleshooting entry for pre-fork servers.

The SDK's default sampler is parentbased_always_on: every trace is kept unless the caller said otherwise. parentbased_traceidratio with 0.2 keeps 20% of new traces and always follows the caller's decision, so a trace is never half-recorded across services. The default propagators are W3C tracecontext and baggage; if any service still uses X-Ray headers, add xray to OTEL_PROPAGATORS on all of them during the migration.

For Application Signals, use AWS's ADOT packages instead (on ECS an init container copies them in; on EKS and Lambda they're injected for you). The variables are the same family, plus a few AWS-specific ones listed in section 08.

08ECS Fargate

Sources: Application Signals on ECS, sidecar setup, ADOT on ECS, Fargate task sizes, Fargate rates from the AWS Price List file for AmazonECS, us-east-1, published 19 Oct 2026.

How do you set up OpenTelemetry on ECS Fargate?

On Fargate the collector runs as a sidecar container in every task; there is no daemon option, because Fargate has no hosts you can put one on. For Application Signals the steps are:

  1. Enable Application Signals in the account once (console or the AWS::ApplicationSignals::Discovery resource), and turn on Transaction Search.
  2. Store the agent config in Parameter Store: a small JSON with "application_signals": {} under traces.traces_collected and under logs.metrics_collected.
  3. Add the ecs-cwagent sidecar to the task definition, from the CloudWatch agent image on ECR Public, reading that parameter.
  4. Add an init container from the ADOT auto-instrumentation image for your language, which copies the agent into a shared volume, and make the app container depend on it.
  5. Set the app's environment: OTEL_RESOURCE_ATTRIBUTES with service.name and deployment.environment, OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true, the exporter endpoints on localhost:4316, and the xray sampler and propagator. Python also needs OTEL_PYTHON_DISTRO=aws_distro, OTEL_PYTHON_CONFIGURATOR=aws_configurator and PYTHONPATH.
  6. Give the task role the CloudWatchAgentServerPolicy managed policy, as AWS's guide does, and the execution role read access to the parameter.

ECS doesn't discover service names; what you put in service.name is what the dashboards show, so agree a naming rule first. The tasks also need a route to ECR Public to pull the images, through a NAT gateway or a public subnet.

Effort, our estimate: one to two days for the first service, including the Terraform module, then an hour or two per extra service. A vendor-neutral version with the ADOT Collector instead of the CloudWatch agent is the same shape: swap the sidecar image and pass your own config through AOT_CONFIG_CONTENT from Parameter Store.

What the sidecar costs.

ADOT is free, but the sidecar's CPU and memory are not. If the task has no spare room and you size the sidecar at 0.25 vCPU and 0.5 GB:

Illustrative: collector sidecar on Fargate, us-east-1Arithmetic$ /month
One task, x86(0.25 × $0.04048 + 0.5 × $0.004445) × 730 h9.01
One task, Arm (Graviton)(0.25 × $0.03238 + 0.5 × $0.00356) × 730 h7.21
Ten tasks running all month, x8610 × $9.0190.10

Ten sidecars cost more than the $17.50 tracing data bill in section 10. Fargate also bills by task size, and sizes come in fixed CPU and memory pairs, so adding a sidecar can push a task to the next size up: price the real step for your task, not just the sidecar's share. Two ways to keep it down: check whether your tasks already have spare CPU and memory for a small sidecar, and for the vendor-neutral path, run one central collector service that all tasks send to instead of a sidecar per task. Application Signals on Fargate needs the sidecar.

How do you set up OpenTelemetry on EKS (Kubernetes), Lambda and EC2?

On EKS, install the CloudWatch Observability add-on or the OpenTelemetry Operator and opt pods in with an annotation. On Lambda, add the ADOT layer or tick Application Signals. On EC2, run the CloudWatch agent or a Collector as a service next to the ADOT agent.

EKS: the CloudWatch add-on, or the OpenTelemetry Operator.

The quick path is the Amazon CloudWatch Observability EKS add-on. It installs the CloudWatch agent and turns on Container Insights and Application Signals. You then opt workloads in with one pod annotation, for example instrumentation.opentelemetry.io/inject-python: "true" (also inject-java, inject-nodejs, inject-dotnet), and the add-on injects ADOT into the pods on restart. On EKS, Application Signals discovers service and cluster names itself. One catch: from add-on version 5.0.0 Application Signals is on by default, and AWS warns it affects any OpenTelemetry APM you already run, so read its opt-out notes before upgrading a cluster that has one.

The vendor-neutral path is the OpenTelemetry Operator (or AWS's ADOT add-on, which installs the ADOT Operator). You define an OpenTelemetryCollector resource for the collectors and an Instrumentation resource for exporter, sampler and propagators, then use the same style of annotation. Most installs need cert-manager first. Give the collector's Kubernetes service account an IAM role, as AWS's example does with eksctl create iamserviceaccount.

Effort, our estimate: half a day to a day with the add-on; two to four days for the Operator with your own collectors, cert-manager and IAM. If you are still deciding between the two orchestrators, our ECS vs EKS guide covers the rest of the trade.

Lambda: a layer and one environment variable.

Tick "Application Signals" in the function's monitoring settings, or add the AWS Lambda Layer for OpenTelemetry yourself and set AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument. Layers exist for Python, Node.js, Java and .NET. The current layers don't run a separate collector inside the function; the older collector-based layers are marked "not recommended". Go, Rust and Ruby have no Application Signals layer; for Go, AWS points to instrumenting with the SDK by hand and adding the ADOT Collector layer.

Lambda specifics: sampling is parent-based by default (set OTEL_TRACES_SAMPLER to change it); the execution role needs the CloudWatchLambdaApplicationSignalsExecutionRolePolicy managed policy; and functions deployed as container images can't attach layers, so you copy the layer's contents into the image at build time. The instrumentation runs inside your function, so expect some extra init time and duration on the Lambda bill; to limit cold starts, AWS enables only the AWS SDK and HTTP instrumentations by default for Python, Node.js and Java. Measure a cold start before rolling it out to latency-sensitive functions. Effort: an hour or two including the IaC change.

EC2.

Install the CloudWatch agent with Application Signals enabled and start the app with the ADOT agent for its language, or run the ADOT or upstream Collector as a systemd service. Half a day per kind of host, on our estimate, if the AMI or user data is already automated.

PlatformPathCode changesFirst service (our estimate)Each extra service
LambdaApplication Signals toggle, or ADOT layer + wrapper variableNone1 to 2 hoursMinutes
ECS FargateCloudWatch agent sidecar + ADOT init containerNone; task definition and env vars1 to 2 days1 to 2 hours
ECS Fargate, vendor-neutralADOT or upstream Collector, sidecar or central serviceNone to small2 to 3 days1 to 2 hours
EKSCloudWatch Observability add-on + annotationNoneHalf a day to 1 dayOne annotation
EKS, vendor-neutralOTel Operator, Collector, Instrumentation resourceNone2 to 4 daysOne annotation
EC2CloudWatch agent + ADOT agent, or a Collector serviceNoneHalf a day per host typeMinutes if automated
Moving off the X-Ray SDKReplace SDK calls with OTel API callsYes, for hand-written subsegmentsHalf a day to 2 daysSame

Estimates assume Terraform or CDK already manage the services and an engineer who has done this before. The first time, double them. Add a day for dashboards, alarms and an SLO or two, which is where the value is.

10Cost of tracing

Sources: CloudWatch pricing (rates read from the AWS Price List file for CloudWatch, us-east-1, published 4 Nov 2026; X-Ray file 11 Sep 2026, Managed Prometheus 14 Oct 2026, Managed Grafana 11 Sep 2026), CloudWatch Logs classes, Grafana Cloud pricing, Grafana logs billing, Honeycomb pricing, Honeycomb usage, New Relic pricing, SigNoz pricing, Datadog APM billing.

Illustrative. The traffic of the made-up startup in our Datadog guide: 20 million inbound requests a month, each calling two dependencies, 10 hosts. We assume 5 spans per request and 500 bytes per span, so 100 million spans and 50 GB a month. Measure your own; span size varies a lot with attributes.

How much does OpenTelemetry cost on AWS? Application Signals pricing vs other backends

OpenTelemetry itself costs nothing; the backend bills you for what you send. For our illustrative startup the traces alone cost between $0 and $310 a month depending on the backend, and sampling 10% of traces brings most options under $20.

Backend, list pricesHow it bills tracesAll traces, $ /month10% sampled, $ /month
CloudWatch Application Signals + Transaction Search$0.35 per GB of spans (first 10 TB); 1% indexed as trace summaries included50 × $0.35 = 17.505 × $0.35 = 1.75
Application Signals without Transaction Search$1.50 per million signals; one per inbound and one per outbound request60M × $1.50/M = 90.0090.00
X-Ray, classic traces$5 per million traces recorded, first 100,000 free19.9M × $5/M = 99.501.9M × $5/M = 9.50
Grafana Cloud Pro$19 platform fee incl. 50 GB; $0.05 per GB processed, $0.40 per GB written above that (we charge processing on every GB, as Grafana's logs billing works; its traces docs don't say)19 + 50 × $0.05 = 21.5019 + 5 × $0.05 = 19.25
Grafana Cloud Free50 GB of traces, 14-day retention0.00, at the limit0.00
HoneycombPer event; each span is an event, and so are span events and links. Free: 20M a month. Pro: from $150 for 50M, scaling up to 750M100M events: above $150; price not published per tier10M events: 0.00 on Free
New Relic100 GB a month free across all data, then $0.40 per GB, plus users0.00 + users0.00 + users
SigNoz Cloud$49 a month including $49 of usage; traces $0.30 per GB50 × $0.30 = 15, so 49.0049.00
Datadog APM$31 per APM host a month (annual), 150 GB of spans included per host; or $6 per Fargate task10 × $31 = 310.00310.00

How to read it:

  • Free allowances distort small bills. Application Signals gives 100 GB of span data and 1 million indexed spans free for the first three months per account. New Relic's 100 GB is shared with your logs and metrics, so it fills up fast if logs go there too.
  • Without Transaction Search, Application Signals bills per request, not per stored trace, as AWS's own pricing example counts it, so sampling doesn't lower that line. With Transaction Search it bills per GB, so it does. AWS's docs suggest always_on sampling with Transaction Search so you see every span; at 50 GB that's still small, but check it against your traffic.
  • Grafana's APM views cost extra. The Grafana line is Tempo storage only. Grafana's Application Observability, its service-level APM pages, is billed at $0.025 per host-hour (about $18 a host a month) on Pro.
  • Datadog's bill follows hosts, not spans. 50 GB is far inside the 1.5 TB that 10 APM hosts include. The full Datadog picture, and how to trim it, is in Datadog pricing, explained.
  • Spans stored by Transaction Search are not charged again as log ingestion, per AWS's pricing page.
  • Add the collector. On Fargate that can be $9 a month per task (section 08), which can exceed the data bill.

Metrics and logs over OTLP into CloudWatch.

OpenTelemetry metrics sent to CloudWatch cost $0.50 per GB, with 15 months of retention and no per-series charge; PromQL queries cost $0.01 per million samples, and console and dashboard queries are free. That's the way around the $0.30 per classic custom metric, which our Datadog guide warns about. Logs sent to the OTLP logs endpoint are priced like other CloudWatch Logs: $0.50 per GB in the Standard class, $0.25 in Essentials (the class formerly called Infrequent Access, which drops Live Tail, metric filters and subscription filters). On EKS, Container Insights with OpenTelemetry is $0.08 per GB. Amazon Managed Prometheus charges $0.90 per 10 million samples after the first 40 million, plus $0.04 per hour and $0.03 per 10 million samples collected if you use its managed collector instead of your own; Managed Grafana is $9 per editor and $5 per viewer a month. Where these lines sit in a whole startup bill is in how much AWS costs a startup.

How do you keep tracing costs down? Head vs tail sampling

Head-sample in the SDK, drop health checks and trim span attributes; that keeps most small teams' tracing bills low. Tail sampling keeps every error but needs its own collector tier.

Head sampling decides at the start of a request, usually in the SDK (parentbased_traceidratio). It is simple and cheap, and it's what we set up first. Its weakness, in OpenTelemetry's own words: you can't make sure every trace with an error is kept, because the decision is made before the error happens.

Tail sampling decides after the trace is complete, in the Collector's tail_sampling processor, so you can keep every error, every slow request and 10% of the rest. The catch is operational: all spans of a trace must reach the same collector instance, which, once you run more than one collector (a sidecar per task counts), means two collector layers with a load-balancing exporter in front, and the processor holds traces in memory while it waits (30 seconds and 50,000 traces by default). It is beta. For a team under 15 engineers we rarely set it up; it is a one-to-two-week job to get right, on our estimate.

What we do instead, in order:

  1. Drop what nobody reads. Health checks and readiness probes produce a trace per call. Exclude those routes in the instrumentation or with a filter in the collector.
  2. Head-sample by service. 100% on low-traffic, high-value services such as payments and sign-up; 5 to 20% on chatty internal ones. Parent-based sampling keeps traces whole.
  3. Keep errors through metrics, not traces. Application Signals and most backends compute error rates from all requests even when traces are sampled, and your error tracker keeps every exception.
  4. Trim attributes. Large request bodies or SQL text on every span inflate GB-based bills. Span size is the lever people forget.

OpenTelemetry's docs also list when not to sample at all: very low volume (tens of small traces per second or fewer) is one. Many seed-stage apps are there, and at 50 GB a month the cheaper options above cost less than an hour of anyone's time.

What is the best OpenTelemetry backend for a startup on AWS?

For most small teams that run only on AWS: CloudWatch with Application Signals, because it needs no new vendor, contract or data processing agreement, and the price scales with traffic. Grafana Cloud if you want Grafana dashboards and PromQL; Honeycomb if your hardest problems are "which customers hit this slow path"; Datadog if you already pay for it.

BackendHow OTel data gets inEU / UK dataGood fit
CloudWatch, Application Signals, X-RayCloudWatch agent, ADOT, or any Collector to the OTLP endpoints with SigV4Any AWS Region you run in, incl. London, Ireland, FrankfurtAll-in on AWS, nobody to run extra tools
Grafana Cloud (Tempo, Mimir, Loki)OTLP gateway; Grafana recommends its Alloy collector for productionUK (eu-west-2), Ireland, Germany, Sweden, Switzerland; a stack's region can't be changed laterTeams that want Grafana and PromQL, and cheap traces
HoneycombOTLP with an x-honeycomb-team key headerEU instance (api.eu1.honeycomb.io)Debugging by any attribute across many services
DatadogDDOT Collector in the Datadog Agent (recommended), an OTel Collector with the Datadog exporter, OTLP ingest in the Agent, or direct OTLPEU1 site (Germany) and a UK1 siteTeams already on Datadog; see our Datadog guide
New RelicOTLP endpoint with an api-key headerEU endpoint (otlp.eu01.nr-data.net)Many services, few engineers needing full seats
SigNoz CloudOTLP; built around OpenTelemetryUS, EU and India data centresOne OTel-native tool for traces, metrics and logs
Self-hosted: Jaeger, Grafana Tempo, SigNoz community editionJaeger v2 is built on the Collector and takes OTLP natively; Tempo needs only object storageYour AWS RegionTeams with someone who wants to run it

Self-hosting removes the vendor invoice and adds a system to patch, size and back up; our Datadog guide prices a single-instance open-source stack and the hours it takes. For a team without an infrastructure person, a managed backend is usually cheaper in total.

SOC 2 and data residency. Traces are an easy place to leak personal data: auto-instrumentation can record URLs with query strings, and people add user emails as attributes. Decide which attributes are allowed, strip the rest in the collector, and set retention on purpose. Spans in Transaction Search live in a CloudWatch log group, so CloudWatch Logs data masking and retention settings apply to them. Each SaaS backend is one more subprocessor your auditor and your customers' DPAs will ask about; keeping telemetry in your AWS account avoids that conversation. Traces are not an audit log: access and change evidence comes from CloudTrail, covered in SOC 2 on AWS.

OpenTelemetry vs Prometheus, Datadog and CloudWatch: what's the difference?

They mostly compare different layers: OpenTelemetry produces and moves telemetry, while Prometheus, Datadog and CloudWatch store and query it, and all three accept OpenTelemetry data.

OpenTelemetry vs Prometheus. Prometheus is a metrics database with a pull model: it scrapes /metrics endpoints and you query with PromQL. OpenTelemetry is instrumentation that pushes metrics, traces and logs over OTLP. They meet in the middle: the Collector's Prometheus receiver scrapes existing exporters, and Prometheus can accept OTLP metrics directly when started with --web.enable-otlp-receiver, off by default because Prometheus has no built-in authentication. If you already have Prometheus metrics, keep them, and add OTel for traces. On AWS, Managed Prometheus or CloudWatch's PromQL support stores either.

OpenTelemetry vs Datadog. Datadog is a backend with its own agent and tracing libraries. It accepts OpenTelemetry through the Datadog Agent's embedded collector, your own Collector, or direct OTLP. The trade: OTel instrumentation keeps you free to leave, while Datadog's own libraries are needed for a few Datadog products; its docs say OTel-instrumented data can't be used in Continuous Profiler, App and API Protection or Ingestion Rules. We found no separate price for OTel data: spans arrive in the same APM product, billed per APM host or Fargate task.

OpenTelemetry vs CloudWatch. Not alternatives: CloudWatch is one of the backends OpenTelemetry can send to. The real choice is between the CloudWatch agent (more integrated, CloudWatch only) and an OTel or ADOT Collector (any backend, slightly less CloudWatch integration).

OpenTelemetry best practices: what goes wrong, and how do you avoid it?

Most broken setups come down to six things: metric cardinality, collector memory, broken context propagation, missing log correlation, unnamed services and unpinned versions.

  1. Metric cardinality. Every distinct set of attribute values is a new series. Put user_id or a full URL on a metric and series multiply. CloudWatch's OTLP metrics endpoint caps a datapoint at 150 labels and 1,024 characters per value, and per-series backends bill you for every one. Keep IDs on spans, not metrics.
  2. Collector out of memory. Without memory_limiter a traffic spike or a slow backend makes the collector buffer until the container is killed, and you lose the data from exactly the incident you needed. Put it first in every pipeline and set GOMEMLIMIT.
  3. Broken context propagation. Traces stop at the first hop that doesn't pass headers: a mix of X-Ray and W3C propagators, a hand-rolled HTTP client, or a message on SQS, SNS or EventBridge where nobody added the trace context. The symptom is many short traces instead of one long one. Use the same propagator list everywhere during a migration.
  4. No log correlation. A trace that can't take you to its logs is half as useful. Inject the trace ID into every log line (check your language's OTel logging instrumentation for your logger), and on ECS set aws.log.group.names in the resource attributes so Application Signals can link them.
  5. Unnamed services. Forget service.name and everything shows up as unknown_service or, on ECS with Application Signals, UnknownService.
  6. Pinning to latest. Pin collector and instrumentation image versions like any other dependency, and upgrade on purpose. ADOT says each Collector release is supported until the next minor release.

A monitoring setup is also part of production readiness; the alerting and on-call items that go with it are on our production readiness checklist, and incident investigation on top of this data is what the AWS DevOps Agent does.

15Questions

OpenTelemetry on AWS: short answers.

What is OpenTelemetry in simple terms?

A free, open standard and set of libraries for producing traces, metrics and logs from your code and sending them anywhere over one protocol, OTLP. It doesn't store or display anything; you pair it with a backend such as CloudWatch, Grafana Cloud, Honeycomb or Datadog. Its value is that you instrument once and can change backend later.

Is AWS X-Ray deprecated?

The X-Ray service is not. The X-Ray SDKs and daemon entered maintenance mode on 25 February 2026: security fixes only, no new features. AWS recommends moving instrumentation to OpenTelemetry, which can still send traces to X-Ray. AWS's docs list no end-of-support date as of 5 November 2026.

Is OpenTelemetry free?

Yes. OpenTelemetry is an open source CNCF project, and AWS's distribution, ADOT, is free to use as well. What you pay for is the backend that stores the data, priced per GB, per event, per host or per user depending on the vendor, plus the CPU and memory your collector uses. On Fargate a collector sidecar can cost more than the tracing data itself.

What is AWS Distro for OpenTelemetry (ADOT)?

AWS's supported build of the OpenTelemetry SDKs, auto-instrumentation agents, Lambda layers and Collector, with AWS components such as the SigV4 extension and X-Ray exporter included. It costs nothing to use; you pay for the data you send to AWS services.

Does OpenTelemetry replace AWS X-Ray?

It replaces the X-Ray SDKs and daemon, not the X-Ray service. AWS now recommends instrumenting with OpenTelemetry or ADOT and sending traces to X-Ray through the CloudWatch agent, an OpenTelemetry Collector or the OTLP endpoint. X-Ray keeps storing traces and drawing the trace map, and Application Signals adds service metrics and SLOs on top of the same data.

What is the difference between Application Signals and X-Ray?

X-Ray stores traces and draws the trace map. Application Signals uses the same instrumentation to produce per-service metrics (volume, latency, faults, errors), a service map and SLOs. You use Application Signals to see what is wrong and X-Ray to look at individual requests.

How much does CloudWatch Application Signals cost?

With Transaction Search, $0.35 per GB of spans and application metrics for the first 10 TB a month in us-east-1, with 1% of spans indexed as trace summaries included. Without it, $1.50 per million signals for the first 100 million, where each inbound and outbound request is a signal. The free tier covers 100 GB for the first three months per account.

Do I need an OpenTelemetry Collector?

Not strictly: SDKs can export straight to a backend, and on Lambda the current ADOT layers need no collector. On ECS, EKS and EC2 you usually want one, for batching, retries, SigV4 signing, filtering sensitive attributes and changing backends without redeploying apps.

How do I set up OpenTelemetry on ECS Fargate?

Add a collector sidecar to each task definition (the CloudWatch agent for Application Signals, or the ADOT Collector for any backend), an init container with the ADOT auto-instrumentation for your language, and the OTEL environment variables on the app container. Budget one to two days for the first service.

Does OpenTelemetry work on AWS Lambda?

Yes. Add the AWS Lambda Layer for OpenTelemetry and set AWS_LAMBDA_EXEC_WRAPPER to /opt/otel-instrument, or tick Application Signals in the Lambda console. Layers exist for Python, Node.js, Java and .NET; Go, Rust and Ruby have no Application Signals layer. Expect some extra cold start time, and measure it before rolling out to latency-sensitive functions. Setup takes an hour or two, on our estimate.

What is the best OpenTelemetry backend?

For a small team running only on AWS, CloudWatch with Application Signals: nothing new to buy and costs that scale with traffic. Grafana Cloud suits teams who want Grafana and PromQL, Honeycomb suits deep debugging across many services, and Datadog suits teams already paying for it.

Is OpenTelemetry push or pull?

Push, by default. The SDKs export traces, metrics and logs over OTLP to a collector or straight to a backend. The Collector can also pull: its Prometheus receiver scrapes existing /metrics endpoints, so you can keep Prometheus exporters and still move the data through one OpenTelemetry pipeline. Prometheus itself is pull-based, which is the main design difference between the two.

Does Datadog support OpenTelemetry?

Yes. Datadog accepts OpenTelemetry data through the DDOT Collector in the Datadog Agent, which it recommends, OTLP ingest in the Agent, an OpenTelemetry Collector with the Datadog exporter, or direct OTLP. Spans land in the same APM product, billed per APM host or Fargate task. A few Datadog products, including Continuous Profiler and App and API Protection, can't use OTel-instrumented data.

OpenTelemetry vs Prometheus: which should I use?

Both, for different jobs. Prometheus stores metrics and scrapes them; OpenTelemetry instruments code and pushes traces, metrics and logs. The OTel Collector can scrape Prometheus exporters, and Prometheus can accept OTLP metrics when the receiver is enabled. Keep existing Prometheus metrics and add OTel for traces.

Is OpenTelemetry logging ready for production?

In some languages. Logs are stable in Java, .NET and PHP, a release candidate in Go, and still in development in JavaScript and Python. For Node.js and Python apps, keep JSON logs on stdout with the trace ID in each line, and ship them the usual way.

Should a startup sample traces?

At low volume, often not: OpenTelemetry's own docs say sampling isn't needed for tens of small traces per second or fewer. Above that, head-sample chatty services at 5 to 20% with a parent-based sampler and keep 100% on critical paths. Tail sampling is powerful but needs its own collector tier.

16Related
17Next step

If you run on Lambda, or on EKS with the CloudWatch add-on, you can probably do this yourself in a day from the steps above, and you don't need us. If you're on ECS Fargate with several services, moving off the X-Ray SDK, or want traces in two backends while you decide, we do that as a fixed-scope monitoring setup: instrumentation in Terraform, a collector config you own, dashboards, alarms and a first SLO. We don't have a set price for it yet, so we quote it after a short call about your services. Ask for a monitoring setup quote. If you'd rather have someone own monitoring along with the rest of your AWS setup, alerts tuned month by month and the bill watched, that's ordinary work on a fractional DevOps plan, or managed DevOps if you want us answering the alerts too. For a one-off look at monitoring alongside IAM, backups and cost, there's the AWS readiness review for $1,900.

StripPLAN-20
PlanEssentials
Hoursup to 20 / mo
ResponseNext business day
TermMonthly after 3 mo
Price$2,900 /mo
NextFractional DevOps plan
Talk to an engineer