Docs / Lambda

Lambda

LocalEmu Lambda executes your function code in real Docker containers using the official AWS Lambda runtime images from public.ecr.aws/lambda. State persists across restarts when PERSISTENCE=1. Cold-start, container reuse, layers, versioning, aliases, VPC configuration, and event-source mappings all work end-to-end.

Coverage at the operation level: see the Lambda coverage matrix for which of the 85 Lambda operations are implemented.

Quick start

Create a function from a zip

Terminal
$ cat > handler.py <<'EOF'
def lambda_handler(event, context):
    return {"statusCode": 200, "body": "hello from LocalEmu"}
EOF
$ zip handler.zip handler.py

$ awsemu lambda create-function \
    --function-name hello \
    --runtime python3.12 \
    --role arn:aws:iam::000000000000:role/lambda-role \
    --handler handler.lambda_handler \
    --zip-file fileb://handler.zip

FunctionArn: arn:aws:lambda:us-east-1:000000000000:function:hello
State: Active
Runtime: python3.12

No build step on LocalEmu's side. The zip is loaded into the runtime container at invocation time.

Invoke synchronously

Terminal
$ awsemu lambda invoke \
    --function-name hello \
    --payload '{}' \
    /tmp/out.json

StatusCode: 200

$ cat /tmp/out.json
{"statusCode": 200, "body": "hello from LocalEmu"}

Invoke asynchronously

Terminal
$ awsemu lambda invoke \
    --function-name hello \
    --invocation-type Event \
    --payload '{"k":"v"}' \
    /tmp/out.json

StatusCode: 202

Async invocations return 202 immediately, the queue dispatches to a container in the background, and retries follow AWS's exponential backoff (configurable, see LAMBDA_RETRY_BASE_DELAY_SECONDS).

Supported runtimes

Image format: public.ecr.aws/lambda/<family>:<version>. The runtime identifier you pass to --runtime maps to a tagged image in AWS's public ECR gallery. The image is pulled on first use and cached locally.

Family Active runtimes Deprecated (best-effort)
nodejs nodejs24.x, nodejs22.x, nodejs20.x, nodejs18.x, nodejs16.x nodejs14.x, nodejs12.x
python python3.14, python3.13, python3.12, python3.11, python3.10, python3.9, python3.8 python3.7
java java25, java21, java17, java11, java8.al2 java8
dotnet dotnet10, dotnet8, dotnet6 dotnetcore3.1
ruby ruby3.4, ruby3.3, ruby3.2 ruby2.7
go (use provided.al2023 for Go) go1.x
provided provided.al2023, provided.al2 provided

Container image functions (--package-type Image) bypass this mapping and run your image directly. Pull the runtime images ahead of time with LAMBDA_PREBUILD_IMAGES=1 if you want to avoid the first-invocation wait.

Executor architecture

LocalEmu ships one runtime executor: docker. Selected by LAMBDA_RUNTIME_EXECUTOR (default: docker). The executor spins up a container per cold start, keeps it warm between invocations, and tears it down after LAMBDA_KEEPALIVE_MS of idle time (default 10 minutes).

Container lifecycle:

Containers expose AWS_ENDPOINT_URL in the environment so boto3 inside your function reaches LocalEmu without any extra wiring. Unset this with LAMBDA_DISABLE_AWS_ENDPOINT_URL=1 if you need the function to use a different endpoint.

Limits and defaults

Limit Default Source
Function timeout3 s (max 900 s)AWS Lambda limit
Memory128 MB (max 10240 MB)AWS Lambda limit
Sync invoke payload6 MBLAMBDA_LIMITS_MAX_FUNCTION_PAYLOAD_SIZE_BYTES
Function code (zipped)50 MBLAMBDA_LIMITS_CODE_SIZE_ZIPPED
Function code (unzipped)250 MBLAMBDA_LIMITS_CODE_SIZE_UNZIPPED
Layers per function5AWS Lambda limit
Env vars per function4 KiB totalLAMBDA_LIMITS_MAX_FUNCTION_ENVVAR_SIZE_BYTES
Concurrent executions (account)1000LAMBDA_LIMITS_CONCURRENT_EXECUTIONS
Total code storage (account)~75 GBLAMBDA_LIMITS_TOTAL_CODE_SIZE

