Bite-Sized Serverless

Lambda Icon

When is the Lambda Init Phase Free, and when is it Billed?

Lambda - Intermediate (200)
It's an open secret that the 'init' phase in AWS Lambda is free. This fact is not included in the AWS documentation, FAQ, or pricing pages, yet the Lambda Function logs clearly show the 'Init Duration' is not included in the 'Billed Duration'. At least, most of the time. This Bite will demystify when the AWS Lambda init duration is free, and when it is billed.
The Lambda Service creates execution environments for your Lambda Functions. These micro-VMs boot up in milliseconds, then execute your Lambda Function code. When the call has completed, the execution environment is kept around by the Lambda Service so it can handle another call. A Lambda Function consists of handler code (executed for every invocation of the Lambda Function) and initialization code (executed when a new execution environment is created). The initialization code is commonly used to import libraries and initialize reusable classes, such as AWS Service Clients. For more details, see the Bite Lambda Cold Starts and Bootstrap Code.
Many people who regularly use AWS Lambda know that the initialization phase is both free and gets two unthrottled vCPUs, even at the lowest memory configurations. This leads to design patterns where as much reusable code as possible is moved outside the handler. But there are several cases where the initialization phase is not free.
To conclusively determine which Lambda Functions get a free init phase and which don't, we have created a CDK project (GitHub) that deploys many different Lambda configurations and measures if the init phase is free. Long story short, you can find the results below.
Package TypeRuntime / ImageArchitectureInit ConfigInit DurationDurationBilled DurationFree Init
Imagepublic.ecr.aws/lambda/nodejs:12x86Sleep 1 Sec1293.83 ms1049.63 ms2344 ms🚫
Imagepublic.ecr.aws/lambda/nodejs:14x86Sleep 1 Sec1307.68 ms1058.15 ms2366 ms🚫
Imagepublic.ecr.aws/lambda/python:3.7x86Sleep 1 Sec1258.24 ms1002.25 ms2261 ms🚫
Imagepublic.ecr.aws/lambda/python:3.7x86Sleep 10 Sec-12079.53 ms12080 ms🚫
Imagepublic.ecr.aws/lambda/python:3.8x86Sleep 1 Sec1285.01 ms1002.07 ms2288 ms🚫
Imagepublic.ecr.aws/lambda/python:3.9x86Sleep 1 Sec1148.21 ms1001.62 ms2150 ms🚫
Imagepublic.ecr.aws/lambda/python:3.9-arm64arm64Sleep 1 Sec1147.77 ms1001.65 ms2150 ms🚫
Imagepython:3.9-alpine3.12x86Sleep 1 Sec1684.10 ms1002.00 ms2687 ms🚫
ZipNODEJS_12_Xx86Sleep 1 Sec1152.58 ms1031.01 ms1032 ms
ZipNODEJS_12_Xarm64Sleep 1 Sec1144.01 ms1019.79 ms1020 ms
ZipNODEJS_14_Xx86Sleep 1 Sec1273.41 ms1077.13 ms1078 ms
ZipNODEJS_14_Xarm64Sleep 1 Sec1156.92 ms1011.18 ms1012 ms
ZipPYTHON_3_7x86Sleep 1 Sec1125.03 ms1003.04 ms1004 ms
ZipPYTHON_3_8x86Sleep 1 Sec1189.79 ms1001.90 ms1002 ms
ZipPYTHON_3_8arm64Sleep 1 Sec1108.83 ms1001.59 ms1002 ms
ZipPYTHON_3_9x86Sleep 1 Sec1101.84 ms1002.54 ms1003 ms
ZipPYTHON_3_9arm64Sleep 1 Sec1096.78 ms1001.86 ms1002 ms
ZipRUBY_2_7x86Sleep 1 Sec1142.84 ms1003.05 ms1004 ms
ZipRUBY_2_7arm64Sleep 1 Sec1139.89 ms1003.10 ms1004 ms
ZipGO_1_Xx86Sleep 1 Sec1083.49 ms1002.60 ms1003 ms
ZipGo on PROVIDED_AL2x86Sleep 1 Sec1064.65 ms1001.72 ms2067 ms🚫
ZipGo on PROVIDED_AL2arm64Sleep 1 Sec1049.58 ms1002.49 ms2053 ms🚫
ZipRust on PROVIDED_AL2x86Sleep 1 Sec1023.89 ms1002.13 ms2027 ms🚫
ZipPYTHON_3_9 with internal extensionx86Extension 1 Sec, Init 1 Sec2124.46 ms1001.82 ms1002 ms
ZipPYTHON_3_9 with external extensionx86Extension 1 Sec, Init 1 Sec2509.05 ms1002.16 ms1003 ms
ZipPYTHON_3_7x86Sleep 10 Sec-12100.60 ms12101 ms🚫
ZipPYTHON_3_8x86Sleep 10 Sec-12121.07 ms12122 ms🚫
ZipPYTHON_3_8arm64Sleep 10 Sec-12107.96 ms12108 ms🚫
ZipPYTHON_3_9x86Sleep 10 Sec-12012.99 ms12013 ms🚫
ZipPYTHON_3_9arm64Sleep 10 Sec-11937.29 ms11938 ms🚫

