Mayfly implements the Lambda Runtime API (2018-06-01) so an Elixir release runs on the provided.al2023 runtime.

Lifecycle

  1. Lambda executes bootstrap (generated by Mayfly.Release.bootstrap/1). For layer builds it first verifies that /opt/erlang/bin/erl exists and that the layer's ERTS version equals releases/start_erl.data, then runs bin/<release> eval "Mayfly.Boot.main()".
  2. Mayfly.Boot configures the Logger from AWS_LAMBDA_LOG_FORMAT / AWS_LAMBDA_LOG_LEVEL, starts every application in the release, then calls Mayfly.start_link/0.
  3. Mayfly.Supervisor resolves _HANDLER once and runs the handler's init/1. On failure it posts to /runtime/init/error and the VM exits with status 1 – Lambda marks the function as failed.
  4. It starts AWS_LAMBDA_MAX_CONCURRENCY (default 1) Mayfly.Poller processes. Each poller long-polls /runtime/invocation/next, decodes the event, builds a Mayfly.Context, calls handle/3 and posts the result (/response, buffered or streamed) or the error (/error), then polls again.

Nothing in this chain runs unless bootstrap (or your code) starts it: mix test and iex -S mix in a project depending on Mayfly are unaffected.

Modules

ModuleRole
Mayflystart_link/1 – the public entry point
Mayfly.Bootmain/0 used by bootstrap; logger setup, app start, exit codes
Mayfly.Supervisorresolves + inits the handler, supervises pollers
Mayfly.Pollerone concurrency slot: poll → invoke → respond, with backoff
Mayfly.Handlerbehaviour, _HANDLER resolution (module or legacy MFA), invocation with rescue/catch
Mayfly.Contextper-invocation metadata from headers
Mayfly.Responsebuffered/streamed responses, Function URL prelude
Mayfly.ErrorPayloaderror documents, header type normalisation, X-Ray cause
Mayfly.RuntimeAPIthe four API calls; a behaviour so tests can fake it
Mayfly.HTTPHTTP/1.1 over :gen_tcp: content-length, chunked (both directions), trailers
Mayfly.Telemetryoptional :telemetry events
Mayfly.LogFormatterJSON lines for advanced logging controls
Mayfly.Releaserelease steps: prepare, bootstrap, zip
Mayfly.LocalRuntimeRuntime API emulator for mix lambda.invoke and tests
Mix.Tasks.Lambda.Build / .InvokeDocker build orchestration; local invoke

Why no :httpc

:httpc needs :inets and :ssl at boot (slower cold start), returns charlists (8–16× memory for large payloads), honours proxy settings the link-local Runtime API must never see, and cannot send chunked request bodies with trailers – which response streaming requires. A 250-line :gen_tcp client covers exactly what the Runtime API needs.

Concurrency model

One BEAM process per concurrency slot; no shared mutable state in the runtime. The only shared value is the handler state from init/1, passed immutably to every handle/3. On standard Lambda there is one slot; on Managed Instances there are AWS_LAMBDA_MAX_CONCURRENCY. A poller dies only on a Runtime API container error (HTTP 500), which terminates the supervisor and, via Mayfly.Boot, the VM – as the Runtime API contract demands.

Error contract

WhereerrorTypeHeader
Handler exceptionexception moduleFunction.<Module>
{:error, term}HandlerError or the map's errorTypeFunction.<Type>
exit / throwExit / ThrowFunction.Exit / Function.Throw
Bad return / unencodableRuntime.InvalidResponsesame
Bad JSON eventRuntime.InvalidEventsame
Bad _HANDLERRuntime.NoSuchHandler (init)same
init/1 failureRuntime.InitError (init)same
Error after streaming startedtrailers Lambda-Runtime-Function-Error-Type/-Body–

Lambda-Runtime-Function-Error-Type must look like <Category.Reason> or Lambda normalises it to Runtime.Unknown/Function.Unknown; ErrorPayload.header_type/1 guarantees the shape. Lambda-Runtime-Invocation-Id is echoed on every /response and /error call.

Environment variables

VariableSet byUse
AWS_LAMBDA_RUNTIME_APILambdaRuntime API host:port
_HANDLERLambda Handler setting, default from bootstrapModule or Module.function
AWS_LAMBDA_MAX_CONCURRENCYLambda (Managed Instances)number of pollers; also switches bootstrap from +S 1:1 to all vCPUs
AWS_LAMBDA_LOG_FORMAT, AWS_LAMBDA_LOG_LEVELLambda logging configJSON formatter, level
LOGLEVELyouoverrides the level
_X_AMZN_TRACE_IDMayfly, per invocationX-Ray
RELEASE_TMP, RELEASE_DISTRIBUTIONbootstrap/tmp, none
MAYFLY_ERTSyou (layer builds)ERTS location, default /opt/erlang