Should you use Lambda containers?

Samuel Durand
PCG GmbH
Published in
5 min readJun 5, 2023

Assumptions

This article assumes that you have some knowledge about:

  • AWS Lambda functions
  • Docker

First impressions

A while back, when AWS announced Lambda support for containers, my first reaction was to dismiss it as something to be used only as a last resort, typically if you really wanted to use an unsupported language, like Rust or PHP. I also assumed that there would be some disavantages, like slower start time and some additional work to create your own container, but I didn’t look further into it.

It has now been 3 years since this feature was released and I decided it was time I revisited the subject, to see how much of my assumptions were true and if Containers for Lambda had other usages or limitations that I didn’t consider at the time.

So let’s get to it and see what we can learn!

Standard Lambda deployment

Let’s start with reviewing how a standard Lambda packaging and deployment normally works and what it allows us to do.

The usual steps are as follows:

  1. package your code, usually as a Zip file
  2. deploy your code, via one of the many tools available (SAM, Cloudformation, Serverless, Terraform…)
  3. configure some trigger to execute your Lambda function (SQS, API Gateway…)

Currently, the supported Runtimes are:

  • Node.js: 12 to 18
  • Python: 3.7 to 3.10
  • Java: 8, 11 or 17
  • Ruby: 2.7
  • Go
  • .NET 7 and .Net Core 3.1

Advantages

  • very little management overhead
  • offers Lambda Layers to provide common dependencies and extensions
  • no need for any Docker experience

Downsides

  • limited runtime choices
  • no control over what is eventually present alongside your executable (in the Firecracker MicroVm used by AWS)
  • limited package and dependencies size (50 Mb for the package, and 250Mb for Lambda + the Layers)

Deployment via Docker container

Why use it?

Before looking at what this type of deployment entails, let’s consider why you might want to complicate your life by using an alternative Lambda deployment method. After some investigation, I discovered to my surprise that there was more than one good reason to do so, although most of them are considered “edge cases”. Here are the most common ones:

  • Runtime: Unsupported runtime (PHP, Rust)
  • Runtime version: Unsupported runtime version, either more recent (Java 19…) or older for legacy code (Node.js 10)
  • Performance: You have a special container setup that allows your code to load and run much faster than the default Lambda (barebone Go image…)
  • Dependency size: Very large set of dependencies, for instance for Data Processing via Panda (it can easily exceed the maximum Lambda layer size of 250Mb)
  • Deployment package size: Very large deployment package (exceeds the maximum 50Mb)
  • Dataset size: You have a very large dataset used in every lambda
  • Legal: Some regulatory or legal requirements, which impose to limit Lambda to a simple orchestrator, and to control what goes into the containerNote that it is however possible to find solutions to compensate or mitigate some of those issues.

How does it work?

The only step that changes compared to a standard lambda deployment is the packaging step. But the change is drastic and adds a lot of complexity.

Here are the steps:

  1. package your code
  2. choose a base image among the following options (more details in the docs)
    – base image with runtimes included
    – base linux image with Lambda components but no runtime
    – any docker image (Debian, Alpine…) as long as they support the Lambda Runtime API
  3. write a Dockerfile using that image
  4. build your container
  5. push it to ECR
  6. provide the ECR image URL in the Lambda config
  7. deploy your code
  8. configure the execution triggers

As expected, the operational overhead when using a Docker image is a lot more significant. However, with complexity comes flexibility, since you can choose any OS base and Docker image, and even start from scratch (literally if you choose the Scratch Docker image).

Note that your container will be limited to 10GB in size, much more than what is allowed with standard Lambdas (250Mb for the layers + 50Mb for the code).

This operational overhead is not the only disadvantage when using containers for Lambdas.

What are the downsides?

Using containers instead of the standard Zip archive has several negative implications, which affect both the management and execution performances of Lambdas.

  • Operational overhead: As mentioned previously the build process and the maintenance of the Lambda will be much more complex than the standard process.
  • Cost: ECR is the only container registry allowed, and using it will result in additional storage costs (VPC transfer inside the same region is free).
  • Lambda Layers: Lambda Layers are not usable with containers, which also means that the potential cold start speed boost it can provide via caching will not apply.
  • Lambda Extensions: Lambda Extensions are a type of Lambda Layers and thus not usable either. This means you also cannot use AWS managed extensions such as the Parameters and Secrets extension.
  • Slow cold start: If you use a very large container, the cold start performance will be significantly impacted, just as large Zip files affect standard Lambda cold start.

For instance the slow cold start for large container can be improved by using one of the AWS amazon base images. This seems counter-intuitive since they are larger than most base Linux Docker images, but those images are cached pro-actively by the Lambda service, which means they will not need to be downloaded from ECR.

Consider alternatives

As we have seen, in some cases the container for Lambdas look like a viable and even a necessary option. But some of the advantages it provides can be obtained by other means that will not preclude the usage of the standard Lambda packaging and will avoid most of the operational overhead.

Runtime

If your main reason for using containers is to use a runtime not officially supported, then consider instead using a Lambda Layer, since it can be used to include a custom runtime. This can solve the problem without any of the previously described downsides.

Dataset

If your goal is instead to include a common dataset, then consider loading the dataset in S3 and downloading it from there (using an S3 VPC Gateway endpoint will remove the network fees at no extra cost).

Deployment package

If your problem is that your deployment package is too big (> 50Mb in zip form), consider uploading the overly large package to S3 and using it in your Lambda configuration.

Conclusion

Using containers can fit the bill for specific edge cases, but it will imply significant operational overhead and disadvantages. It is worth considering that the difficulties that bring us to consider this option can often be overcome by instead using the standard Lambda deployment strategy, combined with some simple techniques. It is best to make sure to consider all the alternatives, as well as to compare cost and maintenance complexity before settling on using this feature.

If you eventually decide that the extra complexity and downsides are worth it for your use case, make sure to test your containers to verify that they will function properly in the AWS Lambda environment via the Lambda RIE.

Thank you for reading, I hope this helped you make the best decision possible.

References

--

--

Samuel Durand
PCG GmbH
Writer for

AWS and JVM focused engineer, currently working at Kreuzwerker, an AWS Partner