# Lambda Warmer and keeping AWS Lambda functions out of the cold

> Lambda Warmer is a small dependency-free JavaScript module that recognises a scheduled ping event in a Lambda handler and returns early, so the container stays warm and the function does not pay an initialisation cost on the first real request. The interesting engineering is that it also fans out to warm several concurrent execution environments at once, and the interesting constraint is that doing so needs a self-invoke permission and, in a VPC, a NAT gateway.

**jeremydaly/lambda-warmer** — A module to optimize AWS Lambda function cold starts

- Repository: https://github.com/jeremydaly/lambda-warmer
- Stars: 537 · Forks: 55
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jeremydaly-lambda-warmer

## What the module actually does

Lambda Warmer is a module to optimize AWS Lambda function cold starts, and it is described as a lightweight module with no dependencies that can be added to your Lambda functions to manage warming events as well as handling automatic fan-out for initializing concurrent functions.

The mechanism is a short-circuit. You schedule a periodic event that invokes your function with a recognisable payload, and the module checks for that payload at the top of your handler. If it matches, the handler returns immediately without doing its real work, and the container stays alive because it was recently invoked.

The code is small enough to read in one sitting:

```javascript
const warmer = require('lambda-warmer')

exports.handler = async (event, context) => {
  // if a warming event
  if (await warmer(event, {/* lambda warmer config here */}, context)) return 'warmed'
  // else proceed with handler logic
  return 'Hello from Lambda'
}
```

The return value is a resolved promise with either true, meaning this is a warming invocation, or false, meaning it is not. The documentation shows both the async and await form above, and a callback form for handlers that use callbacks, where the module starts a promise chain and the handler logic becomes conditional on the result.

The context object is optional in the general case but required in one specific situation. When your function is invoked using an alias such as stable or prod, you need to pass the context object to lambda-warmer so it can accurately determine if the function is invoking itself. By passing context, the module can extract the alias from context.invokedFunctionArn and properly handle warming invocations.

The primary language is JavaScript, the licence is MIT, and the recent releases are v2.3.0 from 2025-02-04, v2.2.0 from 2024-10-12 and v2.1.0 from the same day. The last push was on 2026-10-03.

One versioning note that will save a debugging session: lambda-warmer@v2 is using AWS SDK v3, and if you are on AWS SDK v2 you should use lambda-warmer@v1.

## The design rules it inherits, and why they matter

The module is built on a set of rules that come from a talk by Chris Munns, the Senior Developer Advocate for Serverless at AWS, given at an AWS Startup Day event in Boston. The readme is explicit that using the CloudWatch Events ping method is really the only way to do it right now, and that Munns acknowledged it as a hack.

The rules are worth reading because they tell you what the module will not do for you. Do not ping more often than every five minutes. Invoke the function directly rather than going through API Gateway. Pass in a test payload that can be identified as such. Create handler logic that replies accordingly without running the whole function.

That last one is the design constraint the module exists to make easy. The ping has to be cheap, otherwise the warming mechanism costs more than the cold start it prevents. The four rules together produce a scheme where the recurring cost is a near-empty invocation every five minutes.

There is a subtlety about keeping several concurrent functions warm, and it is the reason the fan-out exists. If you want more than one execution environment warm, you need to invoke the same function multiple times with delayed executions, because otherwise the system reuses the same container. One ping warms one environment.

The module therefore takes a concurrency setting, and the readme is clear that when you use the fan-out process with concurrency greater than one, the function invokes itself multiple times using the AWS SDK. The delay setting exists for the same reason. Concurrent functions are invoked asynchronously, and setting a delay enforces Lambda to create multiple invocations, with a default of 75 milliseconds to attempt sub 100ms invocation times.

So the schedule, the payload shape and the handler guard are a contract with three parts, and the module handles the fourth part, which is fanning out several invocations so that several containers stay warm at once.

## Configuration and the permission it requires

