← Todo el blog

Ejecutar una app web en contenedor sobre AWS Lambda

Cómo md2docx pasó de un contenedor en un VPS a serverless en AWS por ~0 $ — sin cambiar una línea de código de la app, más las tres trampas de las que nadie te avisa.

  • AWS
  • Serverless
  • Lambda

md2docx funcionaba feliz como contenedor Docker en un VPS. Lo migré a serverless en AWS por dos razones: pagar ~0 $ mientras nadie lo usa y aprender la plataforma de verdad. La sorpresa fue que el código de la aplicación no cambió en absoluto. La fricción estaba en todo lo demás.

La app no cambia — lo hace un adaptador

Lambda habla de eventos; Express habla HTTP. El AWS Lambda Web Adapter une ambos: pon su binario en la imagen del contenedor como extensión de Lambda y traduce cada invocación en una petición HTTP normal a tu servidor en localhost. El Dockerfile gana una línea; el servidor queda exactamente como corre en el portátil. Construí la imagen para arm64 (Graviton — un 20 % más barato), la subí a ECR y dejé que Lambda la sirviera tras una Function URL integrada.

Tres trampas

Entonces la plataforma se resistió.

  • La región no soportaba Function URLs. Crear una fallaba con AccessDeniedException: Unable to determine service/operation name to be authorized — que grita «bug de permisos» pero en realidad significa «función no disponible en esta región». Una región más nueva simplemente no la tenía. Solución: usar una región tier-1.
  • Las cuentas nuevas de AWS bloquean las Function URLs públicas por defecto. Incluso con una resource policy de manual que concede lambda:InvokeFunctionUrl a todos, cada petición devolvía 403 Forbidden. El arreglo, que se siente no documentado: conceder también lambda:InvokeFunction a *.
  • CloudFront delante, pero OAC no firma los cuerpos POST. Para forzar el tráfico por el CDN probé Origin Access Control con auth IAM — y POST se rompió al instante, porque OAC no incluye el cuerpo de la petición en su firma SigV4 y Lambda rechaza payloads sin firmar. Para una app cuyo único trabajo es hacer POST de Markdown, eso es fatal. Cambié a un secreto compartido: CloudFront inyecta una cabecera y la app rechaza todo lo que no la lleve.

Cuánto cuesta

Lambda y CloudFront tienen free tiers perpetuos — no los créditos de seis meses — de 1 millón de peticiones + 400 000 GB-segundos y 1 TB de salida al mes. Un conversor de hobby vive dentro con margen, así que la factura es prácticamente 0 $, y escala a cero cuando está inactivo. El precio es latencia: la primera petición tras un rato de calma espera unos segundos por el arranque en frío. Para una herramienta de uso ocasional, es un buen trato.

Serverless no hizo la app más simple — la hizo gratis en reposo y convirtió su operación en problema de otro. La pega nunca fue el código; fueron la docena de pequeños valores por defecto a su alrededor.