Keep your lambdas warm during winter.
Requirements:
- Serverless v1.12.x or higher (Recommended v1.33.x or higher because of this).
- AWS provider
WarmUp solves cold starts by creating a scheduled lambda that invokes all the selected service's lambdas in a configured time interval (default: 5 minutes) and forcing your containers to stay warm.
Install via npm in the root of your Serverless service:
npm install --save-dev serverless-plugin-warmup
Add the plugin to the plugins
array in your Serverless serverless.yaml
:
plugins:
- serverless-plugin-warmup
Most options are set under custom.warmup
in the serverless.yaml
file.
- folderName (default
_warmup
) - cleanFolder (default
true
) - name (default
${service}-${stage}-warmup-plugin
) - role (default to role in the provider)
- tags (default to serverless default tags)
- vpc (default to vpc in provider, can be set to
false
to deploy the warmup function outside of VPC) - memorySize (default
128
) - events (default
- schedule: rate(5 minutes)
, can be any Serverless event) - package (default
package: { individually: true, exclude: ['**'], include: ['_warmup/**'] }
) - timeout (default
10
seconds) - environment (default to unset all package level environment variables. It can be use to set variables or to unset global variables by setting it to undefined. However, you should never have to change the default.)
- prewarm (default
false
)
But there are some options which can also be set under custom.warmup
to be applied to all lambdas to be warmed up or can be overridden on each individual lambda.
- enabled (default
false
,an be a boolean, a stage or a list of stages.) - payload (default
{ "source": "serverless-plugin-warmup" }
, payload will be stringified) - payloadRaw (default
false
) - concurrency (default
1
)
custom:
warmup:
enabled: true # Whether to warm up functions by default or not
folderName: '_warmup' # Name of the folder created for the generated warmup
cleanFolder: false
memorySize: 256
name: 'make-them-pop'
role: myCustRole0
tags:
Project: foo
Owner: bar
vpc: false
events:
- schedule: 'cron(0/5 8-17 ? * MON-FRI *)' # Run WarmUp every 5 minutes Mon-Fri between 8:00am and 5:55pm (UTC)
package:
individually: true
exclude: # exclude additional binaries that are included at the serverless package level
- ../**
- ../../**
include:
- ./**
timeout: 20
prewarm: true # Run WarmUp immediately after a deploymentlambda
payload:
source: my-custom-source
other: 20
payloadRaw: true # Won't JSON.stringify() the payload, may be necessary for Go/AppSync deployments
concurrency: 5 # Warm up 5 concurrent instances
functions:
myColdfunction:
handler: 'myColdfunction.handler'
events:
- http:
path: my-cold-function
method: post
warmup:
enabled: false
myLowConcurrencyFunction:
handler: 'myLowConcurrencyFunction.handler'
events:
- http:
path: my-low-concurrency-function
method: post
warmup:
payload: different-source-only-for-this-lambda
concurrency: 1
myProductionOnlyFunction:
handler: 'myProductionOnlyFunction.handler'
events:
- http:
path: my-production-only-function
method: post
warmup:
enabled: prod
myDevAndStagingOnlyFunction:
handler: 'myDevAndStagingOnlyFunction.handler'
events:
- http:
path: my-dev-and-staging-only-function
method: post
warmup:
enabled:
- dev
- staging
- Number of lambdas to warm up
- Day cold periods
- Desire to avoid cold lambdas after a deployment
Concurrency can be modified post-deployment at runtime by using Lambda environment variables.
Two configuration options exist:
- Globally set the concurrency of all lambdas on the stack (overriding the deployment configuration):
Set the environment variableWARMUP_CONCURRENCY
- Individually set the concurrency per lambda
Set the environment variable WARMUP_CONCURRENCY_YOUR_FUNCTION_NAME. Must be all uppercase and hyphens (-) are replaced with underscores (_). If present, it overrides the global concurrency setting.
Over time some options have been removed form the pluging. For now, we keep backwards compatibility so they still work. However, they are listed here only to facilitate upgrading the pluging and we strongly recommend switching to the options defined above as soon as possible.
- default Has been renamed to
enabled
- schedule
schedule: rate(5 minutes)
is equivalent toevents: - schedule: rate(5 minutes)
. - source Has been renamed to
payload
- sourceRaw Has been renamed to
payloadRaw
WarmUp requires some permissions to be able to invoke
your lambdas.
custom:
warmup:
folderName: '_warmup' # Name of the folder created for the generated warmup
cleanFolder: false
memorySize: 256
name: 'make-them-pop'
role: myCustRole0
events:
- schedule: 'cron(0/5 8-17 ? * MON-FRI *)' # Run WarmUp every 5 minutes Mon-Fri between 8:00am and 5:55pm (UTC)
timeout: 20
prewarm: true # Run WarmUp immediately after a deployment
tags:
Project: foo
Owner: bar
.....
resources:
Resources:
myCustRole0:
Type: AWS::IAM::Role
Properties:
Path: /my/cust/path/
RoleName: MyCustRole0
AssumeRolePolicyDocument:
Version: '2017'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2017'
Statement:
- Effect: Allow # WarmUp lamda to send logs to CloudWatch
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: Allow # WarmUp lamda to manage ENIS (only needed if deploying to VPC, https://docs.aws.amazon.com/lambda/latest/dg/vpc.html)
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- ec2:DeleteNetworkInterface
Resource: "*"
- Effect: 'Allow' # WarmUp lamda to invoke the functions to be warmed
Action:
- 'lambda:InvokeFunction'
Resource:
- Fn::Join:
- ':'
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function:${self:service}-${opt:stage, self:provider.stage}-*
The permissions can also be added to all lambdas using iamRoleStatements
under provider
(see https://serverless.com/framework/docs/providers/aws/guide/functions/#permissions):
provider:
name: aws
runtime: nodejs10.x
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'lambda:InvokeFunction'
Resource:
- Fn::Join:
- ':'
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function:${self:service}-${opt:stage, self:provider.stage}-*
If using pre-warm, the deployment user also needs a similar policy so it can run the WarmUp lambda.
Lambdas invoked by WarmUp will have the event source serverless-plugin-warmup
(unless otherwise specified using the payload
option):
{
"Event": {
"source": "serverless-plugin-warmup"
}
}
To minimize cost and avoid running your lambda unnecessarily, you should add an early return call before your lambda logic when that payload is received.
// Using the Promise style
module.exports.lambdaToWarm = async function(event, context) {
/** Immediate response for WarmUp plugin */
if (event.source === 'serverless-plugin-warmup') {
console.log('WarmUp - Lambda is warm!');
return 'Lambda is warm!';
}
... add lambda logic after
}
// Using the Callback style
module.exports.lambdaToWarm = function(event, context, callback) {
/** Immediate response for WarmUp plugin */
if (event.source === 'serverless-plugin-warmup') {
console.log('WarmUp - Lambda is warm!')
return callback(null, 'Lambda is warm!')
}
... add lambda logic after
}
// Using context.
// This could be useful if you are handling the raw input and output streams.
module.exports.lambdaToWarm = async function(event, context) {
/** Immediate response for WarmUp plugin */
if (context.custom.source === 'serverless-plugin-warmup') {
console.log('WarmUp - Lambda is warm!');
return 'Lambda is warm!';
}
... add lambda logic after
}
If you're using the concurrency
option you might want to add a slight delay before returning on warmup calls to ensure that your function doesn't return before all concurrent requests have been started:
module.exports.lambdaToWarm = async (event, context) => {
if (event.source === 'serverless-plugin-warmup') {
console.log('WarmUp - Lambda is warm!');
/** Slightly delayed (25ms) response
to ensure concurrent invocation */
await new Promise(r => setTimeout(r, 25));
return 'Lambda is warm!';
}
... add lambda logic after
}
def lambda_handler(event, context):
# early return call when the function is called by warmup plugin
if event.get("source") in ["aws.events", "serverless-plugin-warmup"]:
print('Lambda is warm!')
return {}
# function logic here
...
WarmUp supports serverless deploy
.
WarmUp supports serverless package
.
By default, the WarmUp function is packaged individually and it uses a folder named _warmup
to store duiring the packaging process, which is deleted at the end of the process.
If you are doing your own package artifact you can set the cleanFolder
option to false
and include the _warmup
folder in your custom artifact.
The WarmUp function use normal calls to the AWS SDK in order to keep your lambdas warm. By deafult, the WarmUp function is deployed outside of any VPC so it can reach AWS API. If you use the VPC option to deploy your WarmUp function to a VPC subnet it will need internet access. You can do it by using an Internet Gateway or a Network Address Translation (NAT) gateway.
You can check the Lambda pricing and CloudWatch pricing or can use the AWS Lambda Pricing Calculator to estimate the monthly cost
If you want to warm 10 functions, each with memorySize = 1024
and duration = 10
, using the default settings (and we ignore the free tier):
- WarmUp: runs 8640 times per month = $0.18
- 10 warm lambdas: each invoked 8640 times per month = $14.4
- Total = $14.58
CloudWatch costs are not in this example because they are very low.
Help us making this plugin better and future proof.
- Clone the code
- Install the dependencies with
npm install
- Create a feature branch
git checkout -b new_feature
- Add your code and add tests if you implement a new feature
- Validate your changes
npm run lint
andnpm test
(ornpm run test-with-coverage
)
This software is released under the MIT license. See the license file for more details.