The configuration object is the second parameter and every setting in it is optional. The documented sample is:

```javascript
{
  flag: 'warmer',
  concurrency: 'concurrency',
  test: 'test',
  log: true,
  correlationId: 'XXXXXXXXXXXXX',
  delay: 75
}
```

The first three are field names rather than values, which is the design detail to grasp. The flag is the name of the event field used to notify the module that this is a warming invocation, defaulting to warmer. The concurrency is the name of the event field used to specify how many concurrent functions you would like to warm, defaulting to concurrency. The test is the name of the event field used to flag a warming invocation as a test, defaulting to test.

Because these are field names rather than fixed keys, the warming event is not a magic payload. You decide what a warming event looks like in your own event shape, and the module looks for your field names. That also means a real event that happens to carry a field called warmer would be mistaken for a warming invocation, which is a risk worth thinking about when you design the event schema.

The log option is a boolean controlling whether CloudWatch logs are automatically generated, defaulting to true. The correlationId is passed to all concurrent Lambda invocations and can be used to group invocations within your logs, and it defaults to the id generated for the invoked function, with passing context.awsRequestId described as good practice.

The target option names the target function to be warmed and defaults to funcName, which is the function itself.

The permission is the part to get right. The readme is explicit that your functions must have lambda:InvokeFunction permissions so that they can invoke themselves, and that following the principle of least privilege you should limit the Resource to the function itself. The example policy is:

```yaml
- Effect: "Allow"
  Action:
    - "lambda:InvokeFunction"
  Resource: "arn:aws:lambda:us-east-1:{AWS-ACCOUNT-ID}:function:my-test-function"
```

Note what that grants. It is a self-invoke permission on one named function, not lambda:InvokeFunction on a wildcard, and the readme says so. A broader policy would work and would be a bad idea.

The VPC caveat is the other operational detail, and it is the one most likely to bite. If you are running Lambda Warmer in a VPC and using the fan-out process with concurrency greater than one, you must ensure your VPC has a properly configured NAT gateway, because fan-out requires internet access, which is provided by the NAT Gateway. Without it, the function invocations will fail. The readme suggests that most users with an already configured NAT gateway will not encounter this unless the gateway is removed or misconfigured, and that if you notice failures in concurrent warm-up you should check the gateway.

## Installing it and what the package contains

Installation is an ordinary npm dependency:

```bash
npm i lambda-warmer
```

The package manifest shows how little ships. The main entry is index.js, the types entry is ./types/index.d.ts, and the files list contains lib/, index.js, types/index.d.ts, LICENSE and README.md. The dependencies field is absent, and the readme badge reports zero dependencies, which is the claim that matters for a module you put at the top of every handler.

The development setup is a little more involved than a typical small package. The test script runs mocha with the --check-leaks and --recursive flags, and the coverage variant adds nyc. The CI script chains lint, tests and a coveralls report, and the prepublishOnly hook runs lint, test and dtslint, so the type definitions are checked before anything is published. The devDependencies include chai, sinon, rewire and nyc, and rewire in particular is the tool for reaching into module internals in tests, which is what you need when the interesting logic is deciding whether an event is a warming event.

The repository layout is small and conventional: index.js at the root, a lib/ directory for the implementation, a types/ directory for the TypeScript definitions, a test/ directory, and both .eslintrc.json and eslint.config.js, which suggests the project has migrated to the flat config style while keeping the old file around.

The author is Jeremy Daly, and the package lists a second maintainer, Naor Peled, which is worth noting for a project whose latest release was in February 2025 and whose repository received a push in October 2026.

## Where Lambda Warmer is the wrong choice

Three limits follow from what the module does, and all three are about whether keeping a container warm is worth what it costs.

