Monitor AWS Lambda MicroVMs with OpenTelemetry Collector SigNoz published a guide for monitoring AWS Lambda MicroVMs with the OpenTelemetry Collector, which scrapes host metrics, tails logs, and exports telemetry to SigNoz. The Collector is baked into the MicroVM image snapshot, so it runs at startup. MicroVMs scale up to four times baseline, run up to 8 hours, and support baseline sizes from 0.5 GB to 8 GB. Overview AWS Lambda MicroVMs https://aws.amazon.com/lambda/lambda-microvms/ are Firecracker-based compute environments with VM-level isolation, snapshot start and resume, and full OS capabilities. They suit long-running, stateful sandboxes such as AI agent code execution, interactive development environments, CI jobs, and vulnerability scanners. This guide runs an OpenTelemetry Collector inside the MicroVM image. The Collector scrapes host metrics, tails application logs, receives OTLP from your application, and exports everything to SigNoz. Prerequisites - The AWS CLI with the lambda-microvms command set. Run aws lambda-microvms help to confirm it lists create-microvm-image and run-microvm . - An S3 bucket in the same region for the code artifact. - A build role that Lambda assumes during the image build. It needs s3:GetObject on the artifact and logs:CreateLogGroup , logs:CreateLogStream , and logs:PutLogEvents . - An execution role for the running MicroVM. Without it, application stdout and stderr never reach CloudWatch. It needs logs:CreateLogGroup , logs:CreateLogStream , and logs:PutLogEvents . - Both roles need a trust policy that lets Lambda assume them, with Principal: {"Service": "lambda.amazonaws.com"} and Action: "sts:AssumeRole", "sts:TagSession" . Your own caller needs lambda:CreateMicrovmImage , lambda:RunMicrovm , and iam:PassRole for both roles. - An instance of SigNoz either Cloud https://signoz.io/teams/ or Self-Hosted https://signoz.io/docs/install/self-host/ How monitoring works | Signal | Source inside the MicroVM | Path to SigNoz | |---|---|---| Metrics host CPU, memory, disk, network | hostmetrics | Traces Logs receiver, or OTLP from the SDK https://signoz.io/docs/userguide/collect logs from file/ filelog Lifecycle and audit How sizing works MicroVMs use a baseline and peak model. You set the baseline with the memory value when you create the image, and vCPU scales with it at 2 GB per vCPU. During activity the MicroVM scales vertically up to four times the baseline. | Baseline | Peak | Max disk | |---|---|---| | 0.5 GB, 0.25 vCPU | 2 GB, 1 vCPU | 8 GB | | 1 GB, 0.5 vCPU | 4 GB, 2 vCPU | 8 GB | | 2 GB, 1 vCPU default | 8 GB, 4 vCPU | 8 GB | | 4 GB, 2 vCPU | 16 GB, 8 vCPU | 16 GB | | 8 GB, 4 vCPU | 32 GB, 16 vCPU | 32 GB | A MicroVM runs for at most 8 hours. Set the limit with --maximum-duration-in-seconds , which accepts 1 to 28,800 seconds. Send telemetry to SigNoz Lambda builds the MicroVM image from a ZIP you upload to S3, then snapshots the running result. The Collector is baked into that snapshot, so it is already running the moment a MicroVM starts. Your application exports to it on localhost instead of reaching SigNoz directly. Step 1: Package the application and Dockerfile The code artifact is a ZIP that contains a Dockerfile at the archive root plus your application files. Lambda pulls the ZIP from S3, runs your Dockerfile on top of the managed base image, starts your application, and snapshots the result. Two different base images are involved. The MicroVM base image is the operating system environment, passed as --base-image-arn . The container base image is what your Dockerfile uses in its FROM instruction. FROM public.ecr.aws/lambda/microvms:al2023-minimal al2023-minimal ships without tar, gzip, or procps. Install what you need before extracting anything. RUN dnf install -y tar gzip python3 python3-pip && dnf clean all Release assets are version-named, and MicroVMs are ARM64 only. Pin a version and use the arm64 tarball. ARG OTELCOL VERSION=0.159.0 ADD https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTELCOL VERSION}/otelcol-contrib ${OTELCOL VERSION} linux arm64.tar.gz /tmp/otelcol.tar.gz RUN mkdir -p /usr/local/bin \ && tar -xzf /tmp/otelcol.tar.gz -C /usr/local/bin otelcol-contrib \ && rm /tmp/otelcol.tar.gz COPY app.py /app/app.py COPY otel-collector-config.yaml /etc/otelcol/config.yaml COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh && mkdir -p /var/log/app CMD "/entrypoint.sh" The image build has outbound internet access, so pulling the Collector with ADD works. Start the Collector in the background and your application in the foreground, which keeps the MicroVM alive: entrypoint.sh bash /usr/bin/env bash set -euo pipefail Lambda forwards only stdout and stderr to CloudWatch, so do not redirect the Collector to a file. Writing it into /var/log/app would also make the filelog receiver tail the Collector's own output. /usr/local/bin/otelcol-contrib --config /etc/otelcol/config.yaml 2 &1 & exec python3 -u /app/app.py Step 2: Configure the OpenTelemetry Collector otel-collector-config.yaml receivers: On Collector v0.151.0 and newer, use "host metrics" to avoid a deprecation warning. hostmetrics: collection interval: 30s scrapers: cpu: metrics: system.cpu.utilization is an optional metric and off by default. Enable it explicitly or it never reaches SigNoz. system.cpu.utilization: enabled: true memory: {} load: {} filesystem: {} network: {} On Collector v0.149.0 and newer, use "file log" to avoid a deprecation warning. This only collects anything if your application writes to this path. An application that logs to stdout alone produces no records here. Keep the Collector's own log out of this glob. A Collector that tails its own output re-exports every line it writes. filelog: include: /var/log/app/ .log exclude: /var/log/app/otelcol .log start at: beginning otlp: protocols: http: endpoint: localhost:4318 processors: batch: {} On Collector v0.153.0 and newer, use "resource detection" to avoid a deprecation warning. resourcedetection: detectors: env, system resource: attributes: Lambda injects this into every MicroVM. It is the same value the CloudWatch Agent uses for its ImageName dimension. - key: aws.lambda.microvm.image name value: ${env:AWS LAMBDA MICROVM IMAGE NAME} action: upsert - key: aws.lambda.microvm.image version value: ${env:AWS LAMBDA MICROVM IMAGE VERSION} action: upsert exporters: On Collector v0.144.0 and newer, use "otlp http" to avoid a deprecation warning. otlphttp: endpoint: ${env:SIGNOZ ENDPOINT} headers: signoz-ingestion-key: ${env:SIGNOZ INGESTION KEY} service: pipelines: metrics: receivers: hostmetrics, otlp processors: resourcedetection, resource, batch exporters: otlphttp traces: receivers: otlp processors: resourcedetection, resource, batch exporters: otlphttp logs: receivers: filelog, otlp processors: resourcedetection, resource, batch exporters: otlphttp Lambda injects AWS LAMBDA MICROVM IMAGE NAME , AWS LAMBDA MICROVM IMAGE ARN , AWS LAMBDA MICROVM IMAGE VERSION , and AWS REGION into every MicroVM. Using the image name as a resource attribute lets you filter and group all three signals by image in SigNoz. Step 3: Instrument your application Because the MicroVM is long-lived, use standard service-style instrumentation rather than a Lambda layer. Point the SDK at the embedded Collector with these variables: OTEL EXPORTER OTLP ENDPOINT="http://localhost:4318" OTEL SERVICE NAME="my-microvm-app" OTEL RESOURCE ATTRIBUTES="deployment.environment=production" There is no environment variable flag on run-microvm , so pass these to --environment-variables when you build the image in Step 4 step-4-build-the-microvm-image . Setting them anywhere else has no effect. See the SigNoz instrumentation guides https://signoz.io/docs/instrumentation/ for your language. Write your application logs to /var/log/app/ for the filelog receiver from Step 2 step-2-configure-the-opentelemetry-collector to pick them up, or remove that receiver and export logs over OTLP from the SDK. Your directory now holds everything the build needs. Zip it with the Dockerfile at the archive root and upload it: zip -r app.zip Dockerfile entrypoint.sh otel-collector-config.yaml app.py aws s3 cp app.zip s3://