Proxylity

Lambda MicroVM Destinations

Service Name: Lambda MicroVMs
ARN Format: arn:aws:lambda:{region}:{account}:microvm-image:{image name} or arn:aws:lambda:{region}:{account}:microvm-image:{image name}:{version}
Style: Request/Response
Actions (required Permissions): lambda:RunMicrovm,
lambda:CreateMicrovmAuthToken,
lambda:GetMicrovmImage,
lambda:GetMicrovmImageVersion,
lambda:GetMicrovm,
lambda:ResumeMicrovm,
lambda:TerminateMicrovm,
iam:PassRole (when ExecutionRoleArn is set)
Payload Format: JSON object with array of request packet objects, sent over HTTPS
Arguments: ExecutionRoleArn (string, optional),
RunHookExtraData (string, optional),
EgressNetworkConnectorArns (list of strings, optional),
IdleDurationSeconds (string containing an integer, optional),
TenantId (string, optional),
TenantIdExpression (binary range expression, optional),
TenantIdFormatter (string "ascii", "hex", "base64" or "utf8", optional)

A Lambda MicroVM is a full Linux environment, built from a container image you provide, that keeps running (and keeps its memory) across requests until it is suspended or terminated. Using a MicroVM image as a destination suits UDP workloads that need long-lived, in-memory state per client or tenant, software that does not fit the Lambda function model, or an HTTP application you would rather not rewrite as a function. For stateless packet handling, Lambda functions are simpler.

The destination is a MicroVM image, not a running MicroVM. Proxylity starts MicroVMs from the image when packets arrive, delivers batches to them, and sends their replies back to the remote UDP client.

How It Works

  1. When packets arrive for a tenant that has no MicroVM, Proxylity calls RunMicrovm to start one from the image, using the execution role from Arguments.ExecutionRoleArn if one is given.
  2. If the image version enables the /run hook, Lambda calls it with the MicroVM ID and a run hook payload from Proxylity. Traffic is routed once the hook returns.
  3. Proxylity calls CreateMicrovmAuthToken for a token scoped to port 8080, and POSTs batches of packets to the MicroVM's HTTPS endpoint as JSON.
  4. The application replies with a JSON document, and Proxylity sends each reply to the original sender.
  5. Later packets for the same tenant go to the same MicroVM.

Note: The first packet for a tenant starts the MicroVM, so it may be dropped or answered late while the MicroVM reaches the RUNNING state. Send a few packets when testing.

Image Versions

With an ARN that has no version suffix, new MicroVMs run the latest active version of the image, so publishing a new version takes effect for MicroVMs started after that (running MicroVMs are not replaced). To pin the destination to a specific version, add the version to the ARN (...:microvm-image:my-image:3). Proxylity re-reads the image version at most once a minute.

If Proxylity cannot read the image (for example, the destination role is missing lambda:GetMicrovmImage or lambda:GetMicrovmImageVersion), it logs a warning and starts MicroVMs without choosing a version and without the run hook payload, so the version is left to Lambda.

Request and Reply Format

The application must listen for HTTP requests on port 8080. This port is fixed. Each request body is a JSON object with a Messages property containing an array of request packet objects, the same format used by Lambda destinations:

{
  "Messages": [{
    "Tag": "opaque-correlation-tag",
    "Remote": { "IpAddress": "203.0.113.10", "Port": 45678 },
    "Local": { "Domain": "ingress-1", "Port": 15678 },
    "Formatter": "base64",
    "Data": "aGVsbG8="
  }]
}

The application responds with a Replies array. Replies are matched to messages using Tag. If no replies are needed, return an empty array:

{ "Replies": [{ "Tag": "opaque-correlation-tag", "Data": "SGVsbG8gZnJvbSBNaWNyb1ZNLi4u" }] }

The Data format follows the destination's Formatter property, for both requests and replies (base64 by default).

Arguments

All arguments are optional.

ArgumentDefaultDescription
ExecutionRoleArn none IAM role the MicroVM runs with. Required for the application to use AWS services, and for its stdout/stderr to be forwarded to CloudWatch Logs. The role must be assumable by lambda.amazonaws.com, and the destination role needs iam:PassRole on it, conditioned on iam:PassedToService equal to lambda.amazonaws.com.
RunHookExtraData none String of up to 2048 characters, delivered to the /run hook as ExtraData. Use it for application settings; do not put secrets in it.
IdleDurationSeconds 900 Seconds without packets before the MicroVM is suspended to save compute cost. Values are clamped to 60 to 28800 (1 minute to 8 hours); 28800 keeps the MicroVM running for its whole lifetime. A suspended MicroVM resumes automatically when packets arrive, which adds latency to the first packet.
EgressNetworkConnectorArns none List of 1 to 10 customer-managed Lambda network connector ARNs (optionally version-qualified, ...:network-connector:{id}:{version}) that MicroVMs use for outbound traffic. Without it Lambda's default applies, which gives MicroVMs public internet access. See Network Access.
TenantId, TenantIdExpression, TenantIdFormatter none Start one MicroVM per tenant, using a fixed tenant ID or one extracted from each packet. See Tenants.

