Skip to main content
AWS Lambda functions can be instrumented two ways: Odigos-managed, where the connector attaches everything for you, or self-managed, where you attach it yourself through your own IaC. This page is about the self-managed path — see Odigos-managed vs. self-managed instrumentation if you haven’t decided which one you want yet.
Terraform and a plain aws lambda update-function-configuration CLI command are also available for every language shown here — the Custom access-level policy preview in the Central UI surfaces all four formats for a discovered function. This page covers the two most common: CDK and CloudFormation.

What actually gets added to your function

However you apply it, self-managed Lambda instrumentation is the same four things:
  1. The OpenTelemetry layer — a prebuilt Lambda layer for your function’s language and architecture.
  2. An exec wrapper (AWS_LAMBDA_EXEC_WRAPPER) — tells Lambda to run your handler through the layer’s instrumentation code first. The exact path is different per language.
  3. The bundled collector’s configuration (OPENTELEMETRY_COLLECTOR_CONFIG_URI) — tells the small OpenTelemetry collector that ships inside the layer where to send your telemetry. It runs in the same Lambda sandbox and forwards data after your handler already returned, so a slow destination costs extra billed time, not extra response latency.
  4. The ODIGOS_IAC marker — a small JSON value that tells Odigos “this function is instrumented, and here’s the layer version it’s running.” Without it, Odigos has no way to tell the difference between an instrumented function and one you haven’t touched yet — it will keep reporting the function as Not Instrumented / Waiting for you, even if everything else above is already in place.
The CDK construct sets all four for you. With CloudFormation, you set them by hand — one per-language tab below shows exactly what to add.

Using AWS CDK

The easiest option if your functions are already defined in a CDK app. One package covers every supported language — the construct looks at each function’s runtime and figures out the right layer and wrapper on its own.
TypeScript and JavaScript only, for now — a Python distribution is built but not yet published. Python CDK apps should use the CloudFormation instructions below.
That’s it — this one call attaches the layer, sets the wrapper, configures the bundled collector (using the same renderer the connector itself uses, so the values match exactly), and writes ODIGOS_IAC for every function you list.
Don’t also enable a CDK-instrumented function as an Odigos-managed source. Every cdk deploy would reassert the CDK’s configuration, every connector reconcile would reassert its own, and you’d get a new function version on every round trip.

Full CDK reference

Every option in depth — layer sources, instrumenting a whole stack at once, the AWS Lambda Web Adapter, Java handler interfaces, and the complete API.

Using CloudFormation directly

If you’re not using CDK, merge these properties into your function’s AWS::Lambda::Function resource by hand. The shape is identical across languages — a layer, a wrapper, a service name, and the collector configuration — but the wrapper path (and, for the Web Adapter case, the loader variable) is different for each one. Pick your language below:
Java ships three more wrappers for handler interfaces other than the default RequestHandler: /opt/otel-proxy-handler (API Gateway proxy events), /opt/otel-sqs-handler (SQS), and /opt/otel-stream-handler (RequestStreamHandler). Using the default wrapper on a RequestStreamHandler function breaks the invocation — it’s the wrong interface, not just a less-detailed trace.
Running behind the AWS Lambda Web Adapter? Remove AWS_LAMBDA_EXEC_WRAPPER — the adapter already owns it — and instead append the agent to JAVA_TOOL_OPTIONS. CloudFormation can’t read an existing environment variable back, so add your current value in by hand:
Substitute only the placeholders in <ANGLE_BRACKETS>. Change nothing else in OPENTELEMETRY_COLLECTOR_CONFIG_URI — Odigos compares the whole value as one string, and even a missing space after a colon breaks it (see below).

Things to watch out for

OPENTELEMETRY_COLLECTOR_CONFIG_URI is written in a compact YAML style where a colon must always be followed by a space. Miss one and the collector doesn’t fail with a helpful parse error — it accepts the variable, logs the URI it read, and then fails to start with an unrelated-looking otelcol state is Closed.
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, ODIGOS_OTLP_ENDPOINT, and ODIGOS_OTLP_INSECURE are all compared against unset — adding any of them is reported as drift, even if the value looks harmless. OTEL_LOG_LEVEL is the one exception: leave it unset unless debug telemetry is turned on for this connector, in which case it must be exactly debug.
It’s the only signal Odigos has that this function is instrumented at all, and the layer ARN recorded inside it is what future checks compare against — so a version you deliberately pinned is respected instead of being flagged as an available upgrade.
That variable belongs to the connector alone — it’s how the connector remembers what to roll back to after it makes a change itself. Setting it yourself claims a rollback that was never actually recorded.
If your destination doesn’t accept metrics or logs, set OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER to "none" and remove that signal’s pipeline from the collector configuration. Leave both untouched (unset) for a signal your destination does accept.
Once ODIGOS_IAC is set and the layer, wrapper, and collector configuration all match, Odigos flips the function to Instrumented on its own — usually within a few minutes, at the next discovery cycle. No action needed on the Odigos side.

Next steps

Full CDK reference

Every construct option, in depth.

Preload the layer

Get the layer into your own account first, if your policy requires it.