cronalerts all systems nominal

Cold starts on scheduled work and what they cost

A guest post from the Modern Serverless team on the specific cold start economics of scheduled functions, and why the right tradeoffs differ from request-driven cold starts.

Cold starts on serverless functions are most often discussed in the context of request-driven workloads. A user makes a request, the platform spins up a new execution environment, and the user waits an extra second or two for the response. The customer-facing impact is the framing.

Scheduled work has a different cold start economics, and most teams do not think about it explicitly. Every scheduled run is, by definition, separated from every other scheduled run. The platform is unlikely to keep an environment warm between invocations of a function that fires once per hour, once per day, or once per week. Every run is a cold start. The cumulative latency cost across many functions is real.

The interesting consequence is that the standard cold start advice, which is mostly aimed at reducing user-perceived latency on rare cold starts, is not quite the right advice for scheduled work. For scheduled functions, every invocation is paying the cost. The optimization moves to a different place.

The first consideration is package size. The package size affects every cold start equally, which means it affects every invocation of a scheduled function. A bloated deployment package on a function that runs every hour is paying the bloat cost twenty-four times a day. The same bloat on a request-driven function might only matter on the rare cold start. Trimming the package on the scheduled function is the highest-leverage optimization, more so than for request-driven workloads.

The second consideration is initialization work. Scheduled functions often have meaningful work to do at module load time. Database connections, configuration loading, dependency injection setup. The team writes the same patterns they write for long-running services, where the init cost is amortized over many requests. On a scheduled function, the init cost is paid every run, with no amortization. The pattern that works for request-driven services is the wrong pattern for scheduled ones, and the fix is to make the init lighter or to defer it until first use within the run.

The third consideration is provisioned concurrency, which has different economics for scheduled work than for request-driven work. Provisioned concurrency keeps environments warm. For a request-driven function with sustained traffic, this is often a good investment. For a scheduled function that fires once an hour, keeping an environment warm between invocations costs more than the cold start saves. Provisioned concurrency is rarely the right answer for purely scheduled functions.

The fourth consideration is the trigger itself. Scheduled work that arrives via CloudWatch Events runs cold every time. Scheduled work that arrives via SQS or SNS, with the schedule encoded in the upstream system, can sometimes share warm environments across closely-spaced invocations. For high-frequency scheduled work, the trigger choice can be a meaningful cold start optimization that the team would not consider if they were thinking about the function in isolation.

The fifth consideration is whether the work needs to be on serverless at all. Some scheduled work runs continuously enough that a small always-on container is cheaper and faster than a serverless function that pays for cold start on every invocation. The break-even is workload-specific, but the team that sees high-frequency scheduled work paying cold start costs every minute is paying for a model that does not fit. A migration to a different runtime is sometimes the right answer.

The implication for monitoring teams is that the cold start metrics for scheduled functions deserve their own dashboard. The percentile-based view that works for request-driven cold starts does not apply, because scheduled work is mostly cold. The metric that matters is total invocation duration including cold start, with the cold start portion broken out, and a target that the team has explicitly chosen.

For a team running a fleet of scheduled functions that has not thought about cold starts in this framing, the working pattern is to identify the scheduled functions whose cold start contribution is more than a small fraction of the total invocation time, and to address the ones where the cumulative cost across the year is meaningful. The work is bounded, the savings are predictable, and the architecture often becomes cleaner as a side effect.


This is a guest post from the team at Modern Serverless, who run engineering engagements for teams operating Lambda, Cloud Run, and Azure Functions in production. The work covers cold start tuning, cost rationalization, migrations, and on-call coverage.