Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
serverless-plugin-warmup
Advanced tools
Keep your lambdas warm during winter.
Requirements:
WarmUp solves cold starts by creating a scheduled lambda (the warmer) 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
The warmup plugin supports creating one or more warmer functions. Warmers must be defined under custom.warmup
in the serverless.yaml
file before they can be used in the functions' configs:
custom:
warmup:
officeHoursWarmer:
enabled: true
events:
- schedule: cron(0/5 8-17 ? * MON-FRI *)
concurrency: 10
verbose: true
logRetentionInDays: 14
outOfOfficeHoursWarmer:
enabled: true
events:
- schedule: cron(0/5 0-7 ? * MON-FRI *)
- schedule: cron(0/5 18-23 ? * MON-FRI *)
- schedule: cron(0/5 * ? * SAT-SUN *)
concurrency: 1
verbose: false
testWarmer:
enabled: false
The options are the same for all the warmers:
.warmup
)true
)${service}-${stage}-warmup-plugin-${warmerName}
)${service.service}-${stage}-${region}-${warmerName.toLowerCase()}-role
)false
in order to deploy the warmup function outside of a VPC (defaults to the vpc in the provider)128
)- schedule: rate(5 minutes)
)x86_64
){ individually: true, patterns: ['!**', '.warmup/${warmerName}/**'] }
)10
)boolean
, Active
or PassThrough
and defaults to the provider-level setting)true
)false
)There are also some options which can be set under custom.warmup.<yourWarmer>
to be applied to all your lambdas or under yourLambda.warmup.<yourWarmer>
to overridde the global configuration for that particular lambda. Keep in mind that in order to configure a warmer at the function level, it needed to be previously configured at the custom
section or the pluging will error.
false
)false
to avoid sending any client context custom data){ "source": "serverless-plugin-warmup" }
)false
)1
)custom:
warmup:
default:
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: warmer-default
roleName: my-custom-role
role: WarmupRole
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
patterns:
- '!../**'
- '!../../**'
- ./**
timeout: 20
tracing: true
verbose: false # Disable the logs
logRetentionInDays: 14
prewarm: true # Run WarmUp immediately after a deploymentlambda
clientContext:
source: my-custom-source
other: '20'
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:
default:
enabled: false
myLowConcurrencyFunction:
handler: 'myLowConcurrencyFunction.handler'
events:
- http:
path: my-low-concurrency-function
method: post
warmup:
default:
clientContext:
source: different-source-only-for-this-lambda
payload:
source: different-source-only-for-this-lambda
concurrency: 1
myProductionOnlyFunction:
handler: 'myProductionOnlyFunction.handler'
events:
- http:
path: my-production-only-function
method: post
warmup:
default:
enabled: prod
myDevAndStagingOnlyFunction:
handler: 'myDevAndStagingOnlyFunction.handler'
events:
- http:
path: my-dev-and-staging-only-function
method: post
warmup:
default:
enabled:
- dev
- staging
Concurrency can be modified post-deployment at runtime by setting the warmer lambda environment variables.
Two configuration options exist:
WARMUP_CONCURRENCY
WARMUP_CONCURRENCY_YOUR_FUNCTION_NAME
. Must be all uppercase and hyphens (-) must be replaced with underscores (_). If present for one of your lambdas, it overrides the global concurrency setting.The WarmUp function use normal calls to the AWS SDK in order to keep your lambdas warm. If you set up at the provider level or the warmer confir level that the wamer function should be deployed into into a VPC subnet you need to keep in mind a couple of things:
Since the AWS SDK doesn't provide any timeout by default, this plugin uses a default connection timeout of 1 second. This is to avoid the issue of a lambda constantly timing out and consuming all its allowed duration simply because it can't connect to the AWS API.
WarmUp requires permission to be able to invoke
your lambdas.
If no role is provided at the custom.warmup
level, each warmer function gets a default role with minimal permissions allowing the warmer function to:
The default role for each warmer looks like:
resources:
Resources:
WarmupRole:
Type: AWS::IAM::Role
Properties:
RoleName: WarmupRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: WarmUpLambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
# Warmer lambda to send logs to CloudWatch
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${warmer.name}:*
- Effect: Allow
Action:
- logs:PutLogEvents
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${warmer.name}:*:*
# Warmer lambda to invoke the functions to be warmed
- Effect: 'Allow'
Action:
- lambda:InvokeFunction
Resource:
- !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${fn1.name}
- !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${fn2.name}
# and one more row for each function that must be warmed up by the warmer
# Warmer lambda to manage ENIS (only needed if deploying to VPC, https://docs.aws.amazon.com/lambda/latest/dg/vpc.html)
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- ec2:DeleteNetworkInterface
Resource: "*"
The permissions can also be added to all lambdas using setting the role to IamRoleLambdaExecution
and setting the permissions in iamRoleStatements
under provider
(see https://serverless.com/framework/docs/providers/aws/guide/functions/#permissions):
provider:
name: aws
runtime: nodejs20.x
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'lambda:InvokeFunction'
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${opt:stage, self:provider.stage}-*
custom:
warmup:
default:
enabled: true
role: IamRoleLambdaExecution
architecture: 'arm64'
If setting prewarm
to true
, the deployment user used by the AWS CLI and the Serverless framework also needs permissions to invoke the warmer.
When invoked by WarmUp, your lambdas 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!';
}
// ... function logic
}
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!')
}
// ... function logic
}
Using the 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!';
}
// ... function logic
}
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
}
You can handle it in your function:
def lambda_handler(event, context):
# early return call when the function is called by warmup plugin
if event.get("source") == "serverless-plugin-warmup":
print("WarmUp - Lambda is warm!")
return {}
# ... function logic
Or you could use a decorator to avoid the redundant logic in all your functions:
def skip_execution_if.warmup_call(func):
def warmup_wrapper(event, context):
if event.get("source") == "serverless-plugin-warmup":
print("WarmUp - Lambda is warm!")
return {}
return func(event, context)
return warmup_wrapper
# ...
@skip_execution_if.warmup_call
def lambda_handler(event, context):
# ... function logic
You can handle it in your function:
public ApiGatewayResponse handleRequest(Map<String, Object> input, Context context) {
if ("serverless-plugin-warmup".equals(input.get("source"))) {
System.out.println("WarmUp - Lambda is warm!");
return ApiGatewayResponse.builder()
.setStatusCode(200)
.build();
}
// ... function logic
}
You can handle it in your function:
def handle_request(app:, event:, context:, config: {})
if event['source'] == 'serverless-plugin-warmup'
puts 'WarmUp - Lambda is warm!'
return {}
end
# ... function logic
end
WarmUp plugin uses 3 lifecycles hooks:
warmup:addWarmers:addWarmers
: This is where the warmers are added to the service. It runs after:package:initialize
.warmup:cleanupTempDir:cleanup
: This is where the warmers' temp folders are removed. It runs after:package:createDeploymentArtifacts
.warmup:prewarm:start
: This is where the warmers are invoked. It runs after:deploy:deploy
or when running the command serverless warmup prewarm
.warmup:prewarm:end
: This is after the warmers are invoked.WarmUp supports
serverless package
By default, each warmer function is packaged individually and it uses a folder named .warmup/<function_name>
to serve as temporary folder during the packaging process. This folder is deleted at the end of the packaging process unless the cleanFolder
option is set to false
.
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.
WarmUp adds package the warmers and add them to your services automatically when you run
serverless deploy
After the deployment, any warmer with prewarm: true
is automatically invoked to warm up your functions without delay.
Apart from prewarming automatically after each deployment. You can invokes a warmer after a sucessful deployment to warm up functions using:
serverless warmup prewarm -warmers <warmer_name>
The warmers
flag takes a comma-separated list of warmer names. If it's nor provided, all warmers with prewarm
set to true
are invoked.
From Serverless 2.32.0 the patterns
option is the recommended approach to include/exclude files from packaging. In version 3.X, the include
and exclude
are removed.
This plugin applies the same philosophy.
What used to be:
custom:
warmup:
default:
enabled: 'prod'
package:
individually: true
exclude: '../**',
include: 'myFolder'
is the same as
custom:
warmup:
default:
enabled: 'prod'
package:
individually: true
patterns:
- '!../**',
- 'myFolder'
Previous versions of the plugin only support a single warmer which limited use cases like having different concurrentcies in different time periods. From v5, multiple warmers are supported. The warmup
field in the custom
section or the function section, takes an object where each key represent the name of the warmer and the value the configuration which is exactly as it used to be except for the changes listed below.
custom:
warmup:
enabled: true
events:
- schedule: rate(5 minutes)
have to be named, for example, to default
:
custom:
warmup:
default:
enabled: true
events:
- schedule: rate(5 minutes)
.warmup
Previous versions of the plugin named the temporary folder to create the warmer handler _warmup
. It has been renamed to .warmup
to better align with the serverless framework and other plugins' behaviours.
Remembe to add .warmup
to your git ignore.
Previous versions of the plugin used the $LATEST
alias as default alias to warm up if no alias was provided. From v5, the unqualified alias is the default. You can still use the $LATEST
alias by setting it using the alias
option.
custom:
warmup:
default:
alias: $LATEST
Previous versions of the plugin exclude everything in the service folder and include the .warmup
folder. This caused that any files that you include to the service level were also included in the plugin specially if you include ancestor folders (like ../**
)
From v5, all service level include are automatically excluded from the plugin. You still override this behaviour using the package
option.
Previous versions of the plugin supported replacing the configuration by a boolean, a string representing a stage or an array of strings representing a list of stages. From v5, this is not supported anymore. The enabled
option is equivalent.
custom:
warmup: 'prod'
is the same as
custom:
warmup:
default: # Name of the warmer, see above
enabled: 'prod'
The following legacy options have been completely removed:
enabled
schedule: rate(5 minutes)
is equivalent to events: - schedule: rate(5 minutes)
.payload
payloadRaw
If no role is provided in the custom.warmup
config, a default role with minimal permissions is created for each warmer. See "Permissions" section
If tracing is enabled at the provider level or at the warmer config level, the X-Ray client is automatically installed and X-Ray tracing is enabled.
See the "Networking" section for more details.
You can check the Lambda pricing and CloudWatch pricing or can use the AWS Lambda Pricing Calculator to estimate the monthly cost
If you have a single warmer and want to warm 10 functions, each with memorySize = 1024
and duration = 10
, using the default settings ($0.0000166667 for every GB-second) and ignoring the free tier:
CloudWatch costs are not consdiered in this example.
Help us making this plugin better and future-proof.
npm install
git checkout -b new_feature
npm run lint
and npm test
(or npm run test-with-coverage
)This software is released under the MIT license. See the license file for more details.
Thanks to Fidel who initially developed this plugin.
FAQs
Keep your lambdas warm during winter.
The npm package serverless-plugin-warmup receives a total of 36,661 weekly downloads. As such, serverless-plugin-warmup popularity was classified as popular.
We found that serverless-plugin-warmup demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.