Cold Starts en Serverless: Causas, Costos y Mitigación
Los cold starts son el costo oculto del serverless: qué los provoca, cuánto cuestan en latencia y estrategias prácticas para mantener las funciones activas.

La primera invocación de una función serverless después de un período de inactividad tarda considerablemente más que las siguientes llamadas. Este "cold start" incluye aprovisionar un contenedor, cargar el runtime, inicializar las dependencias y ejecutar el código de inicialización. En AWS Lambda, un cold start puede agregar desde 100 ms hasta varios segundos, según el runtime elegido, el tamaño del paquete y la configuración de VPC.
Qué sucede durante un cold start
Una invocación serverless sigue este recorrido: la plataforma recibe la solicitud, verifica si existe un contenedor activo y, si no hay ninguno, aprovisiona uno nuevo. El camino frío incluye descargar el paquete de implementación, iniciar el runtime y ejecutar el código a nivel de módulo antes de que el handler pueda procesar la solicitud.
// Everything outside the handler runs during cold start
import { DynamoDB } from "@aws-sdk/client-dynamodb"; // ~200ms import
import { S3Client } from "@aws-sdk/client-s3"; // ~150ms import
const dynamodb = new DynamoDB({}); // Connection initialization
const s3 = new S3Client({});
// This handler runs on EVERY invocation (warm or cold)
export const handler = async (event: APIGatewayEvent) => {
// Handler code here
return { statusCode: 200, body: "OK" };
};Cómo medir el impacto de un cold start
La diferencia entre las respuestas frías y activas se puede medir con logging estructurado.
// ❌ No visibility into cold starts
export const handler = async (event: unknown) => {
const result = await processRequest(event);
return result;
};
// ✅ Track cold starts explicitly
let isFirstInvocation = true;
export const handler = async (event: unknown) => {
const start = Date.now();
const isCold = isFirstInvocation;
isFirstInvocation = false;
const result = await processRequest(event);
console.log(JSON.stringify({
coldStart: isCold,
duration: Date.now() - start,
functionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
memoryAllocated: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE,
}));
return result;
};Estrategia de mitigación 1: reducir el tamaño del paquete
El paquete de implementación debe descargarse y extraerse antes de que el runtime se inicie. Los paquetes más pequeños significan cold starts más rápidos.
// ❌ Importing the entire AWS SDK v2 (~70MB)
import AWS from "aws-sdk";
const dynamodb = new AWS.DynamoDB();
// ✅ Import only what you need (SDK v3 modular imports ~3MB)
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
const client = new DynamoDBClient({});El tree-shaking y el bundling del código de la función con esbuild u herramientas similares pueden reducir drásticamente el tamaño del paquete:
# Bundle with esbuild — output is a single file with only used code
npx esbuild src/handler.ts \
--bundle \
--platform=node \
--target=node18 \
--outfile=dist/handler.js \
--minify \
--external:@aws-sdk/* # SDK v3 is included in Lambda runtimeEstrategia de mitigación 2: inicialización diferida (lazy initialization)
No todas las invocaciones necesitan todas las dependencias. Los recursos costosos deben inicializarse solo cuando la ruta de código realmente los requiera.
// ❌ Always initializing S3 even if most invocations don't use it
const s3 = new S3Client({});
const dynamodb = new DynamoDBClient({});
export const handler = async (event: APIGatewayEvent) => {
if (event.path === "/upload") {
await s3.send(new PutObjectCommand(uploadParams));
}
// 90% of invocations only use DynamoDB
return dynamodb.send(new GetItemCommand(params));
};
// ✅ Lazy initialization — S3 client created only when needed
const dynamodb = new DynamoDBClient({});
let _s3: S3Client | null = null;
function getS3Client(): S3Client {
if (!_s3) _s3 = new S3Client({});
return _s3;
}
export const handler = async (event: APIGatewayEvent) => {
if (event.path === "/upload") {
await getS3Client().send(new PutObjectCommand(uploadParams));
}
return dynamodb.send(new GetItemCommand(params));
};Estrategia de mitigación 3: provisioned concurrency
Para las rutas sensibles a la latencia, provisioned concurrency mantiene siempre activa una cantidad específica de instancias de la función, lo que elimina por completo los cold starts a costa de pagar por cómputo inactivo.
# serverless.yml — provisioned concurrency configuration
functions:
api:
handler: dist/handler.handler
memorySize: 512
provisionedConcurrency: 5 # 5 instances always warm
events:
- http:
path: /api/{proxy+}
method: ANYEstrategia de mitigación 4: selección de memoria y runtime
Asignar más memoria aumenta la CPU de forma proporcional, lo que acelera la inicialización.
// CloudFormation / SAM template
// Doubling memory from 128MB to 256MB can cut cold start time in half
// The extra cost per invocation is often offset by faster execution
// Runtime comparison (approximate cold start overhead):
// Python 3.x: ~200ms
// Node.js 18.x: ~250ms
// Go 1.x: ~100ms (compiled binary)
// Java 17: ~800ms (JVM startup)
// .NET 6: ~400ms
// Choose your runtime based on cold start toleranceCold starts en VPC
Conectar una función Lambda a una VPC históricamente agregaba entre 5 y 10 segundos a los cold starts debido a la creación de la ENI (Elastic Network Interface). AWS mejoró esto con Hyperplane ENI, pero las funciones dentro de una VPC todavía presentan mayor latencia de cold start. Conviene conectar una función a una VPC solo cuando realmente necesite acceder a recursos dentro de ella.
Puntos clave
- Los cold starts son sobrecarga de inicialización — el aprovisionamiento del contenedor, el arranque del runtime y la carga de módulos contribuyen a ella
- Reducir el tamaño del paquete — empaquetar y aplicar tree-shaking a las dependencias, usar imports modulares del SDK
- Inicializar de forma diferida los recursos costosos — crear los clientes solo para las rutas de código que los necesitan
- Provisioned concurrency elimina los cold starts — a costa de pagar por cómputo siempre activo
- Más memoria implica una inicialización más rápida — la CPU escala de forma lineal con la memoria asignada
- Evitar las VPC salvo que sea necesario — conectar una función a una VPC sigue agregando una sobrecarga medible de cold start


