Can I mix ARM64 and x64 Lambdas in a single AWS Serverless Application project?

Today, I spent some time trying to publish a .NET 6 AWS Serverless Application that consisted of:

  • A few serverless functions that I wanted to run using the default CPU architecture, x64 (or using AWS terminology, x86_64)
  • A few other serverless functions that I wanted to run on arm64 architecture (on Graviton2 CPUs)

I made sure that each a configuration for functions targeting arm64 differed by setting the following non-default property in the serverless.template file:

    // (...)
    "Properties": {
        "Runtime": "dotnet6",
        "MemorySize": 512,
        "Architectures": [
            "arm64"
        ],
        // (...)
    }
Code language: JSON / JSON with Comments (json)

A problem: Internal Server Error from arm64 lambdas

Unfortunately, after I published the application, I noticed that:

  • The functions that targeted the default x86_64 architecture worked flawlessly
  • The functions that targeted arm64 responded with Internal Server Error. Looking at the logs in CloudWatch I noticed the exception:
Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'Amazon.Lambda.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. An attempt was made to load a program with an incorrect format.Code language: JavaScript (javascript)

Such an error might suggest a mismatch between the runtime environment supported by the application and the actual runtime environment.

What happened when I targeted different architectures in a single project?

The hint came from the publish operation logs. I noticed the tooling used the following command to prepare the deployment package for the first of my functions (which was x64):

dotnet publish "D:\Projekty\Benchmark.AwsServerless\Benchmark.AwsServerless." --output "D:\Projekty\Benchmark.AwsServerless\Benchmark.AwsServerless.\bin\Release\net6.0\publish" --configuration "Release" --framework "net6.0" /p:GenerateRuntimeConfigurationFiles=true --runtime linux-x64 --self-contained False

What happened next was that the compiled artifacts got packed into a ZIP package and uploaded to AWS S3 storage. However, the same artifacts were set to be re-used by subsequent functions as well. Even those configured to target arm64! Hence the mismatch in runtime.

Source of confusion: the deployment tooling assumes that all the Lambdas within the application target the same CPU architecture as the first Lambda it encounters.
Source of confusion: the deployment tooling assumes that all the Lambdas within the application target the same CPU architecture as the first Lambda it encounters.

How I solved the issue

This looks like an assumption hard-coded in the tooling that architecture is the same for all Lambdas within the project.

If that’s the case, the easiest solution seems to just split the C# project into two separate projects, one containing only x64 Lambdas, and the other containing only ARM64 Lambdas. But the projects can still share a code via a class library.

When I did that, the deployment tooling properly auto-detected the linux-arm64 target in the arm64 project, and no other code changes were needed beside the one in the serverless.template file mentioned earlier.

Leave a Comment