NestJS en AWS Lambda con CDK y GitHub Actions 2026
Desplegar NestJS en AWS Lambda usando CDK y GitHub Actions requiere adaptar el entry point de la app, definir infraestructura como código con AWS CDK (Lambda + HTTP API Gateway), y automatizar el deploy mediante un workflow de GitHub Actions con autenticación OIDC. Publicado originalmente el 30 de marzo de 2026 por Ajeet Chaulagain en dev.to.
En 30 segundos
- NestJS necesita un handler.ts separado del main.ts para correr en Lambda, usando
@codegenie/serverless-expresspara adaptar el framework al modelo de ejecución serverless. - AWS CDK define la infraestructura como código: función Lambda + HTTP API Gateway, con bundling via esbuild y variables de entorno configurables.
- GitHub Actions automatiza el deploy en cada push a main, autenticándose con AWS mediante OIDC (sin secrets hardcodeados).
- Los cold starts son el problema más visible con NestJS en Lambda: la inicialización del módulo DI tarda entre 800ms y 2s en el primer request.
- Errores comunes: nest-cli.json mal configurado, permisos IAM insuficientes, y DTO validation que falla en Lambda por diferencias en el entorno de ejecución.
¿Por qué desplegar NestJS en AWS Lambda?
AWS Lambda es una función serverless: vos subís el código, AWS lo ejecuta cuando llega un request, y pagás por el tiempo de ejecución real, no por un servidor corriendo 24/7. Para APIs de bajo tráfico, webhooks o microservicios con picos esporádicos, el ahorro puede ser considerable comparado con tener una instancia EC2 o un contenedor en ECS siempre encendido.
El caso de uso clásico: tenés una API REST para un proyecto personal o una herramienta interna que recibe cien requests por día. Con EC2 o un VPS en donweb.com pagás por el servidor aunque esté idle el 90% del tiempo. Con Lambda, pagás prácticamente nada hasta que el tráfico escale. El escalado además es automático, sin intervención manual.
Lo que no te conviene: si la app tiene tráfico constante y alto, Lambda sale más caro y los cold starts molestan. Para eso, EC2 o ECS tienen mejor costo-beneficio.
Requisitos y configuración inicial
Antes de escribir una sola línea de infraestructura, necesitás tener instalado y configurado:
- Node.js 20+ y NestJS CLI (
npm i -g @nestjs/cli) - AWS CLI configurado con credenciales de un usuario/rol IAM con permisos de CloudFormation, Lambda, API Gateway y S3
- AWS CDK instalado globalmente (
npm i -g aws-cdk) y bootstrapped en tu cuenta (cdk bootstrap) - Cuenta de GitHub para el repositorio y GitHub Actions
Una vez creado el proyecto con nest new nestjs-serverless-aws-cdk, instalás las dependencias específicas para serverless: Cubrimos ese tema en detalle en estrategias SEO para APIs globales.
@codegenie/serverless-express: adapta frameworks Express-compatibles (NestJS incluido) al handler de Lambdaaws-lambday@types/aws-lambda: tipos e interfaces del runtime de Lambdaaws-cdk-libyconstructs: SDK de CDK para definir infraestructura
Adaptar la aplicación NestJS para Lambda
Acá está el punto donde la mayoría se traba. NestJS arranca con main.ts, que llama NestFactory.create() y después app.listen(3000). Ese modelo no funciona en Lambda: no hay un puerto abierto esperando conexiones. Lambda recibe un evento, ejecuta el handler, y cierra.
Lo que necesitás es un lambda.ts (o handler.ts) que arranque la app NestJS, la envuelva con serverlessExpress, y exporte una función handler que Lambda pueda invocar. El truco es que la inicialización de la app se hace una sola vez y se cachea fuera del handler, así los requests subsiguientes en la misma instancia cálida no vuelven a inicializar todo el módulo DI:
El archivo queda más o menos así: creás la app con NestFactory.create(AppModule), la inicializás, y pasás la instancia a serverlessExpressHandler. Ese handler se exporta como exports.handler y Lambda lo invoca por cada request.
Después, en nest-cli.json, tenés que agregar lambda.ts como entry adicional para que el compilador lo incluya en el output. Si se te olvida este paso (y se olvida seguido), el deploy funciona pero Lambda tira “Handler not found” al invocar. (spoiler: es el error número uno que aparece en los foros)
Definir infraestructura con AWS CDK
AWS CDK es TypeScript que genera CloudFormation. Para este setup, el stack necesita dos recursos: una función Lambda y un HTTP API Gateway que la exponga. Tema relacionado: alternativas serverless a Heroku.
Un concepto que vale entender antes de avanzar: Lambdalith. La arquitectura “lambdalith” despliega toda la API NestJS como una sola función Lambda, en lugar de dividir cada endpoint en su propia función. Para proyectos medianos es lo más razonable: menos configuración, routing manejado por NestJS, y logs centralizados. La alternativa de múltiples funciones tiene sentido cuando cada endpoint tiene cargas muy diferentes o ciclos de vida distintos, pero agrega complejidad operativa que raramente vale la pena al principio.
El stack de CDK define la Lambda con bundling via esbuild (más rápido que webpack para este caso), memory de 512MB como punto de partida razonable, y timeout de 29 segundos (el máximo de API Gateway). Las variables de entorno se pasan directamente en el stack.
| Parámetro Lambda | Valor inicial recomendado | Por qué |
|---|---|---|
| Memory | 512 MB | Balance entre costo y cold start. Con 128MB los cold starts de NestJS superan los 3s. |
| Timeout | 29 segundos | Máximo de API Gateway HTTP API. |
| Runtime | nodejs22.x | LTS actual, soporte hasta 2028. |
| Architecture | arm64 | Graviton2: ~20% más rápido y ~20% más barato que x86. |

