Todo lo que necesitas saber sobre cold starts en Lambda y las estrategias para reducir su impacto.
La primera vez que me topé con un cold start, no tenía idea de qué estaba pasando.
Había terminado de deployar una API con Lambda para un proyecto en el que estaba trabajando. Todo funcionaba. Los tests pasaban. El endpoint respondía rápido. Estaba feliz.
Hasta que un compañero me escribió: "Oye, a veces la API tarda como 3 segundos en responder. ¿Es normal?"
Mi primera reacción fue: "No puede ser, a mí me responde en 200ms". Así que abrí CloudWatch, empecé a revisar los logs... y ahí lo vi. Un INIT duration de 2.8 segundos que aparecía de vez en cuando, sin patrón aparente. No había error. No había timeout. Solo... lentitud.
Pasé un buen rato buscando qué significaba ese INIT. ¿Era un bug? ¿Algo que configuré mal? ¿Un problema de red?
No. Era un cold start.
Y cuando entendí qué era y por qué pasaba, todo empezó a tener sentido. Esas peticiones lentas no eran aleatorias. Eran el precio de no tener servidores corriendo 24/7.
Si alguna vez sentiste que tu función Lambda "se dormía" y tardaba en despertar, este artículo es para ti. Te voy a contar todo lo que aprendí sobre cold starts: qué son, por qué ocurren, y las estrategias que uso para reducir su impacto.
¿Qué es un cold start?
Cuando invocas una función Lambda, AWS necesita un lugar donde ejecutar tu código. Ese lugar se llama execution environment: un contenedor aislado con su runtime, tu código, y las dependencias que necesita.
Si ya existe un environment disponible (porque tu función se ejecutó hace poco), Lambda lo reutiliza. Eso es un warm start. Rápido. Sin sorpresas.
Pero si no hay ninguno disponible — porque es la primera invocación, porque pasó mucho tiempo sin actividad, o porque hay un pico de tráfico y Lambda necesita crear más environments — entonces tiene que crear uno nuevo desde cero.
Eso es un cold start.

Y crear un environment desde cero no es instantáneo. Lambda tiene que hacer varias cosas antes de que tu código empiece a ejecutarse:
- Container Provisioning: asignar los recursos de cómputo según la memoria configurada
- Runtime Initialization: cargar el runtime del lenguaje (Python, Node.js, Java, etc.)
- Function Code Loading: descargar y desempaquetar tu código
- Dependency Resolution: cargar las librerías y paquetes que tu función necesita
Todo eso es la fase de inicialización (Init phase). Y esa latencia adicional es lo que llamamos cold start.