Network Access

By default Lambda gives a MicroVM public internet access for outbound traffic, and Proxylity starts MicroVMs with that default. To restrict it, create a Lambda network connector that routes the MicroVM's outbound traffic through your VPC (for example one with no route to the internet) and list its ARN in EgressNetworkConnectorArns. Proxylity passes the connectors to RunMicrovm when it starts a MicroVM, so the VPC's routing, security groups and network ACLs control what the application can reach. Connectors cannot change while a MicroVM is running: MicroVMs that are already running keep the connectors they started with, and a change applies to MicroVMs started afterward. The inbound path used for packet delivery is not affected.

The destination role must be allowed to pass the connectors that RunMicrovm attaches. Without EgressNetworkConnectorArns these are the AWS-managed HTTP_INGRESS and INTERNET_EGRESS connectors, as in the example below. With it, grant lambda:PassNetworkConnector on HTTP_INGRESS and on your connectors (and on their unversioned ARNs if you pin versions), and leave out INTERNET_EGRESS so the role cannot start MicroVMs with internet access. The execution role is separate: with no internet or VPC endpoints the application cannot reach AWS APIs regardless of its policy, so omit ExecutionRoleArn or grant only what the endpoints in your VPC provide.

Tenants

Without a tenant setting, all packets go to a single MicroVM. Set Arguments.TenantId to a fixed string, or Arguments.TenantIdExpression to a binary range expression (for example "u8[0] >> 6 ?? 0") and optionally Arguments.TenantIdFormatter to start a separate MicroVM for each tenant. Batches are split by tenant, and tenants are delivered concurrently. This is the natural way to give each client, device or customer its own isolated, stateful environment.

MicroVM Lifecycle

Run Hook Payload

The /run hook is optional. Proxylity sends a run hook payload only if the image version it starts enables the hook. Lambda posts { "microvmId": "...", "runHookPayload": "..." } to the hook, where runHookPayload is a JSON string:

{
  "Version": 1,
  "DestinationName": "the destination's name",
  "ImageArn": "arn:aws:lambda:us-west-2:123456789012:microvm-image:my-image",
  "TenantId": "tenant ID, or null",
  "Region": "the region delivering packets to this MicroVM",
  "Formatter": "base64",
  "Port": 8080,
  "StartedAt": "2026-11-07T18:00:00.0000000Z",
  "ExtraData": "the RunHookExtraData argument, or null"
}

The payload never contains credentials or tokens. Serve the hooks on a port other than 8080 (the example uses 9000) so they cannot be reached with the port 8080 token Proxylity holds. Lambda limits the whole payload to 4096 characters; if a long tenant ID and RunHookExtraData exceed it, the MicroVM is not started and an error is logged.

Example

The destination role below is assumed by Proxylity. MicroVM actions are authorized against the image the MicroVM runs from, so scoping them to the image's ARN limits the destination to MicroVMs started from that image. RunMicrovm also requires lambda:PassNetworkConnector for each network connector attached to the MicroVM.

DestinationRole:
  Type: AWS::IAM::Role
  Properties:
    # AssumeRolePolicyDocument for Proxylity omitted; see the Access and Permissions docs
    Policies:
      - PolicyName: microvm
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - lambda:RunMicrovm
                - lambda:GetMicrovm
                - lambda:ResumeMicrovm
                - lambda:TerminateMicrovm
                - lambda:GetMicrovmImage
                - lambda:GetMicrovmImageVersion
                - lambda:CreateMicrovmAuthToken
              Resource: !GetAtt EchoMicrovmImage.ImageArn
            - Effect: Allow
              Action: lambda:PassNetworkConnector
              Resource:
                - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:aws:network-connector:aws-network-connector:HTTP_INGRESS"
                - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:aws:network-connector:aws-network-connector:INTERNET_EGRESS"
            - Effect: Allow
              Action: iam:PassRole
              Resource: !GetAtt MicrovmExecutionRole.Arn
              Condition:
                StringEquals:
                  iam:PassedToService: lambda.amazonaws.com

UdpListener:
  Type: Custom::ProxylityUdpGatewayListener
  Properties:
    # ...
    Destinations:
      - Description: Deliver UDP packets to a Lambda MicroVM
        Role:
          Arn: !GetAtt DestinationRole.Arn
        DestinationArn: !GetAtt EchoMicrovmImage.ImageArn
        Arguments:
          IdleDurationSeconds: "900"
          TenantIdExpression: "u8[0] >> 6 ?? 0"
          TenantIdFormatter: hex
          ExecutionRoleArn: !GetAtt MicrovmExecutionRole.Arn
          RunHookExtraData: '{"Example":"lambda-microvm"}'

The image itself is declared with the AWS::Serverless::MicrovmImage SAM resource. See the complete Lambda MicroVM example, including a small Node.js application, step-by-step deployment and test commands.