Automatizar deploys con GitHub Actions
Ponele que configuraste todo manualmente y funciona. El problema es que la próxima vez que hagas un cambio, tenés que recordar correr cdk deploy con los flags correctos, con las credenciales correctas, desde la máquina correcta. GitHub Actions resuelve eso.
El workflow vive en .github/workflows/deploy.yml y se dispara en cada push a main. Los pasos son: checkout del código, instalación de dependencias, build de NestJS, y cdk deploy --require-approval never.
Lo más importante de la configuración: usá OIDC para autenticar GitHub Actions con AWS. La alternativa es guardar AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY como secrets de GitHub, lo cual funciona pero es un vector de riesgo innecesario. Con OIDC, GitHub Actions obtiene un token temporal que AWS valida directamente, sin credenciales de largo plazo. La documentación oficial de AWS cubre el setup del Identity Provider y el rol IAM necesario.
Las variables que necesitás en el workflow: AWS_REGION, el ARN del rol IAM que GitHub va a asumir, y el nombre del stack de CDK. Todo configurable como variables de repositorio (no secrets, en este caso). Te puede servir nuestra cobertura de infraestructura como código en AWS.
Optimizar performance y cold starts
El cold start en Lambda es el tiempo que tarda la función en arrancar desde cero cuando no hay una instancia cálida disponible. Con NestJS, el sistema de inyección de dependencias agrega latencia real: entre 800ms y 2s en el primer request, dependiendo de cuántos módulos tengás.
¿Qué podés hacer? Varias cosas:
- Provisioned Concurrency: Lambda mantiene instancias pre-calentadas. Cuesta dinero, pero elimina los cold starts para esas instancias. Tiene sentido si tenés SLAs de latencia estrictos.
- Tree-shaking agresivo: esbuild con
minify: trueyexternalModulesbien configurado reduce el tamaño del bundle y mejora el tiempo de carga. - Arquitectura arm64: ya lo mencioné en la tabla, pero vale repetirlo: Graviton2 reduce cold starts además de bajar costos.
- Inicialización lazy: si tenés módulos pesados que no se usan en todos los endpoints, cargalos bajo demanda en vez de en el bootstrap.
El monitoreo va por CloudWatch. Lambda emite automáticamente métricas de duración, errores e invocaciones. Para los cold starts específicamente, buscá el campo Init Duration en los logs de CloudWatch Logs Insights.
Testing y validación en ambiente Lambda
Acá viene lo bueno: no todo lo que funciona en local va a funcionar en Lambda. Hay diferencias de entorno que te pueden romper el deploy sin advertencia previa.
Para simular Lambda localmente, sam local start-api (AWS SAM CLI) es la opción más cercana al ambiente real. Leventás la función localmente con un event de API Gateway simulado y podés hacer requests contra localhost. No es idéntico, pero te salva la mayoría de las sorpresas.
Diferencias frecuentes que generan problemas: el filesystem en Lambda es read-only salvo /tmp (512MB), las variables de entorno son las que definiste en el stack (no las de tu .env local), y el contexto de ejecución puede compartirse entre requests en la misma instancia cálida. Sobre eso hablamos en confiabilidad de tus pipelines CI/CD.
Troubleshooting: errores comunes y soluciones
Error: “Handler not found”
Lambda no encuentra la función handler exportada. Casi siempre es porque lambda.ts no está incluido en la compilación. Revisá nest-cli.json: el campo entryFile debe apuntar al archivo correcto, y si usás múltiples entry points, necesitás configurar el assets o usar el builder de esbuild directamente en CDK.
DTO validation falla en Lambda pero funciona en local
Si usás class-validator y class-transformer, necesitás que las clases estén disponibles en runtime con sus decoradores. El problema aparece cuando esbuild o webpack hacen tree-shaking agresivo y eliminan metadata que reflect-metadata necesita. Solución: agregar reflect-metadata como external en el bundler y asegurarte de que se importa al inicio del handler.
Permisos IAM insuficientes en el deploy
CDK necesita permisos amplios para crear y modificar recursos de CloudFormation. Si el rol de GitHub Actions no tiene los permisos correctos, el deploy falla con errores de AccessDenied. La política de CDK bootstrap crea roles específicos para esto; si los usás (--role-arn en el deploy), el rol de GitHub solo necesita permisos para asumir esos roles.
Cold starts que superan los 29 segundos
¿Alguien vio esto? Raramente, pero pasa si el bundle es muy grande o si la inicialización hace llamadas externas (a una base de datos, a un servicio de secretos). Revisá que la inicialización de la app esté fuera del handler y que no estés haciendo llamadas bloqueantes en el bootstrap. Secretos como contraseñas de base de datos: cargalos con AWS Secrets Manager dentro del handler, no en la inicialización global.
Preguntas Frecuentes
¿Qué es un Lambdalith y cuándo tiene sentido usarlo?
Un Lambdalith es una arquitectura donde toda la API NestJS se despliega como una sola función Lambda, en lugar de dividir cada endpoint en su propia función. Tiene sentido para la mayoría de los proyectos medianos: el routing lo maneja NestJS internamente, los logs están centralizados, y hay mucho menos overhead de configuración. La división en múltiples funciones solo vale cuando los endpoints tienen perfiles de carga muy distintos o ciclos de vida diferentes.
¿Cómo despliego una aplicación NestJS en AWS Lambda sin romper el sistema de módulos?
Creás un lambda.ts separado que inicializa la app NestJS con NestFactory.create(), la envuelve con @codegenie/serverless-express, y exporta la función handler. La clave es cachear la instancia de la app fuera del handler para que no se reinicialice en cada request. Asegurate de incluir este archivo en la compilación via nest-cli.json.
¿Por qué usar OIDC en vez de AWS Access Keys en GitHub Actions?
Con OIDC, GitHub Actions obtiene credenciales temporales de AWS por cada ejecución del workflow, sin necesidad de guardar access keys de largo plazo como secrets. Si las credenciales se filtran (por un log, por un fork, por error), las temporales expiran en minutos. Las de largo plazo pueden estar comprometidas sin que te enteres durante meses. AWS documenta el setup completo del Identity Provider y el rol IAM necesario.
¿Cómo resuelvo los cold starts en Lambda con NestJS?
Las estrategias prácticas en orden de impacto: usar arquitectura arm64 (Graviton2, ~20% más rápido), aumentar la memoria asignada (más memoria = más CPU = arranque más rápido), reducir el tamaño del bundle con tree-shaking, y si tenés SLAs estrictos, activar Provisioned Concurrency que mantiene instancias pre-calentadas a costo fijo. Un NestJS bien optimizado debería tener cold starts por debajo de 1s con 512MB en arm64.
¿Cuánto cuesta desplegar una API NestJS en AWS Lambda?
El Free Tier de AWS incluye 1 millón de invocaciones Lambda y 400.000 GB-segundos por mes. Para una API de bajo tráfico (menos de 1M requests/mes), el costo es prácticamente cero. A mayor escala, con 512MB de memoria y duración promedio de 100ms, un millón de requests adicionales cuesta alrededor de USD 0.20 en invocaciones más USD 0.40 en duración. Para APIs con tráfico constante alto, conviene comparar contra instancias EC2 t4g.small (desde USD 11/mes).
Conclusión
Desplegar NestJS en AWS Lambda con CDK y GitHub Actions no es complicado, pero tiene varios puntos de falla no obvios: el lambda.ts como entry point separado, el nest-cli.json bien configurado, y la autenticación OIDC en lugar de access keys estáticas. Según la guía publicada en marzo de 2026, con estos tres pilares bien configurados tenés una infraestructura serverless funcional, automatizada y razonablemente segura.
El patrón Lambdalith con CDK es el punto de entrada correcto para la mayoría de los proyectos. Cuando el tráfico justifique más granularidad o cuando los cold starts sean inaceptables para el caso de uso, el siguiente paso natural es Provisioned Concurrency o migrar a contenedores con ECS. Pero ese es un problema de escala que la mayoría de los proyectos no tienen al empezar.
Fuentes
- Ajeet Chaulagain en dev.to – Guía completa de deploy NestJS + CDK + GitHub Actions (marzo 2026)
- Serverless By TheoDo – NestJS on AWS Lambda: estrategia CDK para APIs monolíticas
- AWS Documentation – Deploying Lambda functions with GitHub Actions
- LogRocket Blog – NestJS serverless application con AWS DynamoDB
- GitHub – nest-js-aws-cdk-lambda-deployment: código de referencia






