Lambda cold starts are often a performance bottleneck, but you can shrink them to under 100ms by optimizing your function’s initialization phase.

Let’s see a Node.js Lambda function in action. This example demonstrates a common scenario where we fetch data from an external API during initialization.

// index.js
const axios = require('axios');
const API_URL = 'https://api.example.com/data';
let cachedData = null;

// Initialization phase (runs on cold start)
async function initialize() {
  console.log('Initializing function...');
  try {
    const response = await axios.get(API_URL);
    cachedData = response.data;
    console.log('Data cached successfully.');
  } catch (error) {
    console.error('Error fetching initial data:', error);
    // Handle error appropriately, maybe throw or set default data
    throw error;
  }
}

// Handler function (runs on every invocation)
exports.handler = async (event) => {
  if (!cachedData) {
    // This block would only run if initialization failed and wasn't retried
    await initialize();
  }

  console.log('Function invoked. Using cached data.');
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Data retrieved successfully!',
      data: cachedData,
    }),
  };
};

// Trigger initialization immediately if not already done
initialize();

When this function is invoked for the first time (a cold start), Lambda will:

  1. Download your function’s deployment package.
  2. Start a new execution environment (a container).
  3. Run your initialization code (initialize() function in this case). This includes importing dependencies (axios) and making external API calls.
  4. Execute your handler function (exports.handler).

The time taken for steps 1-3 is the cold start duration. Subsequent invocations within a short period will reuse the existing execution environment (a warm start), skipping the initialization phase and resulting in much faster response times.

The problem Lambda cold starts solve is resource provisioning and management. Instead of having a server running 24/7, Lambda spins up resources only when needed. This is cost-effective for spiky or infrequent workloads but introduces latency for the first request.

The core levers you control are:

  • Runtime choice: Some runtimes (like Node.js, Python) generally have faster initialization than others (like Java, .NET).
  • Dependency size: Large libraries or many dependencies increase download and initialization time.
  • Initialization logic complexity: Expensive operations (network calls, heavy computation) in your initialization code directly add to cold start time.
  • Memory allocation: More memory often means more CPU, which can speed up initialization.
  • Provisioned Concurrency: This feature keeps a specified number of execution environments pre-initialized and ready, eliminating cold starts for those environments.

The actual initialization of a Lambda function involves more than just running your code. Lambda first provisions the underlying infrastructure, downloads your code package, and then executes your initialization code. The time spent in "downloading your code package" is a significant, often overlooked, part of the cold start. Even if your code initializes instantly, a large deployment package will still contribute to latency.

The next conceptual hurdle is understanding how Lambda’s internal networking and VPC integration can also contribute to cold start times, particularly when your function is configured to run within a Virtual Private Cloud.

Want structured learning?

Take the full Lambda course →