Back to Blog
Guide7 min read2026-05-01

Serverless Cold Starts: Causes, Impact, and How to Reduce Them

Cold starts are the most cited downside of serverless functions — this guide explains what causes them, how they affect your users, and practical steps to minimize their impact.

What Is a Cold Start?

A cold start occurs when a serverless function is invoked and no existing runtime container is available to handle it. The platform must create a new container, load your runtime (Node.js, Python, etc.), initialize your code, and then execute the function. This initialization phase adds latency — anywhere from a few hundred milliseconds to several seconds depending on the platform and runtime.

Contrast this with a warm start: the container from a previous invocation is still alive, and the platform routes the new request directly to it. Warm starts are fast — often under 10ms of platform overhead.

Why Cold Starts Happen

Cold starts occur when:

  1. 1The function has never been invoked — fresh deployment with no prior invocations
  2. 2Traffic spikes — demand exceeds the number of warm containers, so new ones must be created
  3. 3A container has been idle too long — platforms recycle containers after a period of inactivity
  4. 4A new version was deployed — existing warm containers run the old version; new containers must be created for the updated code

Measuring Cold Start Impact

The cold start penalty varies by:

  • Runtime — Node.js and Python are generally faster to initialize than Java or .NET
  • Bundle size — Larger packages mean longer loading time
  • Dependencies — Heavy initialization (database connections, config loading) adds to startup time

For user-facing API endpoints where p99 latency matters, a 500ms cold start on an otherwise 50ms function is a 10× latency spike. For background processing or webhooks, the same cold start is usually imperceptible.

Writing Cold-Start-Friendly Functions

Keep your function small

The most effective way to reduce cold start time is to reduce what needs to be loaded. Only import what you need:

// ❌ Avoid: importing entire libraries when you need one function
const _ = require('lodash');
const result = _.get(obj, 'a.b.c');

// ✅ Better: targeted import or native alternative
const result = obj?.a?.b?.c;

Move initialization outside the handler

Code outside the main function runs once when the container starts and is reused on warm invocations. Use this for expensive setup:

// config.js — database client initialized once per container
const { Pool } = require('pg');

// Runs once on cold start, reused on warm starts
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const main = async (params) => {
  const { userId } = params;
  const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
  return {
    statusCode: 200,
    body: JSON.stringify({ user: result.rows[0] })
  };
};

module.exports = { main };

The same pattern applies in Python:

# handler.py
import json
import psycopg2
import os

# Initialized once per container lifetime
conn = psycopg2.connect(os.environ['DATABASE_URL'])

def main(params):
    user_id = params.get('userId')
    if not user_id:
        return {'statusCode': 400, 'body': json.dumps({'error': 'userId required'})}

    cursor = conn.cursor()
    cursor.execute('SELECT id, email FROM users WHERE id = %s', (user_id,))
    row = cursor.fetchone()

    if not row:
        return {'statusCode': 404, 'body': json.dumps({'error': 'Not found'})}

    return {'statusCode': 200, 'body': json.dumps({'id': row[0], 'email': row[1]})}

Minimize cold initialization work

Defer work that is not needed on every invocation:

let configCache = null;

const getConfig = async () => {
  if (configCache) return configCache; // warm: instant
  configCache = await fetchRemoteConfig(); // cold: one-time fetch
  return configCache;
};

const main = async (params) => {
  const config = await getConfig();
  // use config...
};

module.exports = { main };

Keeping Functions Warm

The most direct way to eliminate cold starts for critical functions is to keep the container alive with periodic invocations.

PandaStack's cronjob system is perfect for this. Set up a cronjob to invoke your function every 5 minutes:

panda cronjobs create   --name keep-warm-hello-fn   --schedule "*/5 * * * *"   --image curlimages/curl   --command "curl -s https://functions.pandastack.io/api/v1/namespaces/_/actions/hello-fn"

This keeps the container warm without requiring any changes to your function code.

Architectural Strategies

  • Decouple cold-start-sensitive paths — use warm containers for user-facing endpoints; accept cold starts in background jobs
  • Batch invocations — fewer, larger requests mean fewer container creations per unit of work
  • Use cronjobs as warmers — PandaStack cronjobs can ping any HTTP endpoint on any schedule

Conclusion

Cold starts are real, but they are manageable. Write lean functions, initialize resources outside your handler, and use PandaStack cronjobs to keep critical functions warm. For most workloads, these strategies bring cold start impact well within acceptable thresholds. Manage your functions and cronjobs at dashboard.pandastack.io.

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also