# aws-lambda-rust-runtime: writing AWS Lambda functions in Rust

> The AWS-maintained workspace that gives Rust a Lambda runtime, HTTP adapters and typed event structs. It is a good fit for compiled handlers with strict error reporting, and a poor fit if you want to avoid the Cargo Lambda toolchain.

**aws/aws-lambda-rust-runtime** — A Rust runtime for AWS Lambda

- Repository: https://github.com/aws/aws-lambda-rust-runtime
- Stars: 3,615 · Forks: 398
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/aws-aws-lambda-rust-runtime

## What aws-lambda-rust-runtime actually removes from your handler

A Lambda function is a process that polls the Lambda Runtime API for the next invocation, runs a handler, and posts the result back. Writing that loop by hand in Rust means handling the runtime API endpoints, serialising responses, and turning Rust errors into the JSON error shape Lambda expects. The lambda-runtime crate supplies the loop. The README describes it as "a library that provides a Lambda runtime for applications written in Rust", and the workspace splits the rest of the job across sibling crates: lambda-http for HTTP-shaped functions behind API Gateway, ALB, Lambda Function URLs and VPC Lattice, lambda-extension for Runtime Extensions, lambda-events for strongly typed event structs, and lambda-runtime-api-client as the shared client for the Runtime API.

The audience is narrow and specific. You need Rust as the implementation language, a build step that produces a Linux binary, and a reason to prefer that over a managed runtime. The payoff is the type system at the boundary: the handler receives a typed event and returns a typed value, and the runtime owns the translation to and from Lambda's wire format. If your function is a thin wrapper over a few AWS SDK calls, that payoff is small and the build pipeline is pure overhead.

## The service_fn handler and the Diagnostic error contract

The core abstraction is a function wrapped by service_fn and passed to lambda_runtime::run, which drives the Runtime API loop. The README's example builds the handler from an async function that takes a LambdaEvent<Value> and returns a Result<Value, Error>, then calls into_parts() to split the event from the Lambda context.

Errors are where the design gets opinionated. Lambda expects a JSON object with error_type and error_message, and the runtime represents that internally with a struct called Diagnostic. The default conversion from general error types produces an error_type taken from the Rust type name, and the README is blunt about the result: if your handler returns lambda_runtime::Error, the error_type becomes something like alloc::boxed::Box<dyn core::error::Error + core::marker::Send + core::marker::Sync>. That string ends up in your CloudWatch logs and in any downstream error handling. Implementing From<YourError> for Diagnostic lets you set error_type to a stable name such as MyErrorType, and the README points at the thiserror example under examples/basic-error-thiserror for that pattern.

For the popular error crates the runtime ships feature flags instead. Enabling anyhow, eyre or miette on the lambda_runtime dependency adds a From<T> for Diagnostic implementation for that crate's error type, so an anyhow::Error can be converted with .into(). That is a real convenience, but it also means the descriptive error_type problem comes back unless you convert deliberately. The flag gives you a working conversion, not a good name.

## Installing the toolchain and running a first function

The README does not treat the crates as the entry point. It points at Cargo Lambda, described as a related Cargo plugin that provides commands for Rust on Lambda, and says the preferred way to install it is a package manager. On macOS that is Homebrew, and on Windows it is Scoop:

```bash
brew tap cargo-lambda/cargo-lambda
brew install cargo-lambda
```

```bash
scoop bucket add cargo-lambda https://github.com/cargo-lambda/scoop-cargo-lambda
scoop install cargo-lambda/cargo-lambda
```

If you have Python 3, pip3 works on any system, and the README also shows uv as an alternative way to install the same pip package as an executable:

```bash
pip3 install cargo-lambda
```

```bash
uv tool install cargo-lambda
```

With the plugin installed, the README's first step is the new subcommand, which generates a Rust package containing initial source code for your function:

```bash
cargo lambda new YOUR_FUNCTION_NAME
```

If you would rather write the package by hand, the README gives a complete handler. It reads a firstName field from the event and returns a greeting, defaulting to "world" when the field is missing:

```rust
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Error> {
    let func = service_fn(func);
    lambda_runtime::run(func).await?;
    Ok(())
}

async fn func(event: LambdaEvent<Value>) -> Result<Value, Error> {
    let (event, _context) = event.into_parts();
    let first_name = event["firstName"].as_str().unwrap_or("world");

    Ok(json!({ "message": format!("Hello, {}!", first_name) }))
}
```

Two things to notice in that snippet. The handler returns Error, which is exactly the case the README warns produces an unreadable error_type, so treat it as a starting point rather than a pattern to keep. And the runtime is driven by tokio, since the entry point is annotated with #[tokio::main].

## Graceful shutdown is feature-gated, Unix-only and opinionated

The runtime offers spawn_graceful_shutdown_handler(), a helper that takes a FnOnce closure returning an async block, executed when the function receives SIGTERM or SIGKILL. Two constraints are stated plainly: it requires the graceful-shutdown feature flag, and it only supports Unix systems. The README lists three ways the helper is opinionated, starting with the fact that it spawns a task to drive your signal handlers and registers a no-op extension in order to enable graceful shutdown signals.

The third item in that list is cut off in the README at "it panics on u", so the exact condition is not visible here and should be read from the crate documentation before you rely on the helper in production. The SIGKILL detail is also worth pausing on: SIGKILL cannot be caught by a process on Unix, so a handler that plans around it is planning around something it will not receive. Whether the README means the signal that Lambda actually delivers, or is using the name loosely, is not something the text resolves.

The no-op extension is the part with real cost. Runtime Extensions run as separate processes alongside your function, and registering one changes the shutdown path for the whole execution environment. If you already run an extension, adding another to get signal handling is a decision you should make deliberately rather than by turning on a feature flag.