La buena noticia: el cold start solo ocurre una vez por ciclo de vida del environment. Una vez que el environment está listo, las siguientes invocaciones son warm starts.
La otra buena noticia: según AWS, los cold starts típicamente afectan menos del 1% de las invocaciones. Pero ese 1% puede ser un problema si tu aplicación es sensible a la latencia.
¿Por qué ocurren?
Los cold starts no son un bug. Son una consecuencia natural de cómo funciona el modelo serverless.
Eficiencia de recursos: no pagas cuando tu código no se ejecuta. Eso es genial para tu billetera, pero significa que Lambda apaga los environments que llevan tiempo sin usarse. Cuando llega una nueva invocación, tiene que crear uno nuevo.
Aislamiento de seguridad: cada execution environment es independiente. No comparte memoria ni estado con otros. Esa seguridad requiere crear un environment fresco cada vez.
Auto-scaling: cuando llega un pico de tráfico, Lambda crea nuevos environments para manejar la carga. Cada environment nuevo pasa por la fase de inicialización.
En resumen: los cold starts son el precio que pagas por no tener que administrar servidores. Y en la mayoría de los casos, es un precio muy bajo.
Pero cuando sí importa — APIs de usuario, microservicios interactivos, flujos donde cada segundo cuenta — necesitas saber cómo reducirlos.
Factores que influyen en la duración
No todos los cold starts son iguales. Algunos duran 100 milisegundos. Otros duran 5 segundos. La diferencia depende de varios factores.
Runtime
Los lenguajes interpretados como Python y Node.js inicializan más rápido que los compilados como Java o .NET.
¿Por qué? Java necesita cargar la JVM y hacer JIT compilation. .NET tiene un proceso similar. Python y Node.js simplemente cargan el intérprete y empiezan a ejecutar.
Eso no significa que debas elegir tu lenguaje solo por el cold start. Pero es un factor a considerar si la latencia es crítica.
Tamaño del paquete
Más grande el paquete = más tiempo para descargarlo, descomprimirlo e inicializarlo.
Si tu función incluye dependencias que no usa, estás pagando cold start por código que nunca se ejecuta. Un paquete de 5 MB inicializa mucho más rápido que uno de 250 MB.
Memoria asignada
Esto es contraintuitivo: más memoria = cold starts más rápidos.
¿Por qué? Porque Lambda asigna CPU proporcionalmente a la memoria. Más memoria significa más CPU, y más CPU significa que la inicialización se completa más rápido.
A veces, subir la memoria de 128 MB a 512 MB reduce el cold start significativamente, y el costo adicional es mínimo porque la función termina más rápido.
Configuración de red (VPC)
Si tu función está dentro de una VPC, Lambda necesita crear una Elastic Network Interface (ENI) para conectarla a la red. Eso agrega latencia al cold start.
AWS mejoró esto significativamente con Hyperplane, que redujo la latencia de VPC de segundos a milisegundos. Pero sigue siendo un factor si tu función necesita acceso a recursos dentro de una VPC.
Inicialización estática
Todo el código que se ejecuta fuera del handler se ejecuta durante la fase Init. Si estás importando 15 librerías, creando conexiones a bases de datos, y cargando archivos de configuración... todo eso suma al cold start.
# Todo esto se ejecuta durante el Init (cold start)
import pandas as pd
import numpy as np
import boto3
from langchain import LLMChain
s3_client = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('my-table')
def lambda_handler(event, context):
# Esto se ejecuta en cada invocación (warm o cold)
...
La inicialización estática es un arma de doble filo: quieres inicializar cosas fuera del handler para reutilizarlas en warm starts, pero todo lo que pongas ahí aumenta el cold start.
Estrategias de optimización (sin costo adicional)
Antes de llegar a las soluciones de AWS que cuestan dinero, hay varias cosas que puedes hacer gratis.
1. Importa solo lo necesario
# ❌ Importa todo el SDK
import boto3
# ✅ Importa solo lo que necesitas
from boto3 import client
s3 = client('s3')
2. Mantén tus paquetes pequeños
Elimina dependencias que no uses. Usa tree shaking en Node.js. Excluye archivos de tests, documentación, y ejemplos de tus paquetes de deployment.
3. Inicializa clientes fuera del handler
# ✅ Se inicializa una vez y se reutiliza en warm starts
s3_client = boto3.client('s3')
def lambda_handler(event, context):
response = s3_client.get_object(Bucket='my-bucket', Key='my-key')
...
4. Asigna memoria adecuada
No te quedes con los 128 MB por defecto si tu función carga dependencias pesadas. Experimenta con 256 MB, 512 MB, o más. Usa AWS Lambda Power Tuning para encontrar el balance óptimo entre costo y rendimiento.
5. Usa layers para dependencias compartidas
Si varias funciones usan las mismas dependencias, empaquétalas en un Lambda Layer. Esto no reduce el cold start directamente, pero te ayuda a mantener paquetes organizados y más fáciles de optimizar.
Provisioned Concurrency: environments siempre calientes
Si las optimizaciones de código no son suficientes, AWS tiene dos soluciones dedicadas. La primera es Provisioned Concurrency.
La idea es simple: le dices a Lambda "mantén X environments siempre listos". Lambda los pre-inicializa y los mantiene calientes, esperando invocaciones.
Resultado: cero cold starts para esas invocaciones. Respuestas en milisegundos de doble dígito.
# Configurar 10 environments siempre listos
aws lambda put-provisioned-concurrency-config \
--function-name my-function \
--qualifier prod \
--provisioned-concurrent-executions 10
Cuándo usarlo
- Tu aplicación tiene requisitos estrictos de latencia (APIs de usuario, microservicios interactivos)
- El tráfico es predecible o puedes configurar auto-scaling
- Estás dispuesto a pagar por environments que están listos aunque no se usen
Cuándo NO usarlo
- Tráfico esporádico o impredecible (vas a pagar por environments idle)
- Workloads asíncronos donde la latencia no es crítica (procesamiento de datos, colas)
- Tu presupuesto es ajustado
Nota importante: Provisioned Concurrency tiene un costo adicional. Pagas por cada environment provisionado, esté o no procesando invocaciones. Pero si la latencia es crítica para tu negocio, el costo vale la pena.
También puedes combinar Provisioned Concurrency con Application Auto Scaling para ajustar automáticamente la cantidad de environments según el tráfico. Puedes usar scheduled scaling (para patrones predecibles) o target tracking (para ajuste dinámico).
SnapStart: snapshots del environment inicializado
La segunda solución es Lambda SnapStart. Y funciona de una manera completamente diferente.
En lugar de mantener environments calientes, SnapStart toma una foto (snapshot) del environment después de que se inicializa. Cuando llega una invocación, en lugar de inicializar desde cero, Lambda restaura el environment desde esa foto.
El resultado: cold starts que bajan de varios segundos a sub-segundo.
Runtimes soportados
- Java 11+
- Python 3.12+
- .NET 8+
Cuándo usarlo
- Funciones con inicialización pesada (dependencias grandes, frameworks, modelos ML)
- Invocaciones frecuentes a escala
- Quieres reducir cold starts sin el costo de Provisioned Concurrency
Cuándo NO usarlo
- Funciones que ya inicializan en milisegundos (no vas a notar la diferencia)
- Necesitas latencia garantizada de doble dígito de milisegundos (usa Provisioned Concurrency)
- Tu función usa EFS, Provisioned Concurrency, o almacenamiento efímero mayor a 512 MB (no es compatible)
SnapStart es especialmente interesante para Python, donde cargar dependencias como Pandas, NumPy, LangChain, o frameworks como Flask y Django puede tomar varios segundos.
👉 En el próximo artículo de esta serie, vamos a profundizar en SnapStart para Python: cómo funciona internamente, cómo activarlo paso a paso, cómo usar runtime hooks, y las mejores prácticas para sacarle el máximo provecho.
¿Cuál estrategia elegir?
No es "una u otra". Puedes (y deberías) combinarlas.
| Estrategia | Latencia | Costo adicional | Ideal para |
|---|---|---|---|
| Optimización de código | Reduce el Init | Ninguno | Siempre. Es el primer paso |
| SnapStart | Sub-segundo | Costo de cache + restauración | Funciones con init pesado a escala |
| Provisioned Concurrency | Milisegundos | Pago por environment provisionado | Latencia ultra-baja garantizada |
La recomendación:
- Siempre optimiza tu código primero. Es gratis y ayuda en todos los escenarios.
- Si después de optimizar sigues teniendo cold starts largos y tu función se invoca frecuentemente, prueba SnapStart.
- Si necesitas latencia garantizada de milisegundos sin excepciones, usa Provisioned Concurrency.
📊 Árbol de decisión (haz clic para expandir)
Monitoreo: cómo saber si tienes un problema
Antes de optimizar, necesitas medir. No asumas que tienes un problema de cold starts solo porque "a veces se siente lento".
Si quieres ir más a fondo en el tema de diagnóstico y optimización de rendimiento en Lambda, te recomiendo este artículo donde comparto mi experiencia: Eleva el rendimiento de AWS Lambda. Ahí cubro métricas de invocación, benchmarks, y estrategias prácticas para llevar tus funciones al siguiente nivel.
Lo que aprendiste
Los cold starts son parte natural del modelo serverless. No son un bug, son un trade-off por no administrar servidores. Afectan menos del 1% de las invocaciones, pero cuando importan, importan mucho.
La duración depende del runtime, el tamaño del paquete, la memoria asignada, y la configuración de red. Optimizar el código es siempre el primer paso y no cuesta nada. Provisioned Concurrency y SnapStart son las dos soluciones de AWS cuando necesitas ir más allá.
Qué sigue
Ya entiendes qué son los cold starts y las estrategias para combatirlos. En el próximo artículo vamos a profundizar en Lambda SnapStart para Python: cómo funciona por debajo (Firecracker snapshots, cache en dos niveles), cómo activarlo paso a paso, cómo usar runtime hooks, y las mejores prácticas para sacarle el máximo provecho a tus funciones Python.
Código real. Configuración real. Resultados reales.
¿Te resultó útil este artículo? Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya estás lidiando con cold starts en producción, me encantaría escuchar cómo los resolviste.


Top comments (0)