
Aws Lambda Managed Instances
- 2k installs
- 2.2k repo stars
- Updated August 4, 2026
- aws/agent-toolkit-for-aws
Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI).
About
Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI). Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching, scaling, routing, and load balancing. Triggers when queries mention Lambda Managed Instances, LMI, capacity providers, multi-concurrent execution environments, EC2-backed Lambda, persistent Lambda instances, PerExecutionEnvironmentMaxConcurrency, CapacityProviderConfig, cold start elimination via dedicated instances, migrating standard Lambda to managed instances, or cost comparison between standard Lambda and LMI with Savings Plans or Reserved Instances. Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching, scaling, routing, and load balancing. Combines Lambda's developer experience with EC2's pricing and hardware options.
- # AWS Lambda Managed Instances (LMI)
- **Note:** Confirm regional availability, quotas, and instance type offerings against current AWS documentation before pr
- ## Quick Decision: Is LMI Right for This Workload?
- | Signal | LMI is a strong fit | Standard Lambda is better |
- |--------|---------------------|---------------------------|
Aws Lambda Managed Instances by the numbers
- 1,991 all-time installs (skills.sh)
- +400 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #392 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
aws-lambda-managed-instances capabilities & compatibility
- Capabilities
- # aws lambda managed instances (lmi) · **note:** confirm regional availability, quotas, · ## quick decision: is lmi right for this workloa · | signal | lmi is a strong fit | standard lambda
- Use cases
- documentation
What aws-lambda-managed-instances says it does
Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI). Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching, scaling, rou
npx skills add https://github.com/aws/agent-toolkit-for-aws --skill aws-lambda-managed-instancesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2k |
|---|---|
| repo stars | ★ 2.2k |
| Last updated | August 4, 2026 |
| Repository | aws/agent-toolkit-for-aws ↗ |
How do I apply aws-lambda-managed-instances using the workflow in its SKILL.md?
Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI). Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching,...
Who is it for?
Developers following the aws-lambda-managed-instances skill for the tasks it documents.
Skip if: Tasks outside the aws-lambda-managed-instances scope described in SKILL.md.
When should I use this skill?
User mentions aws-lambda-managed-instances or related triggers from the skill description.
What you get
Working aws-lambda-managed-instances setup aligned with the documented patterns and constraints.
Files
AWS Lambda Managed Instances (LMI)
Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching, scaling, routing, and load balancing. Combines Lambda's developer experience with EC2's pricing and hardware options.
Works best with the AWS MCP server for sandboxed CLI execution and audit logging. All guidance also works with standard AWS CLI or SAM CLI.
Note: Confirm regional availability, quotas, and instance type offerings against current AWS documentation before production deployment.
Quick Decision: Is LMI Right for This Workload?
| Signal | LMI is a strong fit | Standard Lambda is better |
|---|---|---|
| Traffic | Steady, predictable, 50M+ req/mo | Bursty, unpredictable, long periods of no traffic |
| Cost | Duration-heavy spend at scale | Low or sporadic invocations |
| Cold starts | Unacceptable (LMI eliminates for provisioned capacity) | Tolerable |
| Compute | Latest CPUs, specific families, high network bandwidth, GPU requirements | Standard Lambda memory/CPU sufficient |
| Isolation | Dedicated EC2 instances in your account, full VPC control | Shared Firecracker micro-VMs acceptable |
| Scale-to-zero | Does not scale to zero but can create custom schedules with AWS provided solutions | Required (pay nothing when idle) |
| Code readiness | Thread-safe (Node.js/Java/.NET) or any Python code | Non-thread-safe code, expensive to change |
Routing
Read ONLY the single reference file that matches the user's task. Do not preload multiple references.
| User need | Action |
|---|---|
| Cost comparison, pricing analysis, Savings Plans, Reserved Instances | Read cost-comparison.md |
| Instance types, memory sizing, vCPU ratios, scaling tuning, capacity provider config | Read configuration-guide.md |
| Thread safety, concurrency model, code review checklist, multi-concurrency readiness | Read thread-safety.md |
| Before/after code examples, runtime-specific migration, connection pooling | Read migration-patterns.md |
| IAM roles, VPC setup, CLI commands, SAM template, CDK example | Read infrastructure-setup.md |
| Errors, throttling, debugging, stuck deployments | Read troubleshooting.md |
Troubleshooting quick facts (always mention when diagnosing issues):
- Capacity provider stuck in CREATING → most common cause is private subnets missing a NAT gateway route (instances need outbound internet for image pull and Lambda service communication)
- Function not scaling → check that a version is published (PublishToLatestPublished: true)
- Memory errors → LMI minimum is 2048 MB
Workflow
Step 1: Assess the Workload
Gather these signals before recommending:
1. Traffic pattern: Steady vs bursty? Requests per second? 2. Current costs: Monthly Lambda spend? Existing Savings Plans? 3. Runtime: Node.js, Java, .NET, or Python? 4. Memory/CPU: How much memory? CPU-bound or I/O-bound? 5. Execution duration: Average and P99? 6. Concurrency readiness: Thread safety? Shared /tmp paths? Per-invocation DB connections? 7. VPC: Already in a VPC? Private resource access needed?
When recommending LMI, ALWAYS mention: minimum 3 execution environments for AZ resiliency (cannot go below 3 in production).
Step 2: Build the Cost Comparison
REQUIRED: Present a cost comparison before recommending LMI.
Rule of thumb: LMI becomes cost-competitive at 50-100M+ req/month with steady traffic. Use the LMI Pricing Calculator for accurate comparisons.
Step 3: Configure the Deployment
- Instance families (400+ types, .large and up): C-series (compute), M-series (general), R-series (memory). ARM (Graviton) for best price-performance.
- When using Graviton instances, MUST set `Architectures: [arm64]` in the function configuration to match.
- Memory-to-vCPU ratios: 2:1 (compute), 4:1 (general, default), 8:1 (memory). Min 2 GB, max 32 GB.
- Multi-concurrency per-vCPU maximums: Node.js 64, Java 32, .NET 32, Python 16. These are system caps — the actual setting is PerExecutionEnvironmentMaxConcurrency (per execution environment, not per vCPU).
- For I/O-bound workloads: use the runtime default or higher PerExecutionEnvironmentMaxConcurrency (e.g., 10 for Node.js) since each request uses minimal CPU while waiting on network.
- For CPU-bound workloads: set PerExecutionEnvironmentMaxConcurrency to 1-2 per vCPU since each request saturates CPU.
- Scaling: MinExecutionEnvironments (default 3), MaxVCpuCount (optional, default 400 — set explicitly as best practice), TargetResourceUtilization.
Step 4: Migrate the Code
Review code for concurrency safety. LMI runs multiple invocations concurrently per execution environment:
- Python: Process-based isolation — globals are NOT shared. No thread-safety changes needed. Focus on
/tmpconflicts and memory sizing. - Node.js: Worker threads — globals shared within a worker. Requires async safety.
- Java/.NET: OS threads/Tasks — handler shared across threads. Requires full thread safety.
Step 5: Set Up Infrastructure
1. Create two IAM roles: execution role (for the function) and operator role (for capacity provider EC2 management) 2. Configure VPC with subnets across 3+ AZs 3. Create capacity provider with VPC config and scaling limits 4. Create or update function with capacity provider attachment 5. Publish a version (triggers instance provisioning)
Step 6: Validate and Cut Over
1. Deploy to a non-production environment first 2. Monitor CloudWatch: CPU utilization, memory, concurrency, throttle rate 3. Gradual traffic shift with weighted aliases (10% → 50% → 100%) 4. Compare costs after 1-2 weeks of production data 5. Decommission standard Lambda once stable
Best Practices
Pricing (always mention when discussing costs)
- Three components: EC2 instance hours + 15% management fee + $0.20/1M requests
- Savings Plans: Compute Savings Plans apply to the EC2 portion (up to 60-72% discount)
- The 15% fee is charged on top of EC2 cost for AWS managing provisioning, patching, scaling, lifecycle
Scaling (always mention when discussing scaling or traffic)
- LMI absorbs a 50% traffic spike immediately and doubles capacity within 5 minutes — if traffic more than doubles faster, requests throttle
- Standard Lambda bursts to 3000 instantly — LMI cannot match this
- Pre-warm with MinExecutionEnvironments before known spikes
- MaxVCpuCount (default 400) — set explicitly as a cost ceiling
- Shape: Reduce MinExecutionEnvironments to lower capacity during off-hours (minimum 3 for AZ resiliency)
Instance Sizing
- 1 vCPU + 1 GB reserved per instance for OS overhead (not available to your function)
- Usable capacity = total - overhead
Configuration
- Start with 4:1 ratio and runtime default concurrency
- Use ARM (Graviton) unless x86 dependencies exist
- Let Lambda choose instance types unless specific hardware needed
- Set MaxVCpuCount to control cost ceiling
- Never set MinExecutionEnvironments below 3 (breaks AZ resiliency)
Migration
- Start with I/O-heavy functions (benefit most from multi-concurrency)
- Review code for concurrency safety before attaching to capacity provider
- Use weighted aliases for gradual traffic shift
- Include request IDs in all log statements
- Initialize DB pools and SDK clients outside the handler
Operations
- Set CloudWatch alarms on throttle rate > 1% and CPU > 80%
- Plan for 14-day instance rotation (automatic)
- Never manually terminate LMI EC2 instances (delete the capacity provider instead)
- Always publish a version — unpublished functions cannot run on LMI
Limits Quick Reference
| Resource | Limit |
|---|---|
| Memory | 2 GB min, 32 GB max |
| Execution environments | 3 minimum (MinExecutionEnvironments, AZ resiliency) |
| Instance lifespan | 14 days (auto-replaced) |
| Concurrency/vCPU | 64 (Node.js), 32 (Java/.NET), 16 (Python) |
| Runtimes | Node.js 22+, Java 21+, .NET 8+, Python 3.13+, Rust (provided.al2023) |
| Instance families | C, M, R (.large and up) |
| Scaling | Burst headroom equals unused capacity from TargetResourceUtilization; new instances launch within minutes |
Security Considerations
- Operator role scoping: Add
aws:SourceAccountandaws:SourceArnconditions to trust policies to prevent confused deputy attacks. - VPC egress: Scope security group egress to VPC endpoint security groups or AWS prefix lists rather than 0.0.0.0/0.
- Credentials: Use AWS Secrets Manager or Parameter Store for database credentials — never environment variables for secrets.
- Encryption: Enable SQS SSE, CloudWatch Logs encryption (KMS), and S3 default encryption for any data at rest.
- Logging: Set CloudWatch Log group retention policies. Avoid logging PII or credentials. Enable CloudTrail data events for Lambda.
- Instance rotation: The 14-day automatic rotation ensures security patches are applied without manual intervention.
- References: Lambda Security Best Practices, IAM Best Practices
Files
| File | Content |
|---|---|
| cost-comparison.md | Pricing analysis, break-even calculations, Savings Plans/RI impact |
| configuration-guide.md | Instance selection, memory ratios, scaling tuning, capacity provider config |
| thread-safety.md | Concurrency model per runtime, code review checklist, Powertools compatibility |
| migration-patterns.md | Before/after code by runtime, connection pooling, gradual cutover |
| infrastructure-setup.md | IAM roles, VPC setup, SAM templates, CLI commands |
| troubleshooting.md | Common errors, throttling, debugging, stuck deployments |
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SQS processor on Lambda Managed Instances (c7g.xlarge, arm64)
Parameters:
Environment:
Type: String
AllowedValues: [dev, staging, prod]
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: 3+ subnets across different AZs
SecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: Security group with HTTPS (443) egress scoped to VPC endpoint SGs or AWS prefix lists. Avoid 0.0.0.0/0.
Resources:
OperatorRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaManagedEC2ResourceOperator
CapacityProvider:
Type: AWS::Lambda::CapacityProvider
Properties:
CapacityProviderName: !Sub sqs-processor-cp-${Environment}
VpcConfig:
SubnetIds: !Ref SubnetIds
SecurityGroupIds:
- !Ref SecurityGroupId
PermissionsConfig:
CapacityProviderOperatorRoleArn: !GetAtt OperatorRole.Arn
InstanceRequirements:
Architectures: [arm64]
AllowedInstanceTypes: [c7g.xlarge]
CapacityProviderScalingConfig:
MaxVCpuCount: 16
SqsProcessorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub sqs-processor-${Environment}
Runtime: nodejs22.x
Handler: index.handler
CodeUri: src/
Architectures: [arm64]
MemorySize: 4096
Timeout: 900
CapacityProviderConfig:
LambdaManagedInstancesCapacityProviderConfig:
CapacityProviderArn: !GetAtt CapacityProvider.Arn
PerExecutionEnvironmentMaxConcurrency: 10
Events:
SQSEvent:
Type: SQS
Properties:
Queue: !GetAtt ProcessingQueue.Arn
BatchSize: 10
Policies:
- SQSPollerPolicy:
QueueName: !GetAtt ProcessingQueue.QueueName
ProcessingQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub sqs-processor-queue-${Environment}
VisibilityTimeout: 960
SqsManagedSseEnabled: true
ProcessorLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/sqs-processor-${Environment}
RetentionInDays: 30
Outputs:
FunctionArn:
Value: !GetAtt SqsProcessorFunction.Arn
QueueUrl:
Value: !Ref ProcessingQueue
CapacityProviderArn:
Value: !GetAtt CapacityProvider.Arn
LMI Configuration Guide
Instance Type Decision Tree
- CPU-intensive (encoding, ML, compression) → C-series, 2:1 ratio, concurrency=1/vCPU
- Memory-intensive (caching, large datasets) → R-series, 8:1 ratio
- Network-intensive (streaming, data transfer) → Use AllowedInstanceTypes for n-suffix types, 4:1 ratio
- General/balanced (web APIs, microservices) → M-series, 4:1 ratio, default concurrency
Architecture: ARM (Graviton, g-suffix) for price-performance. x86 (i=Intel, a=AMD) when dependencies require it.
Memory-to-vCPU Ratios
| Ratio | Profile | When to use | Memory examples |
|---|---|---|---|
| 2:1 | Compute | CPU-bound work | 2GB/1vCPU, 4GB/2vCPU |
| 4:1 | General | Most workloads (default) | 4GB/1vCPU, 8GB/2vCPU |
| 8:1 | Memory | Caching, data, Python apps | 8GB/1vCPU, 16GB/2vCPU |
Min: 2 GB / 1 vCPU. Max: 32 GB. Memory must align with ratio multiples.
Memory Sizing from Existing Lambda
| Current Lambda | LMI memory | Ratio | Rationale |
|---|---|---|---|
| 128-512 MB | 2048 MB | 4:1 | LMI minimum; multi-concurrency shares memory |
| 512 MB-1 GB | 2048 MB | 4:1 | Room for concurrent requests |
| 1-2 GB | 4096 MB | 4:1 | Standard upgrade path |
| 2-4 GB | 4096-8192 MB | 4:1 or 8:1 | Depends on memory vs CPU bottleneck |
| 4-10 GB | 8192-16384 MB | 8:1 | Likely memory-heavy workload |
Concurrency Tuning
| Runtime | Default per EE | I/O-bound | CPU-bound |
|---|---|---|---|
| Node.js | 64 | Keep or increase | 1 per vCPU |
| Java | 32 | Keep | 1 per vCPU |
| .NET | 32 | Keep | 1 per vCPU |
| Python | 16 | Keep | 1 per vCPU |
Total capacity = MinExecutionEnvironments × PerExecutionEnvironmentMaxConcurrency
Capacity Provider Scaling Controls
| Control | Default | Guidance |
|---|---|---|
| MinExecutionEnvironments | 3 | Increase for baseline capacity; never below 3 |
| MaxExecutionEnvironments | — | Set based on cost budget |
| MaxVCpuCount | 400 | Optional but recommended — set explicitly to control cost ceiling |
| TargetResourceUtilization | ~50% headroom | Raise for cost savings (less burst tolerance) |
| AllowedInstanceTypes | All | Restrict only for specific hardware needs |
| ExcludedInstanceTypes | None | Exclude expensive types in dev/test |
Monitoring Thresholds
- CPU > 80%: reduce concurrency or add vCPUs
- CPU < 20%: increase concurrency for better utilization
- Throttle rate (429s) > 1%: increase MinExecutionEnvironments or reduce utilization target
- Memory > 90%: increase memory or reduce concurrency
- ExecutionEnvironmentConcurrency near limit: saturation — reduce concurrency or scale out
CloudWatch Metrics Dimensions
LMI metrics are split across two CloudWatch dimensions:
- Alias (live): Invocations, Errors, Throttles, Duration
- Version ($LATEST or numbered): CPUUtilization, MemoryUtilization, ExecutionEnvironmentConcurrency, ExecutionEnvironmentCount
Create a unified dashboard combining both views to monitor LMI performance effectively.
Lambda vs LMI Cost Comparison
Use the LMI Pricing Calculator for accurate, up-to-date cost comparisons based on your specific workload parameters (region, instance type, request volume, duration).
When building a cost comparison for a user, gather: region, runtime, requests/month, average duration, memory, and architecture (x86 vs ARM). Plug these into the calculator rather than relying on hardcoded estimates.
When LMI is NOT Cheaper
- low number of requests/month (fixed 3-instance cost exceeds Lambda)
- Very short functions (< 100ms duration)
- Highly bursty, unpredictable traffic
- Workloads needing scale-to-zero
Tools
- LMI Pricing Calculator — interactive comparison tool
- AWS Pricing Calculator — general AWS cost estimation
LMI Infrastructure Setup
IAM Roles (Two Required)
1. Execution Role (for the function)
Trust policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<ACCOUNT_ID>"
}
}
}]
}Minimum permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
}
]
}Add VPC permissions only if the function accesses VPC resources:
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:Vpc": "arn:aws:ec2:<REGION>:<ACCOUNT_ID>:vpc/<VPC_ID>"
}
}
}2. Operator Role (for capacity provider EC2 management)
Trust policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<ACCOUNT_ID>"
}
}
}]
}Minimum permissions (scoped with conditions). Use the AWS managed policy `AWSLambdaManagedEC2ResourceOperator` or the equivalent:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:RunInstances", "ec2:CreateTags", "ec2:AttachNetworkInterface"],
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:network-interface/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"StringEquals": {
"ec2:ManagedResourceOperator": "scaler.lambda.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCapacityReservations",
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeInstanceTypes",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcEncryptionControls"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["ec2:RunInstances", "ec2:CreateNetworkInterface"],
"Resource": [
"arn:aws:ec2:*:*:subnet/*",
"arn:aws:ec2:*:*:security-group/*"
]
},
{
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:image/*",
"Condition": {
"StringEquals": { "ec2:Owner": "amazon" }
}
}
]
}The ec2:ManagedResourceOperator condition ensures RunInstances/CreateTags only apply to Lambda-managed instances.
VPC Requirements
LMI runs functions on EC2 instances inside the VPC. These instances need VPC endpoints or NAT to reach AWS services.
- 3+ subnets across different AZs (for default 3-instance fleet)
- Security groups: HTTPS egress (port 443) scoped to VPC endpoint security groups or AWS prefix lists (avoid 0.0.0.0/0); no ingress needed
- Required VPC endpoints:
| Endpoint | Type | Purpose |
|---|---|---|
| S3 | Gateway | Object storage access |
| DynamoDB | Gateway | Table access |
| SQS | Interface | Queue operations |
| CloudWatch Logs | Interface | Log delivery |
| CloudWatch Monitoring | Interface | Metrics/EMF |
| X-Ray | Interface | Distributed tracing |
CLI Workflow
Required Parameters
| Parameter | Description |
|---|---|
SUBNET_IDS | Comma-separated subnet IDs across 3+ AZs |
SECURITY_GROUP_ID | Security group ID for the capacity provider |
ACCOUNT_ID | AWS account ID |
OPERATOR_ROLE_ARN | ARN of the operator role |
EXECUTION_ROLE_ARN | ARN of the execution role |
FUNCTION_NAME | Name for the Lambda function |
CP_NAME | Name for the capacity provider |
ARCHITECTURE | arm64 (Graviton) or x86_64 |
Manual Steps
# 1. Create capacity provider
aws lambda create-capacity-provider \
--capacity-provider-name $CP_NAME \
--vpc-config "SubnetIds=[$SUBNET_IDS],SecurityGroupIds=[$SECURITY_GROUP_ID]" \
--permissions-config "CapacityProviderOperatorRoleArn=$OPERATOR_ROLE_ARN" \
--instance-requirements "Architectures=[$ARCHITECTURE]" \
--capacity-provider-scaling-config "MaxVCpuCount=30"
# 2. Create function
aws lambda create-function --function-name $FUNCTION_NAME --runtime python3.13 \
--handler app.handler --zip-file fileb://function.zip \
--role $EXECUTION_ROLE_ARN --architectures $ARCHITECTURE \
--memory-size 4096 \
--capacity-provider-config \
"LambdaManagedInstancesCapacityProviderConfig={CapacityProviderArn=arn:aws:lambda:$AWS_REGION:$ACCOUNT_ID:capacity-provider:$CP_NAME}"
# 3. Publish version (triggers provisioning — takes several minutes)
aws lambda publish-version --function-name $FUNCTION_NAME
# 4. Invoke (must use versioned ARN)
aws lambda invoke --function-name $FUNCTION_NAME:1 --payload '{}' response.jsonArchitecture must match between function and capacity provider.
SAM Template
Resources:
MyCP:
Type: AWS::Lambda::CapacityProvider
Properties:
CapacityProviderName: my-cp
VpcConfig:
SubnetIds: [!Ref Sub1, !Ref Sub2, !Ref Sub3]
SecurityGroupIds: [!Ref SG]
PermissionsConfig:
CapacityProviderOperatorRoleArn: !GetAtt OpRole.Arn
InstanceRequirements:
Architectures: [arm64]
CapacityProviderScalingConfig:
MaxVCpuCount: 30
MyFn:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.13
Handler: app.handler
MemorySize: 4096
Architectures: [arm64]
CapacityProviderConfig:
LambdaManagedInstancesCapacityProviderConfig:
CapacityProviderArn: !GetAtt MyCP.ArnCleanup
aws lambda delete-function --function-name my-fn
aws lambda delete-capacity-provider --capacity-provider-name my-cpDeleting the capacity provider destroys all associated EC2 instances.
LMI Migration Patterns
Before/after code examples for migrating to multi-concurrency.
Node.js
Global State
// BEFORE (race condition)
let requestCount = 0;
exports.handler = async (event) => {
requestCount++;
return { count: requestCount };
};
// AFTER (request-isolated)
const { AsyncLocalStorage } = require('node:async_hooks');
const als = new AsyncLocalStorage();
exports.handler = async (event, context) => {
return als.run({ id: context.awsRequestId }, async () => {
return await processEvent(event);
});
};File I/O
// BEFORE (shared path)
fs.writeFileSync('/tmp/output.json', JSON.stringify(data));
// AFTER (request-unique path)
const path = `/tmp/output-${context.awsRequestId}.json`;
try { fs.writeFileSync(path, JSON.stringify(data)); }
finally { fs.unlinkSync(path); }Database
// BEFORE (per-invocation connection)
exports.handler = async (event) => {
const conn = await mysql.createConnection({/*...*/});
const [rows] = await conn.execute('SELECT ...');
await conn.end();
};
// AFTER (shared pool)
// For production: retrieve credentials from AWS Secrets Manager
// const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const pool = mysql.createPool({ connectionLimit: 10, /*...*/ });
exports.handler = async (event) => {
const [rows] = await pool.execute('SELECT ...');
return rows;
};Python
Python on LMI uses process-based isolation. Each concurrent invocation runs in its own process with independent memory. Global state is NOT shared, so no locking is needed. The main migration concerns are /tmp conflicts, memory sizing, and connection pooling.
Global State (No Changes Needed)
# This is SAFE on LMI — each process has its own copy of cache
cache = {}
def handler(event, context):
cache[event['key']] = compute(event)
return cache[event['key']]
# Module-level clients are also safe (isolated per process)
s3_client = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')File I/O (Change Required — /tmp is shared across processes)
# BEFORE (conflict — all processes share /tmp)
with open('/tmp/data.json', 'w') as f: json.dump(event, f)
# AFTER (request-unique path)
path = f'/tmp/data-{context.aws_request_id}.json'
try:
with open(path, 'w') as f: json.dump(event, f)
finally:
os.unlink(path)Database (Change Required — each process needs pooled connections)
# BEFORE (per-invocation connection — exhausts limits at concurrency)
def handler(event, context):
conn = psycopg2.connect(host='...')
# AFTER (pool per process — initialized at module level)
from psycopg2 import pool
db_pool = pool.SimpleConnectionPool(1, 3, host=os.environ['DB_HOST'])
def handler(event, context):
conn = db_pool.getconn()
try: return query(conn, event)
finally: db_pool.putconn(conn)
# Note: total connections = pool_size × concurrency (e.g., 3 × 16 = 48)
# For production: retrieve credentials from Secrets Manager, not environment variables
# import boto3
# secret = boto3.client("secretsmanager").get_secret_value(SecretId="my-db-creds")Memory Sizing
# A function using 200 MB per process with default concurrency of 16:
# Total memory ≈ 200 MB × 16 = 3.2 GB
# Use 4:1 or 8:1 memory-to-vCPU ratio to accommodate
# Monitor MemoryUtilization metric and adjust as neededJava
Global State
// BEFORE (race condition)
private static Map<String, String> cache = new HashMap<>();
// AFTER (thread-safe)
private static final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
// Use cache.computeIfAbsent(key, k -> compute(k));Database
// BEFORE (per-invocation)
Connection conn = DriverManager.getConnection("jdbc:...");
// AFTER (HikariCP pool, static init)
private static final HikariDataSource ds;
static {
HikariConfig c = new HikariConfig();
// For production: retrieve credentials from Secrets Manager, not env vars
c.setJdbcUrl(System.getenv("DB_URL"));
c.setMaximumPoolSize(10);
ds = new HikariDataSource(c);
}
// Use: try (Connection conn = ds.getConnection()) { ... }Concurrency Safety for LMI
LMI runs multiple invocations concurrently in the same execution environment. The concurrency model differs by runtime — some require thread safety, others provide process isolation.
Code Review Checklist
When reviewing a function for LMI readiness, check each item:
- [ ] No shared
/tmppaths (use request ID in filenames, clean up after — shared across ALL runtimes) - [ ] Database connections use pools (initialized outside handler, not per-invocation)
- [ ] SDK clients outside handler (module-level singletons are fine — they are thread-safe)
- [ ] Logging includes request ID (for tracing concurrent requests)
- [ ] Node.js/Java/.NET only: No global/static mutable variables (use immutable or request-local state)
- [ ] Node.js/Java/.NET only: Thread-safe libraries only (check DB drivers, HTTP clients, caching libs)
- [ ] Node.js/Java/.NET only: No request state in global scope (use AsyncLocalStorage, ThreadLocal,
AsyncLocal<T>) - [ ] Node.js/Java/.NET only: No environment variable mutation during requests
- [ ] Python only: Memory budget accounts for per-process multiplication (memory × concurrency)
Runtime-Specific Guidance
Python (Process-Based Isolation)
Python uses multiple independent processes, each with its own interpreter and memory space. Global variables, module-level caches, and singleton objects are duplicated per process, not shared. If a function works on standard Lambda today, it works on LMI without code changes related to shared state.
Key concerns:
- Memory consumption: total footprint ≈ per-process memory × concurrency. A 200 MB function with 16 concurrent processes can consume 3+ GB.
/tmpfilesystem is shared across all processes — usecontext.aws_request_idin filenames- Each process needs its own connection pool — size pools per-process, not globally
- Prefer 4:1 or 8:1 memory-to-vCPU ratio to accommodate memory multiplication
- Monitor
MemoryUtilizationmetric and adjust ratio if needed
Safe patterns (no locking needed):
- Module-level mutable globals (isolated per process)
- Module-level SDK clients and caches
os.environreads
Node.js (Worker Threads + Async/Await)
Uses worker threads combined with async/await event loops. The handler and global state are shared across concurrent invocations within a worker thread.
The await keyword yields control to the event loop, which may execute another invocation that overwrites shared state before the first resumes.
Key concerns:
- Use
AsyncLocalStoragefromnode:async_hooksfor request context - Keep mutable state within handler local scope
- Initialize SDK clients and DB pools at module level (they are thread-safe)
- Avoid module-level mutable state (
let count = 0is a race condition) - Callback-based handlers are NOT supported on Node.js 22 — use async handlers
Java (OS Threads)
Uses OS-level threads. Lambda loads the handler class once and invokes handleRequest from multiple threads simultaneously.
Key concerns:
- Use immutable objects and thread-safe collections (
ConcurrentHashMap,Collections.synchronizedList) - Initialize SDK clients and connection pools in constructor or static block
- Avoid mutable
staticfields - Use
ThreadLocal<T>for request-specific state - Use HikariCP or similar for connection pooling (AWS SDK for Java 2.x clients are thread-safe)
.NET (Task-Based Concurrency)
Uses a single process with .NET Tasks (same model as ASP.NET Core). The handler object is shared across all Tasks.
Key concerns:
- Use
AsyncLocal<T>for request-scoped data - Inject scoped services via DI container
- Initialize
HttpClientand SDK clients as singletons - Use
ConcurrentDictionary<TKey, TValue>andSemaphoreSlimfor thread-safe access - Invocation timeouts are NOT enforced by the runtime — use
ILambdaContext.RemainingTime
Common Anti-Patterns
| Anti-pattern | Affected Runtimes | Risk | Fix |
|---|---|---|---|
| New DB connection per invocation | All | Exhausts connection limits | Module-level connection pool |
Hardcoded /tmp paths | All | File conflicts across processes | Use aws_request_id in path |
| Logging without request ID | All | Unreadable interleaved logs | Include aws_request_id |
| Mutable module-level state | Node.js, Java, .NET | Race condition / state corruption | Request-local scope or concurrent collections |
| Setting env vars during request | Node.js, Java, .NET | Race condition | Pass state via parameters |
| Assuming sequential execution | Node.js, Java, .NET | State corruption | Each invocation must be self-contained |
| Ignoring memory multiplication | Python | OOM at high concurrency | Account for per-process × concurrency |
Powertools for AWS Lambda Compatibility
Powertools handles multi-concurrency transparently. No code changes needed.
| Runtime | Package | Minimum Version |
|---|---|---|
| Python | Powertools for AWS Lambda (Python) | 3.23.0 |
| TypeScript | Powertools for AWS Lambda (TypeScript) | 2.29.0 |
| Java | Powertools for AWS Lambda (Java) | 2.8.0 |
| .NET | Powertools for AWS Lambda (.NET) | 3.1.0 |
AWS SDK and X-Ray minimum versions:
| Runtime | AWS SDK minimum | X-Ray SDK minimum |
|---|---|---|
| Node.js | AWS SDK for JavaScript v3 (3.933.0) | 3.12.0 |
| Java | AWS SDK for Java 2.0 (2.34.0) | 2.20.0 |
| .NET | AWSSDK.Core (4.0.0.32) | AWSXRayRecorder.Core (2.16.0) |
LMI Troubleshooting
Common Issues
| Issue | Cause | Resolution |
|---|---|---|
| 429 throttles during scale-up | Traffic doubled faster than 5-min scaling window | Increase MinExecutionEnvironments or lower TargetResourceUtilization |
| Function stuck in PENDING | Capacity provider provisioning instances | Wait several minutes; verify VPC subnets have IP capacity and IAM roles are correct |
| Architecture mismatch error | Function architecture ≠ capacity provider | Align both to arm64 or x86_64 |
| Cannot terminate EC2 instances | LMI instances managed by capacity provider | Delete capacity provider to destroy instances; cannot use EC2 console |
| High CPU, low throughput | Concurrency too high for CPU-bound work | Reduce PerExecutionEnvironmentMaxConcurrency to 1/vCPU |
| Race conditions in production | Code not thread-safe for multi-concurrency | Review with checklist in thread-safety.md |
| Function version not ACTIVE | Fewer than 3 execution environments ready | Wait for provisioning; check capacity provider status |
| Unexpected 500 errors | Unhandled concurrent access to shared state | Add thread-safe patterns from migration-patterns.md |
| CloudWatch logs missing | VPC egress not configured | Add NAT Gateway or CloudWatch Logs VPC endpoint |
| High costs despite low traffic | Minimum 3 instances always running | Evaluate if standard Lambda is more cost-effective |
Debugging Steps
Throttling Issues
1. Check throttles metric for the reason for throttles
Function Not Starting
1. Check capacity provider status: aws lambda get-capacity-provider --capacity-provider-name <name> 2. Verify subnets span 3+ AZs with available IPs 3. Confirm security group allows necessary egress 4. Check operator role has required permissions 5. Check for LMI-managed instances:
aws ec2 describe-instances --filters "Name=tag-key,Values=aws:lambda:capacity-provider" \
--query "Reservations[].Instances[].{Id:InstanceId,State:State.Name}"Performance Issues
1. Check CloudWatch metrics (5-min intervals): CPU utilization, memory, concurrency/env 2. If CPU > 80%: reduce concurrency or add vCPUs (increase memory with appropriate ratio) 3. If throttles > 1%: increase MinExecutionEnvironments 4. If CPU < 20%: increase concurrency — resources are underutilized 5. For Python: verify 4:1 or 8:1 ratio (GIL limits CPU parallelism)
Cost Issues
1. Verify instance count matches actual need (not over-provisioned) 2. Check if Savings Plans or RIs are applied to these instances 3. Compare actual costs against the LMI Pricing Calculator 4. If traffic is lower than expected, consider reducing MaxVCpuCount 5. For dev/test: use ExcludedInstanceTypes to avoid expensive instance families
Related skills
FAQ
What does aws-lambda-managed-instances do?
Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI). Runs Lambda functions on EC2 instances in the user's account while AWS manages provisioning, patching,...
When should I use aws-lambda-managed-instances?
Invoke when Evaluates, configures, and migrates workloads to AWS Lambda Managed Instances (LMI). Runs Lambda functions on EC2 instances in the user's ac.
Is aws-lambda-managed-instances safe to install?
Review the Security Audits panel on this page before installing in production.