Execute Kestra Tasks on AWS Lambda MicroVMs
For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append.mdto anykestra.io/docs/*URL for plain Markdown.
Available on:Enterprise EditionCloud
Run tasks natively on AWS Lambda MicroVMs — no container runtime required, with built-in image management and S3-based file staging.
When to use the Lambda MicroVM task runner
Use the Lambda MicroVM task runner when your workload needs lightweight VM isolation on ARM64 without the overhead of container scheduling or EC2 instance provisioning:
- Fast-start serverless workloads that need VM-level isolation but can tolerate a one-time per-region image build (a few minutes on first run)
- Long-running non-containerized tasks that exceed Lambda’s 15-minute function timeout — runs can last up to 8 hours
- ARM64-native workloads built for Amazon Linux 2023 on ARM64 that do not require a custom container image
- No infrastructure to maintain: the runner builds and manages its own image, with no AMIs or job queues to maintain
For containerized workloads, use the AWS Batch task runner. For direct EC2 control with Spot instances, GPU support, or custom AMIs, use the AWS EC2 task runner.
This runner is ARM64 only. The containerImage property is ignored — the runner builds and manages its own Amazon Linux 2023 ARM64 base image. Do not set containerImage on tasks using this runner.
How it works
For each task run, the Lambda MicroVM task runner:
- On the first run in an AWS account and region, builds a managed ARM64 image named
kestra-task-runner-<hash>from an Amazon Linux 2023 minimal base. This one-time build takes a few minutes. Later runs reuse it. A plugin upgrade, a differentmemory, or a differentbaseImageArnbuilds a new image next to the old one. - Uploads the commands, environment variables,
inputFilesandnamespaceFilesto S3 under thekestra-lambda-microvm/run/prefix. The MicroVM reads and writes them through short-lived presigned URLs, so it needs no IAM credentials for staging. - Starts the MicroVM and runs the task’s commands directly in it, with no container.
- Streams command output to the log group at
/aws/lambda-microvm/kestra(default) via CloudWatch Logs. - Downloads
outputFilesfrom S3 to Kestra internal storage once the command completes. - Terminates the MicroVM unconditionally on success, failure, or worker interruption.
The MicroVM accesses S3 via presigned URLs generated by the Kestra worker, so the execution role does not need S3 permissions for file staging.
The following Pebble expressions and environment variables are available inside the task:
| Pebble expression | Environment variable | Description |
|---|---|---|
{{ workingDir }} | WORKING_DIR | Path to the task’s working directory in the MicroVM, where input files are placed |
{{ outputDir }} | OUTPUT_DIR | Path to the output directory; all files written here are automatically captured |
If a run fails, look up the failure detail in {{ outputs.<taskId>.vars.kestraTaskRunner }}. This output includes the MicroVM identifier and any error message from the run.
IAM permissions
The Lambda MicroVM task runner involves three IAM principals: the runner credentials (used by the Kestra worker to build the image, start and monitor runs, and stage files) and two roles assumed by Lambda: a build role (for image construction) and an execution role (for the task at runtime). For a quick setup, one role can serve as both the build and the execution role (same trust policy, merged permissions). Separate roles are cleaner, because the build role then grants nothing your commands could use.
Runner credentials
The IAM principal used by Kestra (the user or role whose credentials are passed via accessKeyId/secretKeyId, or the one resolved by the AWS default credentials provider chain) needs the following minimum permissions:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["lambda:RunMicrovm", "lambda:GetMicrovm", "lambda:TerminateMicrovm"], "Resource": "*" }, { "Effect": "Allow", "Action": [ "lambda:CreateMicrovmImage", "lambda:GetMicrovmImage", "lambda:ListMicrovmImages", "lambda:GetMicrovmImageVersion", "lambda:ListManagedMicrovmImages", "lambda:ListManagedMicrovmImageVersions", "lambda:TagResource" ], "Resource": "*" }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": ["<buildRoleArn>", "<executionRoleArn>"], "Condition": {"StringEquals": {"iam:PassedToService": "lambda.amazonaws.com"}} }, { "Effect": "Allow", "Action": "lambda:PassNetworkConnector", "Resource": "arn:aws:lambda:*:aws:network-connector:*" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:AbortMultipartUpload"], "Resource": "arn:aws:s3:::<bucket>/kestra-lambda-microvm/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetBucketLocation"], "Resource": "arn:aws:s3:::<bucket>" }, { "Effect": "Allow", "Action": "logs:DescribeLogGroups", "Resource": "*" }, { "Effect": "Allow", "Action": ["logs:CreateLogGroup", "logs:FilterLogEvents"], "Resource": "arn:aws:logs:<region>:<account>:log-group:<logGroup>:*" } ]}Replace <buildRoleArn> and <executionRoleArn> with the ARNs of the two roles described below.
The runner needs lambda:PassNetworkConnector because, when no network connector is set, AWS attaches its managed INTERNET_EGRESS and HTTP_INGRESS connectors to the MicroVM, and passing them requires this permission. If you set custom connectors in ingressNetworkConnectors or egressNetworkConnectors, add their ARNs to that statement.
Running on Kestra Cloud? The Cloud control plane runs outside your AWS account and has no EC2 instance profile to supply a base AWS identity. Static accessKeyId / secretKeyId credentials are required.
Build role
The build role is assumed by Lambda MicroVM to construct the managed image on first run. Create a role with the following trust policy and permissions:
Trust policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ]}Permissions:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::<bucket>/kestra-lambda-microvm/bootstrap/*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:<region>:<account>:log-group:<logGroup>:*" } ]}Replace <bucket> with your staging bucket name.
Execution role
The execution role is assumed by the MicroVM at runtime when running your task commands. At minimum it needs permission to write logs:
Trust policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ]}Permissions:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:<region>:<account>:log-group:<logGroup>:*" } ]}Add any permissions your task commands need (for example, S3, DynamoDB, or other AWS service access) to this role.
KMS-encrypted buckets
To encrypt staged objects with KMS, set serverSideEncryption to aws:kms or aws:kms:dsse. Set kmsKeyId to use a specific key. When it’s unset, S3 uses the AWS managed key aws/s3. The runner rejects kmsKeyId when serverSideEncryption is AES256.
serverSideEncryption only applies to the objects the runner uploads: the bundle, the input archive, and the agent’s code artifact. The MicroVM writes output.zip and status.json through presigned links, so those objects use the bucket’s default encryption.
When you use aws:kms, or your bucket’s default encryption is SSE-KMS, add the following to the runner credentials:
{ "Effect": "Allow", "Action": [ "kms:GenerateDataKey", "kms:Decrypt" ], "Resource": "<kmsKeyArn>"}The runner credentials need kms:GenerateDataKey to upload objects and kms:Decrypt to read the result back. They need both for the MicroVM too, because they sign the presigned links it uses. With an SSE-KMS bucket default, kms:GenerateDataKey on that key also covers the output.zip and status.json objects the MicroVM writes. Add kms:Decrypt to the build role as well, since Lambda assumes it to read the agent’s code artifact from S3.
S3 lifecycle rule
The runner stages files under kestra-lambda-microvm/run/ in your bucket and removes them when the run ends. If a run is killed before it can clean up, the objects stay behind. As a safety net, add a lifecycle rule that expires objects under this prefix after 1 day.
Example flows
Basic shell command
id: lambda_microvm_shellnamespace: company.team
tasks: - id: shell type: io.kestra.plugin.scripts.shell.Commands taskRunner: type: io.kestra.plugin.ee.aws.runner.LambdaMicroVm region: us-east-1 bucket: my-kestra-staging-bucket buildRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-build-role" executionRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-execution-role" commands: - echo "Hello from Lambda MicroVM"Input and output files
id: lambda_microvm_with_filesnamespace: company.team
inputs: - id: file type: FILE
tasks: - id: process type: io.kestra.plugin.scripts.shell.Commands inputFiles: data.txt: "{{ inputs.file }}" outputFiles: - out.txt taskRunner: type: io.kestra.plugin.ee.aws.runner.LambdaMicroVm region: us-east-1 bucket: my-kestra-staging-bucket buildRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-build-role" executionRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-execution-role" memory: 4096 commands: - cp "{{ workingDir }}/data.txt" "{{ workingDir }}/out.txt"With STS role assumption
Instead of static accessKeyId / secretKeyId credentials, authenticate via AWS STS AssumeRole:
id: lambda_microvm_stsnamespace: company.team
tasks: - id: shell type: io.kestra.plugin.scripts.shell.Commands taskRunner: type: io.kestra.plugin.ee.aws.runner.LambdaMicroVm region: us-east-1 bucket: my-kestra-staging-bucket buildRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-build-role" executionRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-execution-role" stsRoleArn: "arn:aws:iam::123456789012:role/kestra-runner-role" stsRoleExternalId: "{{ secret('STS_EXTERNAL_ID') }}" stsRoleSessionName: kestra-session commands: - echo "Hello from Lambda MicroVM with STS"This example is keyless only when Kestra runs inside AWS. On a self-hosted instance on EC2 or EKS, the AWS SDK uses the instance profile or IRSA to make the sts:AssumeRole call without static keys. On Kestra Cloud, you must supply accessKeyId and secretKeyId alongside stsRoleArn.
The runner credentials sign the presigned links the MicroVM uses to write its result, so temporary credentials must stay valid for the whole run. With stsRoleArn, the session defaults to the run’s wait (waitUntilCompletion, or the task timeout when set) plus 15 minutes, capped at 1 hour. For a longer wait, set stsRoleSessionDuration explicitly. Otherwise the runner fails the task before it starts. The role’s maximum session duration must allow the value you set.
Security
The Kestra agent inside the MicroVM listens on port 8080, and its hooks are not authenticated: any peer that can reach it could start a job or stop the running one. With no ingressNetworkConnectors set, AWS attaches its managed HTTP_INGRESS connector, and reaching a MicroVM through it requires a MicroVM auth token (lambda:CreateMicrovmAuthToken). Restrict that permission to trusted principals, and do not add ingress connectors that expose port 8080.
Use ingressNetworkConnectors and egressNetworkConnectors to control the MicroVM’s network access when your account has Lambda Network Connectors configured.
Monitoring
Set monitoring.enabled: true to run the task command under kotlp, a portable observability wrapper Kestra stages into the task’s working directory. No image change is needed.
kotlp reports the command’s resource usage as process.* task metrics, sampled every monitoring.metricsInterval (default PT1S):
| Metric | Type | Unit | Attribute |
|---|---|---|---|
process.cpu.time | sum | s | cpu.mode: user or system |
process.cpu.utilization | gauge | 1 | cpu.mode: user or system |
process.memory.usage | gauge | By | — |
process.memory.virtual | gauge | By | — |
process.disk.io | sum | By | disk.io.direction: read or write |
process.network.io | sum | By | network.io.direction: receive or transmit |
process.thread.count | gauge | {thread} | — |
process.open_file_descriptor.count | gauge | {count} | — |
The three sum metrics (process.cpu.time, process.disk.io, process.network.io) are cumulative and reflect totals for the entire run. The gauge metrics reflect the last sample taken before the command exits, not a peak.
kotlp also runs an embedded OTLP receiver, so traces the command exports are captured as task traces.
id: lambda_microvm_with_monitoringnamespace: company.team
tasks: - id: run type: io.kestra.plugin.scripts.shell.Commands taskRunner: type: io.kestra.plugin.ee.aws.runner.LambdaMicroVm region: us-east-1 bucket: my-kestra-staging-bucket buildRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-build-role" executionRoleArn: "arn:aws:iam::123456789012:role/kestra-lambda-execution-role" monitoring: enabled: true commands: - echo "stdout line" - echo "stderr line" >&2| Property | Default | Description |
|---|---|---|
monitoring.enabled | false | When true, wraps the task command with kotlp. |
monitoring.metricsInterval | PT1S | How often kotlp samples resource usage. Accepts values between PT0.001S and PT1H. |
Monitoring constraints
An explicit commands list is required when monitoring is enabled: kotlp wraps the commands you supply.
Key properties
| Property | Required | Default | Description |
|---|---|---|---|
region | Yes | — | AWS region where the MicroVM runs. |
bucket | Yes | — | S3 bucket for file staging and bootstrap objects. |
buildRoleArn | Unless imageIdentifier is set | — | ARN of the IAM role Lambda MicroVM assumes to build the managed image. See Build role. |
executionRoleArn | Yes | — | ARN of the IAM role the MicroVM assumes at runtime. See Execution role. |
memory | No | Service default | MicroVM memory in MiB, for example 2048. Must be positive. The memory belongs to the image, so each distinct value gets its own managed image, built on first use. |
serverSideEncryption | No | AES256 | S3 server-side encryption algorithm for objects the runner uploads. One of AES256, aws:kms, or aws:kms:dsse. |
kmsKeyId | No | — | KMS key ID, key ARN or alias ARN for S3 object encryption. Only valid with serverSideEncryption set to aws:kms or aws:kms:dsse. S3 uses the AWS managed key aws/s3 when unset. |
accessKeyId | No | — | AWS access key ID. When omitted, the AWS default credentials provider chain is used. |
secretKeyId | No | — | AWS secret access key. |
sessionToken | No | — | AWS session token for temporary credentials. |
stsRoleArn | No | — | ARN of an IAM role to assume via STS. |
stsRoleExternalId | No | — | External ID for the STS trust policy. |
stsRoleSessionName | No | — | Name tag for the assumed-role session. |
endpointOverride | No | — | Replaces the AWS endpoint of every service the runner calls. Only for emulators or private endpoints. |
stsEndpointOverride | No | — | STS endpoint override. |
waitUntilCompletion | No | PT1H | Maximum time to wait for the run to finish; the task timeout takes precedence when set. The MicroVM is capped to this duration. Cannot exceed PT8H; longer values are rejected. |
completionCheckInterval | No | PT5S | How often to poll for run completion. |
logGroup | No | /aws/lambda-microvm/kestra | CloudWatch Logs log group for task output. |
imageIdentifier | No | — | ARN or ID of a custom image to run instead of the managed one. It must embed the Kestra agent (serving POST /aws/lambda-microvms/runtime/v1/{run,terminate,validate,ready} on port 8080). When set, nothing is built and buildRoleArn is not needed. |
imageVersion | No | — | Version of the custom image to run. Only used with imageIdentifier; defaults to the latest active version. |
baseImageArn | No | — | Lambda-managed base image ARN the runner builds its image on. Defaults to the only managed base image AWS lists for the region. |
syncWorkingDirectory | No | false | When true, uploads the whole working directory of the MicroVM back to Kestra after the commands finish, not only outputFiles and the output directory. |
idlePolicy | No | — | What AWS does with a MicroVM that receives no activity. Unset by default: the service default applies. A suspended MicroVM stops running your commands. |
ingressNetworkConnectors | No | — | Lambda Network Connectors for inbound network access. |
egressNetworkConnectors | No | — | Lambda Network Connectors for outbound network access. |
tags | No | — | Tags added to the image the runner builds, in addition to kestra:managed, kestra:agent-hash and kestra:base-image-version. A MicroVM itself cannot be tagged. |
stsRoleSessionDuration | No | — | Duration of the STS assumed-role session. Defaults to the run’s wait (waitUntilCompletion, or the task timeout when set) plus 15 minutes, capped at 1 hour. Set it explicitly for longer runs with stsRoleArn. |
waitForLogInterval | No | PT5S | After the task reports completion, the runner keeps polling CloudWatch Logs until no new output has arrived for this long, so late-arriving output isn’t lost. |
For the full property reference, see the plugin documentation.
Was this page helpful?