Skip to content

Commit 48caa3d

Browse files
authored
feat: add Lambda SnapStart support (#790)
* chore: point lambda_http at SnapStart-enabled runtime branch * feat: add SnapStart hook-path environment variables * test: merge SnapStart env-var tests to avoid parallel race * refactor: extract build_client() and drop SnapStart pool special-case * feat: add swappable restored_client with write-once OnceLock * refactor: return BoxBody<Bytes, Error> from fetch_response * feat: reject external requests to SnapStart hook paths with 403 * feat: add SnapStartHooks bridging restore lifecycle to inner app * feat: register SnapStart resource in adapter run loop * docs: document SnapStart hook environment variables * docs(guide): add SnapStart feature page and env vars * docs: add fastapi-snapstart-zip example * docs: link fastapi-snapstart example from README * docs: drop /lwa prefix from SnapStart example hook paths * refactor: extract register_and_run helper to dedup run() arms * feat: add 60s timeout to SnapStart inner-app hooks * feat: re-run readiness check after SnapStart restore * docs: add fastapi-snapstart (OCI) example * refactor: move SnapStart hook env vars to SAM template in OCI example * chore: point OCI example at 1.1.0 SnapStart-enabled adapter image * chore: use public ECR adapter image in OCI SnapStart example * docs: correct after-restore step ordering in OCI example README * docs: correct after-restore step ordering in zip example README * docs: fix incorrect AWS_LAMBDA_INITIALIZATION_TYPE claim in snapstart guide * chore: use released lambda_http 1.3.0 with SnapStart support Switch from the git-branch dependency to the published lambda_http/lambda_runtime 1.3.0 from crates.io, removing the release blocker. Also fix the integration test body-reader helpers to accept the BoxBody response type, and resolve a clippy ok().expect() lint. * docs: add SnapStart to README features list * fix: reseed RNG in after-restore hook so restored envs get unique ids
1 parent 38e1e0b commit 48caa3d

26 files changed

Lines changed: 1797 additions & 90 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## Unreleased
2+
3+
### Features
4+
5+
- Add SnapStart support. The adapter notifies your web application at the SnapStart
6+
boundary via two opt-in HTTP hooks — `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH`
7+
(before checkpoint) and `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` (after restore) —
8+
so it can drain and re-establish connections. Each hook call is bounded by a
9+
60-second timeout. After restore the adapter refreshes its own HTTP client and
10+
re-runs the readiness check (bounded by 10 seconds) before admitting traffic, and
11+
it rejects external traffic to the hook paths with 403.
12+
13+
---
14+
115
## v1.0.1 - 2026-05-28
216

317
### Bug Fixes

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ http-body = "1.0.1"
2323
http-body-util = "0.1.0"
2424
hyper = { version = "1.5.2", features = ["client"] }
2525
hyper-util = "0.1.10"
26-
lambda_http = { version = "1.1.1", default-features = false, features = [
26+
lambda_http = { version = "1.3.0", default-features = false, features = [
2727
"apigw_http",
2828
"apigw_rest",
2929
"alb",

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ The same docker image can run on AWS Lambda, Amazon EC2, AWS Fargate, and local
1515
- Supports Amazon API Gateway Rest API and Http API endpoints, Lambda Function URLs, and Application Load Balancer
1616
- Supports Lambda managed runtimes, custom runtimes and docker OCI images
1717
- Supports Lambda Managed Instances for multi-concurrent request handling
18+
- Supports Lambda SnapStart with before-checkpoint and after-restore hooks
1819
- Supports any web frameworks and languages, no new code dependency to include
1920
- Automatic encode binary response
2021
- Enables graceful shutdown
@@ -66,6 +67,8 @@ The readiness check port/path and traffic port can be configured using environme
6667
| AWS_LWA_AUTHORIZATION_SOURCE | a header name to be replaced to `Authorization` | None |
6768
| AWS_LWA_ERROR_STATUS_CODES | HTTP status codes that will cause Lambda invocations to fail (e.g. "500,502-504") | None |
6869
| AWS_LWA_LAMBDA_RUNTIME_API_PROXY | overwrites `AWS_LAMBDA_RUNTIME_API` to allow proxying request | None |
70+
| AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH | inner-app path the adapter POSTs to before a SnapStart snapshot (drain resources) | None |
71+
| AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH | inner-app path the adapter POSTs to after a SnapStart restore (reconnect/reseed) | None |
6972

7073
> **Deprecation Notice:** The following non-namespaced environment variables are deprecated and will be removed in version 2.0:
7174
> `HOST`, `READINESS_CHECK_PORT`, `READINESS_CHECK_PATH`, `READINESS_CHECK_PROTOCOL`, `REMOVE_BASE_PATH`, `ASYNC_INIT`.
@@ -75,6 +78,36 @@ The readiness check port/path and traffic port can be configured using environme
7578
7679
👉 [Detailed configuration docs](https://aws.github.io/aws-lambda-web-adapter/configuration/environment-variables.html)
7780

81+
### SnapStart support
82+
83+
When your function uses [Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html),
84+
the adapter can notify your web application at the snapshot boundary so it can
85+
drain and re-establish state (database connections, cached DNS, PRNG seeds,
86+
unique identifiers). Both hooks are opt-in and independent.
87+
88+
| Variable | When the adapter calls it | Use it to |
89+
|---|---|---|
90+
| `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH` | Before the snapshot is taken | Drain/close resources that won't survive the snapshot |
91+
| `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` | After restore, before serving traffic | Reconnect, refresh credentials, reseed randomness, regenerate unique IDs |
92+
93+
Each hook is an empty `POST`; your application must respond with a `2xx` status.
94+
A non-`2xx` response, a connection failure, or taking longer than 60 seconds to
95+
respond fails the SnapStart phase (initialization for the before-checkpoint hook,
96+
restore for the after-restore hook) instead of serving traffic against an
97+
improperly prepared application.
98+
99+
After restore, the adapter also automatically refreshes its own HTTP connection
100+
to your application, so it never reuses a connection captured in the snapshot, and
101+
then re-runs the readiness check before admitting traffic. If the application does
102+
not report ready within 10 seconds of restore, the restore fails.
103+
104+
> These hook paths are control-plane operations. External requests (via API
105+
> Gateway or ALB) that target a configured hook path receive `403 Forbidden` and
106+
> are never forwarded to your application, so choose paths your normal traffic
107+
> does not use.
108+
109+
See the [FastAPI with SnapStart example](examples/fastapi-snapstart-zip) for a complete, deployable application.
110+
78111
## Examples
79112

80113
- [FastAPI](examples/fastapi)
@@ -84,6 +117,8 @@ The readiness check port/path and traffic port can be configured using environme
84117
- [FastAPI with Response Streaming in Zip](examples/fastapi-response-streaming-zip)
85118
- [FastAPI with Response Streaming on Lambda Managed Instances](examples/fastapi-response-streaming-lmi)
86119
- [FastAPI Response Streaming Backend with IAM Auth](examples/fastapi-backend-only-response-streaming/)
120+
- [FastAPI with SnapStart](examples/fastapi-snapstart)
121+
- [FastAPI with SnapStart in Zip](examples/fastapi-snapstart-zip)
87122
- [Flask](examples/flask)
88123
- [Flask in Zip](examples/flask-zip)
89124
- [Serverless Django](https://github.com/aws-hebrew-book/serverless-django) by [@efi-mk](https://github.com/efi-mk)

docs/guide/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- [Non-HTTP Event Triggers](./features/non-http-events.md)
2424
- [Multi-Tenancy](./features/multi-tenancy.md)
2525
- [Lambda Managed Instances](./features/managed-instances.md)
26+
- [SnapStart](./features/snapstart.md)
2627
- [Graceful Shutdown](./features/graceful-shutdown.md)
2728
- [Base Path Removal](./features/base-path-removal.md)
2829
- [Authorization Header](./features/authorization-header.md)

docs/guide/src/configuration/environment-variables.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ All configuration is done through environment variables, set either in your Dock
1919
| `AWS_LWA_AUTHORIZATION_SOURCE` | Header name to replace with `Authorization` | None |
2020
| `AWS_LWA_ERROR_STATUS_CODES` | HTTP status codes that cause Lambda invocation failure (e.g. `500,502-504`) | None |
2121
| `AWS_LWA_LAMBDA_RUNTIME_API_PROXY` | Proxy URL for Lambda Runtime API requests | None |
22+
| `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH` | Inner-app path the adapter POSTs to before a SnapStart snapshot | None |
23+
| `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` | Inner-app path the adapter POSTs to after a SnapStart restore | None |
2224

2325
## Deprecated Variables
2426

docs/guide/src/examples/overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The repository includes working examples for many popular web frameworks, packag
88
|---------|-----------|-----------|
99
| [FastAPI](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi) | Docker | No |
1010
| [FastAPI in Zip](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-zip) | Zip | No |
11+
| [FastAPI SnapStart](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-snapstart) | Docker | No |
12+
| [FastAPI SnapStart Zip](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-snapstart-zip) | Zip | No |
1113
| [FastAPI Background Tasks](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-background-tasks) | Docker | No |
1214
| [FastAPI Response Streaming](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-response-streaming) | Docker | Yes |
1315
| [FastAPI Response Streaming Zip](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-response-streaming-zip) | Zip | Yes |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# SnapStart
2+
3+
[Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) snapshots an initialized execution environment and restores it on later cold starts, reducing startup latency. Because the adapter runs your web application as a separate process, the application does not have direct access to the SnapStart lifecycle. The adapter bridges this gap with two optional HTTP hooks.
4+
5+
## Hooks
6+
7+
| Variable | When the adapter calls it | Use it to |
8+
|----------|---------------------------|-----------|
9+
| `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH` | Before the snapshot is taken | Drain or close resources that will not survive the snapshot |
10+
| `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` | After restore, before serving traffic | Reconnect, refresh credentials, reseed randomness, regenerate unique identifiers |
11+
12+
Both hooks are opt-in and independent — each fires only when its variable is set.
13+
14+
## How it works
15+
16+
The adapter always registers for the SnapStart lifecycle; the Lambda runtime invokes the hooks only when your function runs under SnapStart. When it does, the adapter participates as follows:
17+
18+
1. **Before checkpoint** — if `AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH` is set, the adapter sends an empty `POST` to that path on your application, then signals Lambda that it is ready for the snapshot.
19+
2. **After restore** — Lambda restores the environment. The adapter first refreshes its own HTTP connection to your application (so it never reuses a connection captured in the snapshot); then, if `AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH` is set, sends an empty `POST` to that path; and finally re-runs the readiness check before admitting traffic.
20+
21+
Each hook is an empty `POST`, and your application must respond with a `2xx` status. A non-`2xx` response, a connection failure, or taking longer than 60 seconds to respond fails the SnapStart phase — initialization for the before-checkpoint hook, restore for the after-restore hook — rather than serving traffic against an improperly prepared application. The final readiness check runs on every restore (whether or not an after-restore path is configured); if your application does not report ready within 10 seconds of restore, the restore fails.
22+
23+
## Why you need the hooks
24+
25+
State captured in a snapshot is shared across every restored environment. Two classes of problem follow:
26+
27+
- **Stale connections.** Database connections, cached DNS, and keep-alive HTTP connections captured in the snapshot are dead by the time the environment is restored. Close them in the before-checkpoint hook and re-establish them in the after-restore hook.
28+
- **Uniqueness and entropy.** Values seeded once at initialization — random number generators, UUID seeds, security tokens — become identical across every restored environment. Reseed them in the after-restore hook.
29+
30+
## Securing the hook paths
31+
32+
The hook paths are control-plane operations. External requests (via API Gateway or ALB) that target a configured hook path receive `403 Forbidden` and are never forwarded to your application. The guard matches the exact configured path, so choose paths your normal application traffic does not use (for example, `/snapstart/before` and `/snapstart/after`).
33+
34+
## Example
35+
36+
```python
37+
from fastapi import FastAPI, Response
38+
39+
app = FastAPI()
40+
pool = None # your database/connection pool
41+
42+
43+
@app.post("/snapstart/before")
44+
async def before_checkpoint():
45+
# Close resources that won't survive the snapshot.
46+
if pool is not None:
47+
await pool.close()
48+
return Response(status_code=200)
49+
50+
51+
@app.post("/snapstart/after")
52+
async def after_restore():
53+
# Re-establish resources and reseed anything that must be unique.
54+
global pool
55+
pool = await create_pool()
56+
return Response(status_code=200)
57+
```
58+
59+
Configure the function with:
60+
61+
```
62+
AWS_LWA_SNAPSTART_BEFORE_CHECKPOINT_PATH=/snapstart/before
63+
AWS_LWA_SNAPSTART_AFTER_RESTORE_PATH=/snapstart/after
64+
```
65+
66+
See the [fastapi-snapstart-zip example](https://github.com/aws/aws-lambda-web-adapter/tree/main/examples/fastapi-snapstart-zip) for a complete, deployable application.

0 commit comments

Comments
 (0)