AWS Compute Blog
Introducing the .NET 8 runtime for AWS Lambda
This post is written by Beau Gosse, Senior Software Engineer and Paras Jain, Senior Technical Account Manager.
AWS Lambda now supports .NET 8 as both a managed runtime and container base image. With this release, Lambda developers can benefit from .NET 8 features including API enhancements, improved Native Ahead of Time (Native AOT) support, and improved performance. .NET 8 supports C# 12, F# 8, and PowerShell 7.4. You can develop Lambda functions in .NET 8 using the AWS Toolkit for Visual Studio, the AWS Extensions for .NET CLI, AWS Serverless Application Model (AWS SAM), AWS CDK, and other infrastructure as code tools.
What’s new
Upgraded operating system
The .NET 8 runtime is built on the Amazon Linux 2023 (AL2023) minimal container image. This provides a smaller deployment footprint than earlier Amazon Linux 2 (AL2) based runtimes and updated versions of common libraries such as glibc 2.34 and OpenSSL 3.
The new image also uses microdnf
as a package manager, symlinked as dnf
. This replaces the yum
package manager used in earlier AL2-based images. If you deploy your Lambda functions as container images, you must update your Dockerfiles to use dnf
instead of yum
when upgrading to the .NET 8 base image. For more information, see Introducing the Amazon Linux 2023 runtime for AWS Lambda.
Performance
There are a number of language performance improvements available as part of .NET 8. Initialization time can impact performance, as Lambda creates new execution environments to scale your function automatically. There are a number of ways to optimize performance for Lambda-based .NET workloads, including using source generators in System.Text.Json
or using Native AOT.
Lambda has increased the default memory size from 256 MB to 512 MB in the blueprints and templates for improved performance with .NET 8. Perform your own functional and performance tests on your .NET 8 applications. You can use AWS Compute Optimizer or AWS Lambda Power Tuning for performance profiling.
At launch, new Lambda runtimes receive less usage than existing established runtimes. This can result in longer cold start times due to reduced cache residency within internal Lambda subsystems. Cold start times typically improve in the weeks following launch as usage increases. As a result, AWS recommends not drawing performance comparison conclusions with other Lambda runtimes until the performance has stabilized.
Native AOT
Lambda introduced .NET Native AOT support in November 2022. Benchmarks show up to 86% improvement in cold start times by eliminating the JIT compilation. Deploying .NET 8 Native AOT functions using the managed dotnet8
runtime rather than the OS-only provided.al2023
runtime gives your function access to .NET system libraries. For example, libicu
, which is used for globalization, is not included by default in the provided.al2023
runtime but is in the dotnet8
runtime.
While Native AOT is not suitable for all .NET functions, .NET 8 has improved trimming support. This allows you to more easily run ASP.NET APIs. Improved trimming support helps eliminate build time trimming warnings, which highlight possible runtime errors. This can give you confidence that your Native AOT function behaves like a JIT-compiled function. Trimming support has been added to the Lambda runtime libraries, AWS .NET SDK, .NET Lambda Annotations, and .NET 8 itself.
Using.NET 8 with Lambda
To use .NET 8 with Lambda, you must update your tools.
- Install or update the .NET 8 SDK.
- If you are using AWS SAM, install or update to the latest version.
- If you are using Visual Studio, install or update the AWS Toolkit for Visual Studio.
- If you use the .NET Lambda Global Tools extension (
Amazon.Lambda.Tools
), install the CLI extension and templates. You can upgrade existing tools withdotnet tool update -g Amazon.Lambda.Tools
and existing templates withdotnet new install Amazon.Lambda.Templates
.
You can also use .NET 8 with Powertools for AWS Lambda (.NET), a developer toolkit to implement serverless best practices such as observability, batch processing, retrieving parameters, idempotency, and feature flags.
Building new .NET 8 functions
Using AWS SAM
- Run
sam init
. - Choose 1- AWS Quick Start Templates.
- Choose one of the available templates such as Hello World Example.
- Select N for Use the most popular runtime and package type?
- Select
dotnet8
as the runtime. Thedotnet8
Hello World Example also includes a Native AOT template option. - Follow the rest of the prompts to create the .NET 8 function.
You can amend the generated function code and use sam deploy --guided
to deploy the function.
Using AWS Toolkit for Visual Studio
- From the Create a new project wizard, filter the templates to either the Lambda or Serverless project type and select a template. Use Lambda for deploying a single function. Use Serverless for deploying a collection of functions using AWS CloudFormation.
- Continue with the steps to finish creating your project.
- You can amend the generated function code.
- To deploy, right click on the project in the Solution Explorer and select Publish to AWS Lambda.
Using AWS extensions for the .NET CLI
- Run
dotnet new list --tag Lambda
to get a list of available Lambda templates. - Choose a template and run
dotnet new <template name>
. To build a function using Native AOT, usedotnet new lambda.NativeAOT
ordotnet new serverless.NativeAOT
when using the .NET Lambda Annotations Framework. - Locate the generated Lambda function in the directory under
src
which contains the .csproj file. You can amend the generated function code. - To deploy, run
dotnet lambda deploy-function
and follow the prompts. - You can test the function in the cloud using
dotnet lambda invoke-function
or by using the test functionality in the Lambda console.
You can build and deploy .NET Lambda functions using container images. Follow the instructions in the documentation.
Migrating from .NET 6 to .NET 8 without Native AOT
Using AWS SAM
- Open the
template.yaml
file. - Update Runtime to
dotnet8
. - Open a terminal window and rebuild the code using
sam build
. - Run
sam deploy
to deploy the changes.
Using AWS Toolkit for Visual Studio
- Open the .csproj project file and update the
TargetFramework
tonet8.0
. Update NuGet packages for your Lambda functions to the latest version to pull in .NET 8 updates. - Verify that the build command you are using is targeting the .NET 8 runtime.
- There may be additional steps depending on what build/deploy tool you’re using. Updating the function runtime may be sufficient.
Using AWS extensions for the .NET CLI or AWS Toolkit for Visual Studio
- Open the
aws-lambda-tools-defaults.json
file if it exists.- Set the framework field to
net8.0
. If unspecified, the value is inferred from the project file. - Set the function-runtime field to
dotnet8
.
- Set the framework field to
- Open the
serverless.template
file if it exists. For anyAWS::Lambda::Function
orAWS::Servereless::Function
resources, set the Runtime property todotnet8
. - Open the .csproj project file if it exists and update the TargetFramework to net8.0. Update NuGet packages for your Lambda functions to the latest version to pull in .NET 8 updates.
Migrating from .NET 6 to .NET 8 Native AOT
The following example migrates a .NET 6 class library function to a .NET 8 Native AOT executable function. This uses the optional Lambda Annotations framework which provides idiomatic .NET coding patterns.
Update your project file
- Open the project file.
- Set
TargetFramework
tonet8.0
. - Set
OutputType
toexe
. - Remove
PublishReadyToRun
if it exists. - Add
PublishAot
and set totrue
. - Add or update NuGet package references to
Amazon.Lambda.Annotations
andAmazon.Lambda.RuntimeSupport
. You can update using the NuGet UI in your IDE, manually, or by running dotnet add package Amazon.Lambda.RuntimeSupport anddotnet add package Amazon.Lambda.Annotations
from your project directory.
Your project file should look similar to the following:
Updating your function code
-
- Reference the annotations library with
using Amazon.Lambda.Annotations;
- Add
[assembly:LambdaGlobalProperties(GenerateMain = true)]
to allow the annotations framework to create the main method. This is required as the project is now an executable instead of a library. - Add the below partial class and include a
JsonSerializable
attribute for any types that you need to serialize, including for your function input and output This partial class is used at build time to generate reflection free code dedicated to serializing the listed types. The following is an example: - After the using statement, add the following to specify the serializer to use.
[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>))]
Swap
LambdaFunctionJsonSerializerContext
for your context if you are not using the partial class from the previous step.Updating your function configuration
If you are using aws-lambda-tools-defaults.json.
- Set
function-runtime
todotnet8
. - Set
function-architecture
to match your build machine – eitherx86_64
orarm64
. - Set (or update)
environment-variables
to includeANNOTATIONS_HANDLER=<YourFunctionHandler>
. Replace<YourFunctionHandler>
with the method name of your function handler, so the annotations framework knows which method to call from the generated main method. - Set
function-handler
to the name of the executable assembly in your bin directory. By default, this is your project name, which tells the .NET Lambda bootstrap script to run your native binary instead of starting the .NET runtime. If your project file has AssemblyName then use that value for the function handler.
Deploy and test
- Deploy your function. If you are using
Amazon.Lambda.Tools
, rundotnet lambda deploy-function
. Check for trim warnings during build and refactor to eliminate them. - Test your function to ensure that the native calls into AL2023 are working correctly. By default, running local unit tests on your development machine won’t run natively and will still use the JIT compiler. Running with the JIT compiler does not allow you to catch native AOT specific runtime errors.
Conclusion
Lambda is introducing the new .NET 8 managed runtime. This post highlights new features in .NET 8. You can create new Lambda functions or migrate existing functions to .NET 8 or .NET 8 Native AOT.
For more information, see the AWS Lambda for .NET repository, documentation, and .NET on Serverless Land.
For more serverless learning resources, visit Serverless Land.
- Reference the annotations library with