The first is the cost of the ping itself. You are paying for a Lambda invocation every five minutes, and that invocation is billed. For a function that runs constantly under real traffic, the warm container would have been warm anyway and the ping is pure overhead. For a function that runs a few times a day, the ping is a meaningful fraction of the function's total cost. The rule of thumb in the readme is that you should not ping more often than every five minutes, which sets a floor but not a cost model, and the linked post is described as covering why you might or might not want to use it.

The second is the workload shape. Cold starts hurt when a user is waiting on the first request. If your traffic is spiky and event-driven, most invocations are a first invocation, and the ping warms a container that the next real request does not reuse. The warming works best for a function with a steady, predictable request rate, and it does nothing for a burst that arrives all at once after an idle hour.

The third is the operational surface. The module needs a scheduled event, and with fan-out it needs self-invoke permission, a NAT gateway if the function is in a VPC, and a deliberate decision about the delay and concurrency values. That is a small amount of configuration, but it is configuration, and it interacts with things you may not control, like whether the gateway exists at all.

The alternative worth comparing against is not another module but a different architecture. Provisioned concurrency holds a set of execution environments ready for a function, which is the supported way to remove cold starts, at a cost that scales with the reserved concurrency you ask for. The difference in approach is that Lambda Warmer works within the standard on-demand model and only warms a container when a ping has recently touched it, whereas provisioned concurrency is a guarantee rather than a probability, paid for in advance. For a latency-sensitive function with a known baseline, provisioned concurrency is the more honest answer. For a function with occasional traffic and a small budget, the ping is cheaper and adequate.

## Conclusion

Lambda Warmer is a good fit for a Lambda function with a predictable, steady request pattern where the first-request latency is visible to users, because the module adds one require and one conditional to the handler and needs no infrastructure beyond a scheduled event. It is a poor fit for spiky, event-driven workloads where most invocations are the first, since a ping every five minutes costs a running environment for a function that is idle the rest of the time, and it is a poor fit inside a VPC without a NAT gateway. Before adopting, read the linked post on why you might not want it, check whether your function is invoked through an alias, because the context object has to be passed for that case, and confirm the lambda:InvokeFunction permission is scoped to the function itself.

## FAQ

### What does the lambda-warmer module do?

It detects a scheduled warming event in your handler and returns early, so the container stays warm and the function does not pay initialisation cost on the next real request. It returns a resolved promise with true when the invocation is a warming event and false when it is not.

### How do I install and instrument Lambda Warmer?

Install it with `npm i lambda-warmer`, then require it outside your main handler and pass the event as the first argument. If you use async and await you can await the result and return if it is true, which short circuits the rest of the handler.

### Why do I need to pass the context object to lambda-warmer?

When your function is invoked using an alias such as stable or prod, the context object lets lambda-warmer determine that the function is invoking itself. It extracts the alias from context.invokedFunctionArn and handles warming invocations correctly.

### What permissions does Lambda Warmer need?

Your functions must have lambda:InvokeFunction permission so they can invoke themselves, and the Resource should be limited to the function itself following the principle of least privilege, rather than a wildcard.

### What is the difference between lambda-warmer v1 and v2?

lambda-warmer@v2 is using AWS SDK v3, so if you are on AWS SDK v2 you should use lambda-warmer@v1. The current version is 2.3.0.

### Does Lambda Warmer work inside a VPC?

Yes, but the fan-out process with concurrency greater than one requires internet access, which means your VPC needs a properly configured NAT Gateway. Without one the function invocations will fail, so check the gateway if concurrent warm-up fails.

## Sources

- [Issues](https://github.com/jeremydaly/lambda-warmer/issues)
- [jeremydaly/lambda-warmer on GitHub](https://github.com/jeremydaly/lambda-warmer)
- [License: MIT](https://github.com/jeremydaly/lambda-warmer/blob/master/LICENSE)
- [README](https://github.com/jeremydaly/lambda-warmer/blob/master/README.md)
- [Releases](https://github.com/jeremydaly/lambda-warmer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jeremydaly-lambda-warmer