Test results

The table above is the result of executing dozens of Lambda Functions, in a wide variety of languages, configurations, runtimes, and architectures. If you want to check the results yourself, download out the CDK project which provides a single step to deploying all these functions in your AWS account.
From the results, we can conclude several things:
  1. Only Lambda Functions using zipped (.zip) code on managed runtimes get free init.
  2. The init phase for Lambda Functions packaged as container images and Lambda Functions on custom runtimes (Amazon Linux or Amazon Linux 2) is not free.
  3. The architecture (x86 or arm64) does not influence the free init.
  4. Init phases that take more than 10 seconds are always billed, even when using managed runtimes. More about this below.
  5. Lambda extensions count as part of the init phase and follow the rules above. This is true for both internal and external extensions.

10 second+ init phase

The AWS documentation states:
The Init phase ends when the runtime and all extensions signal that they are ready by sending a Next API request. The Init phase is limited to 10 seconds. If all three tasks do not complete within 10 seconds, Lambda retries the Init phase at the time of the first function invocation.
In practice, this means that as soon as the init phase reaches the 10 seconds mark, it is aborted. When this occurs, the init phase is treated as if it didn't occur at all. Instead, the handler is called, which lands in an uninitialized execution environment. The handler then executes the init code as part of its own execution, however long it takes. This leads to a handler duration (and billed duration) of 'init + handler'. However, the actual duration is '10 seconds + init + handler', as can be seen in the logs below.
12022-04-06T22:12:20.595+02:00 Init starting 22022-04-06T22:12:20.595+02:00 Sleep for 10 second(s) 32022-04-06T22:12:30.434+02:00 START RequestId: c7bf5500-383b-4910-81f9-69abcf72b177 Version: $LATEST 42022-04-06T22:12:31.516+02:00 Init starting 52022-04-06T22:12:31.516+02:00 Sleep for 10 second(s) 62022-04-06T22:12:41.511+02:00 Init done 72022-04-06T22:12:41.512+02:00 Handler starting 82022-04-06T22:12:41.512+02:00 Sleep for 1 second(s) 92022-04-06T22:12:42.513+02:00 Handler done 102022-04-06T22:12:42.517+02:00 END RequestId: c7bf5500-383b-4910-81f9-69abcf72b177 112022-04-06T22:12:42.517+02:00 REPORT RequestId: c7bf5500-383b-4910-81f9-69abcf72b177 Duration: 12079.53 ms Billed Duration: 12080 ms Memory Size: 128 MB Max Memory Used: 11 MB
Note the timestamps: the first init phase starts at 22:12:20 and runs until 22:12:30. Then the handler is called, which restarts init at 22:12:31. The init phase is done at 22:12:41, and the handler is done at 22:12:42. 22 seconds from start to finish, and we're billed for 12080 ms. Luckily, once the first handler has successfully completed the init phase, it will remain available for the next execution.

Provisioned concurrency

