Native AOT or ReadyToRun for a .NET 8 Lambda with reflection-heavy dependencies?
0 reputation · 07 Nov 2023, 00:40 UTC
0 reputation · 07 Nov 2023, 00:40 UTC
I'm deploying a .NET 8 ASP.NET Core Minimal API as an AWS Lambda behind scale-to-zero traffic, so cold-start latency is a hard SLO (under ~300ms). Microsoft documents both Native AOT (PublishAot=true) and standard self-contained publishing with ReadyToRun (PublishReadyToRun=true) as supported, but doesn't declare a default for this scenario.
Native AOT promises cold starts in the tens of milliseconds and a smaller memory footprint, but forbids dynamic code generation and requires reflection-used types to be annotated or preserved. My service currently depends on a few third-party packages whose AOT compatibility is unclear, and JSON serialization would need to move to System.Text.Json source generation. ReadyToRun keeps the full reflection ecosystem working but typically adds several hundred milliseconds of cold start and a much larger binary.
I can audit trimming warnings (IL2026, IL3050) at build time, but I understand missing annotations can still surface as runtime exceptions only in the AOT binary, which makes me hesitant to commit without knowing the real risk surface.
Given a hard cold-start SLO and an unverified dependency graph, which path should I choose? Is the hybrid approach (thin AOT entry point delegating to a standard self-contained library) a practical middle ground, or does it just add deployment complexity? And how do teams validate that trimming warnings actually cover all runtime reflection paths before shipping AOT to production?
31775 reputation · 07 Nov 2023, 02:00 UTC
If you can verify that all reflection-used types in your dependencies are preserved (via ILLinker descriptors, source generation, or explicit [Preserve] attributes), choose Native AOT (PublishAot=true). It will keep cold-start latency well under the 300 ms SLO and reduce memory usage. If any dependency cannot be made AOT-compatible without significant effort, fall back to ReadyToRun (PublishReadyToRun=true) and accept the higher cold-start cost, or isolate the reflection-heavy code in a separate layer.
MissingMetadataException) and is not caught by compile-time warnings alone.PublishAot=true and PublishTrimmed=true and capture ILLinker warnings (IL2026, IL3050). Treat any warning as a blocker until you can add a preservation descriptor or confirm the library is AOT-safe.dotnet publish -c Release /p:PublishAot=true /p:PublishTrimmed=true
dotnet lambda test-server --framework net8.0 --port 9000
# invoke your endpoint and measure start-up time (e.g., with curl or hey)PublishReadyToRun=true (no trimming) to obtain a baseline cold-start latency and package size.To finalize the recommendation, I need to know whether each of your reflection-heavy third-party packages provides ILLinker descriptors, source-generation support, or explicit AOT compatibility documentation. If any lack this, the AOT path may require shims or forks, which could change the recommendation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.