All limit env vars accept a raw byte count, so LAMBDA_LIMITS_CODE_SIZE_ZIPPED=104857600 raises the zipped-code limit to 100 MB.

Configuration

The full set of Lambda environment variables, grouped by purpose. Defaults are the values used when the variable is unset.

Executor and runtime images

VariableDefaultPurpose
LAMBDA_RUNTIME_EXECUTORdockerRuntime executor plugin. One option ships today: docker.
LAMBDA_KEEPALIVE_MS600000Idle window (ms) before a warm container is torn down.
LAMBDA_REMOVE_CONTAINERStrueWhether to delete containers when they're stopped. Set false to keep them for inspection.
LAMBDA_PREBUILD_IMAGESfalsePre-pull runtime images at LocalEmu startup. Avoids first-invocation pull latency.
LAMBDA_RUNTIME_IMAGE_MAPPING(none)JSON map overriding the runtime identifier to image mapping. Useful for air-gapped clones of the AWS images.
LAMBDA_RUNTIME_VALIDATION0When 1, validate that the runtime image exists before invocation.
LAMBDA_IGNORE_ARCHITECTUREfalseSkip architecture (x86_64/arm64) checks. Useful when running arm64 images on x86 hosts.

Container and networking

VariableDefaultPurpose
LAMBDA_DOCKER_NETWORK(none)Docker network to attach Lambda containers to. Set this to your Compose network so containers can reach other services.
LAMBDA_DOCKER_FLAGS(none)Extra flags passed to docker run. Use for capabilities, volume mounts, etc.
LAMBDA_DISABLE_AWS_ENDPOINT_URLfalseStop injecting AWS_ENDPOINT_URL into the function environment.

Cold start and timing

VariableDefaultPurpose
LAMBDA_COLD_START_DELAY0Artificial cold-start delay (seconds). Use to test cold-start handling in your code.
LAMBDA_COLD_START_IDLE_TIMEOUT300Idle timeout (seconds) before a container is considered cold again. Independent of LAMBDA_KEEPALIVE_MS.
LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT20Seconds to wait for the runtime container to come up before failing the invocation.

Account-wide limits

VariableDefault (bytes / count)
LAMBDA_LIMITS_CONCURRENT_EXECUTIONS1000
LAMBDA_LIMITS_MINIMUM_UNRESERVED_CONCURRENCY100
LAMBDA_LIMITS_TOTAL_CODE_SIZE80530636800
LAMBDA_LIMITS_CODE_SIZE_ZIPPED52428800
LAMBDA_LIMITS_CODE_SIZE_UNZIPPED262144000
LAMBDA_LIMITS_CREATE_FUNCTION_REQUEST_SIZE70167211
LAMBDA_LIMITS_MAX_FUNCTION_ENVVAR_SIZE_BYTES4096
LAMBDA_LIMITS_MAX_FUNCTION_PAYLOAD_SIZE_BYTES6291300

Event source mapping

VariableDefaultPurpose
LAMBDA_EVENT_SOURCE_MAPPING_POLL_INTERVAL_SEC1How often the poller checks each event source.
LAMBDA_EVENT_SOURCE_MAPPING_MAX_BACKOFF_ON_ERROR_SEC60Max backoff (seconds) when an event source returns errors.
LAMBDA_EVENT_SOURCE_MAPPING_MAX_BACKOFF_ON_EMPTY_POLL_SEC10Max backoff (seconds) when the source has no events.

Retries, streams, and create timing

VariableDefaultPurpose
LAMBDA_RETRY_BASE_DELAY_SECONDS60Base delay between async-invoke retries (seconds).
LAMBDA_TRUNCATE_STDOUT2000Truncate function stdout to this many characters in logs.
LAMBDA_SYNCHRONOUS_CREATEfalseBlock CreateFunction until the runtime image is pulled. Useful in CI.

Debug and init binary

