Running the OpenTelemetry Collector as a Lambda The OpenTelemetry Collector can be packaged as a Lambda container image and fronted with a Function URL, enabling intermittent telemetry processing at near-zero cost. The architecture uses the AWS Lambda Web Adapter to bridge the Lambda Runtime API with the Collector's OTLP/HTTP receiver, supporting stateless processing for low-volume, asynchronous telemetry. Running the OpenTelemetry Collector as a Lambda The OpenTelemetry Collector is usually deployed as a long-running process, but when telemetry is rare, it makes sense to run it as a Lambda function instead. Here's how to do it. Span or Attribute in OpenTelemetry Custom Instrumentation When you want to add some information to your tracing telemetry, you could emit a log, create a span, or add a piece of data to your current span. Adding a piece of data to your current span is the best Usually. Learn More /blog/span-or-attribute-opentelemetry-custom-instrumentation The OpenTelemetry Collector is usually deployed as a long-running process: a sidecar, a DaemonSet, an EC2 instance, a docker container on my computer. It sits there listening for telemetry. That's fine when I want to send telemetry all day, but not when telemetry is rare. Like right now, when I have an agent defined on AgentCore, and it runs a few times a week maybe. Or my website that hardly sees any traffic. Can I run the OpenTelemetry Collector as a Lambda function? Sounds tricky, but hey, that's what my coding assistant is for Here’s how we got it working. Learn more about Honeycomb Intelligence Connect with our experts today. From here, this post is agent-written. Point your coding agent at this post and ask it, “How will this work in my environment?” You can package the OTel Collector as a Lambda container image and front it with a Function URL. Producers send OTLP/HTTP to the URL; the collector runs whatever processors you configure; the result goes on to your backend. At low volume this costs essentially nothing: a Lambda below the free tier rounds to zero. Cold start is around 4 seconds; warm invocations are 2–4 ms. The full file set Dockerfile, config.yaml , and bootstrap/build/deploy scripts is available as a companion gist https://gist.github.com/jessitron/f2cc160cb5e635d1c60752b5f2d038a8 . The rest of the post explains what each piece does. When this fits The Lambda shape works when: - Traffic is intermittent—single-digit requests per second peak, often zero. - The producer exports asynchronously a BatchSpanProcessor , an AgentCore runtime, another Lambda so cold start is invisible to the user. - The processing you want is stateless per request—OTTL transform , filter , attributes , redaction, routing. It does not fit when: - Sustained throughput exceeds about 1 request per second. Lambda's per-invocation overhead and pricing stop being free. - You need queuing or retry. If the backend is briefly unavailable, in-Lambda retry state dies with the process. Producer-side retry is your only safety net. - You're scraping metrics or logs. This is only for logs and traces that are pushed to the collector. Architecture Producer any OTLP/HTTP client │ OTLP/HTTP/protobuf, bearer token in Authorization header ▼ Lambda Function URL auth type=NONE; bearer enforced inside the collector │ Lambda container image ├─ AWS Lambda Web Adapter extension │ polls the Lambda Runtime API, forwards each invocation as an HTTP │ request to localhost:4318 └─ otelcol-contrib CMD, not ENTRYPOINT ├─ otlp receiver bearertokenauth ├─ your processors └─ otlphttp exporter sending queue disabled → backend e.g. api.honeycomb.io AWS Lambda Web Adapter https://github.com/awslabs/aws-lambda-web-adapter is the load-bearing piece. It registers as a Lambda extension, exposes the Lambda Runtime API as an HTTP listener on a port of your choosing, and forwards each invocation to your container's HTTP server. The Collector's OTLP/HTTP receiver is an HTTP server. LWA bridges them. A note on auth. The bearer token in the diagram is optional but recommended. The Collector's OTLP receiver works without it. But a Function URL with auth type=NONE is publicly reachable, so without a check inside the collector, anyone who learns the URL can send data through it to your backend—running up your ingest bill and polluting your telemetry. The hostname is random, but URLs leak commit history, screenshots, packet captures . A static bearer token, shared between authorized producers and this Lambda, raises the bar from "anyone with the URL" to "anyone with the URL and the token." Details in the Authentication section below. The container image Use a multi-stage build. The official otel/opentelemetry-collector-contrib image is distroless and runs as USER 10001 with no /etc/passwd , both of which the Lambda container runtime trips on. Copy the binary into Alpine and run from there. FROM otel/opentelemetry-collector-contrib:0.151.0 AS collector FROM alpine:3.20 RUN apk add --no-cache ca-certificates COPY --from=collector /otelcol-contrib /app/otelcol-contrib COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.0.0 /lambda-adapter /opt/extensions/lambda-adapter COPY config.yaml /etc/otel/config.yaml ENV AWS LWA PORT=4318 ENV AWS LWA INVOKE MODE=buffered ENV AWS LWA READINESS CHECK PATH=/ CMD "/app/otelcol-contrib", "--config=/etc/otel/config.yaml" What each piece does: —Alpine ships without trusted CA roots. The ca-certificates otlphttp exporter does HTTPS to the backend; without this it fails at TLS. LWA at —Lambda discovers extensions in this directory automatically at container start. LWA ships its own Runtime Interface Client, so you do not need a separate RIC. /opt/extensions/ —The OTLP/HTTP receiver's default port. LWA forwards Function URL invocations here. AWS LWA PORT=4318 —One response per request. The other option, response stream, is for Server-Sent Events. AWS LWA INVOKE MODE=buffered —LWA polls this path until something responds before forwarding traffic. The OTLP receiver returns 404 on /, which counts as ready any HTTP response is enough . AWS LWA READINESS CHECK PATH=/ —Lambda treats CMD-only and ENTRYPOINT+CMD images differently at boot. With ENTRYPOINT set, the main process never starts. See troubleshooting below. CMD , not ENTRYPOINT The configuration Three things shape the config: the receiver needs bearer-token auth, the processing must be stateless per request, and the exporter must not queue. extensions: bearertokenauth/ingest: scheme: Bearer token: ${env:INGEST BEARER TOKEN} receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 auth: authenticator: bearertokenauth/ingest processors: Your processors here. Examples: transform/, filter/, attributes/. Do NOT include a batch processor. exporters: otlphttp/honeycomb: endpoint: https://api.honeycomb.io headers: x-honeycomb-team: ${env:HONEYCOMB API KEY} sending queue: enabled: false service: extensions: bearertokenauth/ingest pipelines: traces: receivers: otlp processors: ... exporters: otlphttp/honeycomb This sends to Honeycomb. To send elsewhere, swap the exporter—any OTLP/HTTP backend works with the same shape change the endpoint and the auth header name and value . Two non-obvious settings: No Lambda freezes the container after the handler returns. Spans sitting in a batch processor. batch processor never flush—they stay in memory until the next cold start which discards them or the next invocation which may or may not arrive . Symptom: the exporter reports 200 in milliseconds, the backend never sees the trace.on every exporter. Same reason. The default async queue holds spans in memory; the freeze strands them. sending queue.enabled: false The official fix for these is the decouple processor from the opentelemetry-lambda collector distribution, which knows to flush before the handler returns. It is not in otel/opentelemetry-collector-contrib . If you stick with the contrib image, synchronous export is the correct shape. Authentication Bearer auth is optional but recommended; see the note in Architecture for why. This section covers how. Function URLs support auth type=AWS IAM and auth type=NONE . IAM would be the strict answer, but it requires Sigv4-signing the OTLP requests on the producer side, and the OpenTelemetry SDKs do not sign with Sigv4. Writing a Sigv4-signing OTLP exporter is more work than this whole pattern is worth. The pragmatic answer is auth type=NONE on the Function URL plus the bearertokenauth extension inside the collector. The bearer token is a shared secret between the producer's environment and the Lambda's environment. If it leaks, rotate it. The producer sets: OTEL EXPORTER OTLP HEADERS=authorization=Bearer