DEV Community

Cover image for Keeping AWS Lambda Warm Without Provisioned Concurrency
Ronen Cypis
Ronen Cypis

Posted on Originally published at runhooks.app

Keeping AWS Lambda Warm Without Provisioned Concurrency

Your Lambda-backed API is quick — until the first call after a quiet period. That one takes a second or two longer than the rest, sometimes enough to trip a client timeout. Call it again and it's snappy.

That first slow call is a cold start, and AWS's official fix for it — provisioned concurrency — solves the latency but adds a bill you may not want for a low-traffic function.

Why Lambda Cold Starts Happen

Lambda runs your code in an execution environment that AWS creates on demand. When a function goes idle, AWS reclaims that environment. The next invocation has to build a new one: download your deployment package, start the runtime, and run your initialization code (imports, SDK clients, database connections) before your handler even begins.

That setup is the cold start. It's most painful for:

  • Functions in a VPC, which historically pay extra for network setup.
  • Large dependency bundles, which take longer to load.
  • JVM and .NET runtimes, which have heavier startup than Node.js or Python.

AWS's answer is provisioned concurrency: keep a set number of environments initialized and ready at all times. It delivers consistent low latency — and it bills you to keep those environments warm continuously, whether or not anyone is calling. For a function that's idle most of the day, that's paying full-time to fix an occasional delay.

The Fix: A Scheduled Warm-Up Invocation

There's a lighter approach: invoke the function yourself on a schedule so AWS keeps at least one environment warm between real requests.

Most Lambda functions sit behind an API Gateway route or a Function URL. Point a scheduled GET at a lightweight health path:

export const handler = async (event) => {
  // A cheap path that skips the real work when it's just a warm-up.
  if (event.rawPath === '/health' || event.warmup) {
    return { statusCode: 200, body: 'ok' };
  }
  // ... real handler logic
};
Enter fullscreen mode Exit fullscreen mode

Then schedule that request every 5 minutes (*/5 * * * *). Each invocation keeps an execution environment alive, so the next real caller lands on a warm function instead of paying the cold-start tax — and you only pay for a handful of short pings an hour, not a continuously provisioned environment.

Why a DIY Cron Job Isn't Enough

You could schedule the ping with a cron job, or even an EventBridge rule. Both have gaps:

  • A laptop or VPS cron job stops when the machine sleeps, and means running a server to keep your serverless function warm.
  • Fails silently. If a warm-up call errors, there's no retry, no alert, no log — the environment goes cold and the next user notices.
  • No unified execution history. Correlating a slow request with a missed warm-up means digging through scattered logs.
  • No retries. A single dropped ping can be the gap where the environment is reclaimed.

You can bolt retries, logging, and alerting onto a curl or a Lambda-invoking script — but at that point you've rebuilt a scheduler.

How Runhooks Keeps Lambda Warm

Runhooks is a scheduled HTTP execution service with reliability built in. Warming a function is a two-minute setup:

  1. Create a job — name it "Lambda keep-warm."
  2. Set the URL — your API Gateway or Function URL health path, e.g. https://abc123.execute-api.us-east-1.amazonaws.com/health.
  3. Set the schedule — */5 * * * *.
  4. Enable retries — 3 attempts with exponential backoff.

What you get beyond a bare cron job or EventBridge rule:

  • Execution logs — every warm-up recorded with status, response, and duration.
  • Automatic retries — a transient failure retries instead of leaving the function to go cold.
  • Failure alerts — if the function starts erroring, you're notified immediately.
  • Managed infrastructure — runs 24/7, independent of your own machines.

Because each ping is logged and alertable, you get keep-warm and uptime monitoring in one job: the invocations keep an environment ready, and the alerts catch a genuinely broken function rather than just a cold one.

When Provisioned Concurrency Is Still Worth It

To be fair, warming isn't a total replacement. If you need guaranteed zero cold starts under load — a high-traffic, latency-critical API where a burst of concurrent requests must all be fast — provisioned concurrency pre-initializes the environments a warm-up ping can't. A single warm environment handles the trickle after it; it doesn't pre-warm the extra environments Lambda spins up when concurrency spikes.

Use a scheduled warm-up when you want to kill the idle cold start on a low-traffic function without paying to keep environments provisioned around the clock. Reach for provisioned concurrency when consistent latency at scale is a hard requirement.

Get Started

Lambda's pay-per-use model is great for cost — cold starts are the trade-off, and you can soften them cheaply:

  1. Add a lightweight health path to your function.
  2. Create a Runhooks account and schedule a GET every 5 minutes.
  3. Callers hit a warm function, and you get alerted if it starts failing.

Preview your schedule with the cron expression visualizer, and compare plans when you need more jobs or longer log retention.

Frequently Asked Questions

Why does AWS Lambda have cold starts?

When a Lambda function hasn't been invoked recently, AWS tears down its execution environment. The next invocation has to create a fresh environment — download your code, start the runtime, and run any initialization — before your handler runs. This adds latency, and it's worst for functions in a VPC, with large dependency bundles, or on JVM and .NET runtimes.

How do I keep a Lambda function warm?

Invoke the function on a schedule so AWS keeps its execution environment alive between real requests. A warm-up invocation every 5 minutes is the common interval. You can trigger it by calling the function's API Gateway URL or Function URL on a schedule with an external scheduler like Runhooks, which also retries failed pings and alerts you when the function errors.

Is a warm-up ping cheaper than provisioned concurrency?

For low-traffic functions, yes. Provisioned concurrency bills you to keep a set number of environments initialized around the clock. A warm-up ping only pays for a short invocation every few minutes, which is far cheaper for functions that are idle most of the time. Provisioned concurrency is still the right tool when you need guaranteed zero cold starts at scale.

Does pinging a Lambda eliminate all cold starts?

It keeps one execution environment warm, which removes the cold start for the steady trickle of traffic that follows. It does not remove cold starts that happen when many requests arrive at once and Lambda spins up additional concurrent environments. For that, provisioned concurrency is the answer. For most low-traffic APIs and webhooks, a single warm environment covers it.


Disclosure: I'm the founder of Runhooks, one of the tools mentioned in this article.

Top comments (0)