The init binary is the entrypoint LocalEmu injects into runtime containers (the same role AWS's aws-lambda-rie plays). These variables override its path, version, or runtime behaviour.

VariableDefaultPurpose
LAMBDA_DEBUG_MODEfalseEnable verbose Lambda-service logging.
LAMBDA_DEBUG_MODE_CONFIG_PATH(none)Path to a debug config file.
LAMBDA_INIT_DEBUGfalseVerbose logging from the init binary inside the container.
LAMBDA_INIT_BIN_PATH(bundled)Override the init binary path on the host.
LAMBDA_INIT_BOOTSTRAP_PATH(bundled)Override the bootstrap script path.
LAMBDA_INIT_RELEASE_VERSION(latest)Pin the init binary release version.
LAMBDA_INIT_USER(container default)Run the init binary as a specific UID/user.
LAMBDA_INIT_POST_INVOKE_WAIT_MS(none)Sleep (ms) inside the init binary after each invocation. Useful for log-flush races.
LAMBDA_INIT_DELVE_PATH(none)Path to the Delve debugger binary, for attaching to Go runtimes.
LAMBDA_INIT_DELVE_PORT40000Port the Delve debugger listens on inside the container.

Features supported

FeatureNotes
Zip and container-image packagesBoth --package-type Zip and --package-type Image work. Container images can be pulled from LocalEmu's ECR.
Versioning and aliasespublish-version, create-alias, traffic shifting via routing config.
LayersUp to 5 layers per function. Cross-account fetch is not supported (see Known limitations).
Function URLscreate-function-url-config exposes the function over HTTP at the gateway.
Provisioned and reserved concurrencyConcurrency limits are enforced against LAMBDA_LIMITS_CONCURRENT_EXECUTIONS.
Dead-letter queuesSQS and SNS DLQs on async invoke.
Environment variables, tagsBoth per-function and per-version.
X-Ray tracingEnabled when TracingMode is set to Active.
SnapStartStored on the function version. Cold-start delay setting drives the simulated restore time.
Code signingCodeSigningConfig CRUD is implemented.

Examples

Container-image function

Terminal
$ awsemu ecr create-repository --repository-name my-fn
$ docker build -t my-fn .
$ docker tag my-fn 000000000000.dkr.ecr.us-east-1.localhost:4566/my-fn:latest
$ docker push 000000000000.dkr.ecr.us-east-1.localhost:4566/my-fn:latest

$ awsemu lambda create-function \
    --function-name my-fn \
    --package-type Image \
    --role arn:aws:iam::000000000000:role/lambda-role \
    --code ImageUri=000000000000.dkr.ecr.us-east-1.localhost:4566/my-fn:latest

PackageType: Image
State: Active

Publish a version, attach an alias

Terminal
$ awsemu lambda publish-version --function-name hello
Version: 1

$ awsemu lambda create-alias \
    --function-name hello \
    --name prod \
    --function-version 1

AliasArn: arn:aws:lambda:us-east-1:000000000000:function:hello:prod

$ awsemu lambda invoke \
    --function-name hello:prod \
    --payload '{}' \
    /tmp/out.json
StatusCode: 200

Publish a layer, attach to the function

Terminal
$ mkdir -p python && pip install requests -t python/
$ zip -r layer.zip python

$ awsemu lambda publish-layer-version \
    --layer-name shared-deps \
    --zip-file fileb://layer.zip \
    --compatible-runtimes python3.12 python3.11

LayerVersionArn: arn:aws:lambda:us-east-1:000000000000:layer:shared-deps:1

$ awsemu lambda update-function-configuration \
    --function-name hello \
    --layers arn:aws:lambda:us-east-1:000000000000:layer:shared-deps:1

SQS event source mapping

Terminal
$ awsemu sqs create-queue --queue-name jobs
$ awsemu lambda create-event-source-mapping \
    --function-name hello \
    --event-source-arn arn:aws:sqs:us-east-1:000000000000:jobs \
    --batch-size 10

UUID: 8a7b6c5d-...
State: Enabled

$ awsemu sqs send-message \
    --queue-url http://sqs.us-east-1.localhost:4566/000000000000/jobs \
    --message-body '{"work":"item-1"}'

# Lambda is invoked asynchronously by the poller.
$ awsemu logs filter-log-events \
    --log-group-name /aws/lambda/hello \
    --query 'events[].message'

SQS, Kinesis, and DynamoDB Streams have dedicated pollers in services/lambda_/event_source_mapping/pollers/. Each runs in a background thread and respects the LAMBDA_EVENT_SOURCE_MAPPING_* backoff variables. Kafka (MSK and self-managed) accepts the same create-event-source-mapping call but dispatches via EventBridge Pipes under the hood.

Known limitations

Twenty of the 85 Lambda operations return NotImplementedException (501). Grouped by feature area:

Other behavioural caveats:

The exact set of operations and their status is in the Lambda coverage matrix.