## Where this runtime is the wrong choice

The build pipeline is the first obstacle. Your handler has to be compiled for Lambda's Linux environment, and the integration tests in the repository lean on cargo zigbuild with the target x86_64-unknown-linux-musl, plus SAM for deployment and a Docker-based test runner. That is a heavier CI surface than uploading a zip of interpreted code. If your team does not already build Rust artifacts for Linux, the runtime is not the part you will spend time on.

Cold start is the second. The related searches around this project include AWS Lambda Rust cold start, and the honest position is that a compiled binary does not make cold start disappear. The runtime still initialises a tokio runtime and an HTTP client to talk to the Runtime API. The repository does include examples/basic-snapstart, which is the mechanism Lambda provides for this problem, but the README does not document the trade-offs of using it with this runtime.

The third case is architectural. If your function is a small amount of glue between an event and an AWS SDK call, the type safety this runtime buys you is thin, and the toolchain and build time are real. Rust on Lambda pays off when the handler contains logic you want the compiler to check and when the error_type you emit matters to whoever reads the logs.

## How it differs from writing the Runtime API loop yourself

The obvious alternative is not another language runtime but the lower-level path: calling the Lambda Runtime API directly from Rust, or using lambda-runtime-api-client on its own. That crate is described as the shared client between the runtime and extension libraries, and it is the piece that talks to the Runtime API. Building on it directly gives you control over the invocation loop, the response serialisation and the error JSON, and it removes the service_fn abstraction.

What you give up is the Diagnostic conversion layer. The runtime's value is that it already implements conversions from general error types, and that it offers the anyhow, eyre and miette features so those crates' errors become Diagnostics without hand-written glue. Reproducing that yourself is not hard, but it is code you now own and test. The same argument applies to lambda-http: it handles the request and response shapes for API Gateway, ALB, Function URLs and VPC Lattice, and writing those adapters by hand is exactly the kind of work that looks trivial until an event shape changes.

If your reason for considering this project is that you want to run Rust in a container image rather than on a managed runtime, note that the repository ships a Dockerfile.rie and a Dockerfile.test, so container-based local testing is part of the project's own workflow. That is a different decision from choosing the runtime library, and the README does not present it as a deployment recommendation.

## Releases, licence and what an upgrade costs

The workspace is licensed Apache-2.0, with a NOTICE file at the repository root. Apache-2.0 includes an explicit patent grant and requires that NOTICE and attribution be preserved when you redistribute, which matters if you vendor the crates rather than pulling them from crates.io. That is a description of the licence text, not legal advice; your own review is the right place for the rest.

The crates are versioned separately. The recent releases listed are lambda_runtime-v1.4.0 on 2026-09-02, lambda_runtime-v1.3.0 on 2026-08-31 and lambda_runtime-v1.2.1 on 2026-05-25, and the repository uses release-plz.toml, which indicates automated release management per crate. The practical consequence is that lambda-runtime, lambda-http, lambda-events and lambda-extension do not move in lockstep, so a version bump in one does not imply a matching bump in the others. The workspace pins shared dependencies centrally in Cargo.toml, including hyper 1.0, tower 0.5 and http 1.0, so a major bump in one of those is a workspace-wide change rather than a per-crate one.

The last push to the default branch was on 2026-09-20, and the repository is not archived. The Makefile's pr-check target runs cargo +1.54.0 check --all, cargo +stable fmt --all -- --check, clippy, and tests under both 1.54.0 and stable. That is a wide compiler range to keep passing, and it is the main cost driver for contributors: any change has to compile on an old pinned toolchain as well as current stable.

## Conclusion

Adopt it if you already compile Rust and want a handler model that maps cleanly onto Lambda's error JSON, with lambda-http and lambda-events covering the API Gateway and Function URL cases. Do not adopt it if you are unwilling to add Cargo Lambda to CI or if your handler is mostly glue code around AWS SDK calls, where a managed runtime costs you less. Before committing, read the graceful-shutdown section in the README in full, because it is truncated mid-sentence at the point where it lists the helper's opinionated behaviours, and check whether the SIGKILL claim matches what you expect on your target platform.

## FAQ

### What is the best runtime for AWS Lambda?

There is no single answer, and this project does not make that claim. aws-lambda-rust-runtime is the AWS-maintained option for Rust handlers, and the README positions Cargo Lambda as the easiest way to start writing Lambda functions with Rust.

### How long can AWS lambdas run for?

The README does not state a maximum invocation duration. It does document graceful shutdown handling for when the execution environment is being torn down, which is the point at which a long-running handler is cut off.

### What runtimes are supported by AWS Lambda?

The README does not enumerate Lambda's supported runtimes. It describes this project's own crates, and notes that lambda-http supports API Gateway, ALB, Lambda Function URLs and VPC Lattice as upstream event sources.

### Can Lambda run a Docker image?

The repository does not answer this in the README. It does ship Dockerfile.rie and Dockerfile.test at the root, used for the project's own local and integration testing rather than presented as a deployment guide.

## Sources

- [aws/aws-lambda-rust-runtime on GitHub](https://github.com/aws/aws-lambda-rust-runtime)
- [Issues](https://github.com/aws/aws-lambda-rust-runtime/issues)
- [License: Apache-2.0](https://github.com/aws/aws-lambda-rust-runtime/blob/main/LICENSE)
- [README](https://github.com/aws/aws-lambda-rust-runtime/blob/main/README.md)
- [Releases](https://github.com/aws/aws-lambda-rust-runtime/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aws-aws-lambda-rust-runtime