Fun fact: Lambda Functions initialized under a provisioned concurrency configuration are not limited to a 10 second init duration. Because provisioned concurrency keeps the configured amount of execution environments 'warm' and ready to accept incoming requests, the init phase is completely isolated from the inbound request. To assure the execution environment is actually available, the init phase is not aborted after 10 seconds. See the logs below, which are for a Lambda Function with a managed runtime (Python 3.7) and provisioned concurrency.
12022-04-06T22:33:18.860+02:00 Init starting 22022-04-06T22:33:18.860+02:00 Sleep for 10 second(s) 32022-04-06T22:33:28.870+02:00 Init done 42022-04-06T22:35:22.883+02:00 START RequestId: c0c823ec-d175-4dce-b580-536e0551063f Version: 1 52022-04-06T22:35:22.886+02:00 Handler starting 62022-04-06T22:35:22.886+02:00 Sleep for 1 second(s) 72022-04-06T22:35:23.887+02:00 Handler done 82022-04-06T22:35:23.890+02:00 END RequestId: c0c823ec-d175-4dce-b580-536e0551063f 92022-04-06T22:35:23.890+02:00 REPORT RequestId: c0c823ec-d175-4dce-b580-536e0551063f Duration: 1003.30 ms Billed Duration: 1004 ms Memory Size: 128 MB Max Memory Used: 36 MB Init Duration: 11714.77 ms
The init phase took more than 10 seconds! The Billed Duration is only 1004 ms, which seems to indicate the entire init phase was free. Unfortunately, the official docs disprove this:
For provisioned concurrency instances, your function's initialization code runs during allocation and every few hours, as running instances of your function are recycled. You can see the initialization time in logs and traces after an instance processes a request. However, initialization is billed even if the instance never processes a request. Provisioned concurrency runs continually and is billed separately from initialization and invocation costs. For details, see AWS Lambda pricing.
Looking at the same test, but with a container image, we also see an init phase of more than 10 seconds. But as with every container image, the init phase is included in the billed duration.
12022-04-06T22:23:40.011+02:00 Init starting 22022-04-06T22:23:40.011+02:00 Sleep for 10 second(s) 32022-04-06T22:23:50.021+02:00 Init done 42022-04-06T22:25:46.791+02:00 START RequestId: aebe0c6b-1893-41e7-8ff7-b62db35df34d Version: 1 52022-04-06T22:25:46.793+02:00 Handler starting 62022-04-06T22:25:46.793+02:00 Sleep for 1 second(s) 72022-04-06T22:25:47.794+02:00 Handler done 82022-04-06T22:25:47.798+02:00 END RequestId: aebe0c6b-1893-41e7-8ff7-b62db35df34d 92022-04-06T22:25:47.798+02:00 REPORT RequestId: aebe0c6b-1893-41e7-8ff7-b62db35df34d Duration: 1003.30 ms Billed Duration: 12990 ms Memory Size: 128 MB Max Memory Used: 35 MB Init Duration: 11986.34 ms

Performance boost

As discussed in the Bite Lambda Cold Starts and Bootstrap Code, the init phase gets two unthrottled vCPUs, even at very low memory configurations. You might wonder if this applies to all configurations, even those without free init. I'm here to assure you: it does.
The table below shows the amount of prime numbers calculated in a single second. The code is written in Go and executed in the init phase and the handler code of four 128 MB Lambda Functions.
Package TypeRuntime / ImageArchitecturePrimes in InitPrimes in Handler
Dockerpublic.ecr.aws/lambda/go:1x86389243557
ZipGo on PROVIDED_AL2x86402713689
ZipGo on PROVIDED_AL2arm64337403200
ZipGO_1_Xx86371743515
The init code was more than ten times as performant as the handler code in all cases, even though only the last test gets a free init phase.

Conclusion

In this Bite we have uncovered the exact scenarios under which AWS provides a free init phase for your Lambda Functions. The goal of this exercise is to clear out any confusion about the free init phase. But what should you do with this knowledge? It might come as a surprise, but the answer is: nothing. You shouldn't choose your technology based on whether you get a free initialization phase. Instead, follow these general guidelines:
  • If you can use the zip format, do so. It is more efficient, easier to maintain, and less prone to security issues than the container image format.
  • Choose your language based on other aspects than the free init, such as performance requirements or familiarity within your team.
  • If you have an init duration of more than 10 seconds, reconsider your function design. A duration this long is a clear sign something is wrong and should be optimized, for example by packaging requirements in a Lambda Layer or by storing reusable large files on EFS.
If these guidelines lead to a design with free init, good! If they don't, it's also good. You made the best architectural decisions for your workload, and the result does not have free init. Finally, keep in mind that free init is not an official feature. AWS might change or remove it at any time, so do not depend on its indefinite availability.

CDK Project

The services and code described in this Bite are available as a Python AWS Cloud Development Kit (CDK) Project. Within the project, execute a cdk synth to generate CloudFormation templates. Then deploy these templates to your AWS account with a cdk deploy. For your convenience, ready-to-use CloudFormation templates are also available in the cdk.out folder. For further instructions how to use the CDK, see Getting started with the AWS CDK.

Click the Download button below for a Zip file containing the project.