DevOps 5 min read 926 words

AWS Lambda now runs plain HTTP servers: meet Web Functions

ES
AWS Lambda now runs plain HTTP servers: meet Web Functions

Two weeks ago I wrote about how Netlify swapped V8 isolates for Firecracker microVMs in its Edge Functions, and I closed by saying microVMs were becoming the default for running third-party code, with Lambda as an example. Today AWS published Announcing AWS Lambda Web Functions, and it’s a change to the programming model more than to the infrastructure: Lambda can now run a plain HTTP server, the kind that listens on a port, without rewriting it as an event handler.

It might sound minor, but anyone who has put an Express or Next.js app on Lambda knows it has never been as straightforward as it should be.

The usual problem

A classic Lambda function receives an event, processes it and returns a response. For an HTTP API, that means API Gateway (or a Function URL) translates each request into a JSON object, passes it to your function, and your function has to return another JSON with the status code, headers and body.

If you already had a web app built with a framework, you had to put an adapter in between: serverless-http, Lambda Web Adapter or whatever your framework shipped. It worked, but the app was no longer the same one you ran locally, and every framework had its quirks.

There was a second problem too, cost: each execution environment handled one request at a time and you paid for the total duration, including the time the function spent waiting on the database or another API. For a web app, which spends most of its time waiting on I/O, that’s paying for doing nothing.

Three ways to use Lambda

With this announcement, AWS reorganises Lambda into three form factors, all on the same Firecracker isolation:

Form factorModelBillingMeant for
Event FunctionsOne event per environmentTotal durationQueues, events from other services, async processing
Web FunctionsHTTP server with concurrent requestsActive CPU + memoryWeb apps, APIs, SSR
MicroVMsStateful environment with its own lifecycle—Running untrusted or user-generated code

Event Functions are the Lambda functions we’ve always had, getting a new name to tell them apart. MicroVMs would deserve an article of their own: they’re exactly what sandboxes for AI agents are after, the same idea as Vercel Sandbox.

How Web Functions work

The key point is that the code is the usual code. The example in the announcement is an Express server with nothing Lambda-specific:

import express from 'express';

const app = express();
const [host, port] = process.env.AWS_LAMBDA_HTTP_ENDPOINT.split(':');

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Lambda Web Functions!' });
});

app.listen(port, host, () => {
  console.log(`Server running on port ${port}`);
});

The only difference is the AWS_LAMBDA_HTTP_ENDPOINT variable, which Lambda fills in with the host and port the server must listen on. Locally you just set it yourself: AWS_LAMBDA_HTTP_ENDPOINT=127.0.0.1:3000 node index.js.

Deploying is a single command, with nothing to create beforehand:

aws lambda-web deploy --name my-web-app --code ./my-app

The CLI bundles the code, creates the function, publishes a first revision (an immutable snapshot of the code) and attaches an endpoint with an HTTPS URL. According to the post, all in under a minute.

What I liked most:

  • Up to 128 concurrent requests per environment. A traffic burst is shared across environments that are already warm, instead of starting a new one per request. Fewer cold starts and much more sensible memory usage.
  • You pay for the CPU you actually use. Time spent waiting on a query or another API call barely uses CPU, so it barely costs anything. Memory is billed for as long as the environment exists. For SSR, APIs or endpoints streaming tokens from an AI model, the difference can be significant.
  • Multi-region with one flag. With --regions us-east-1,eu-west-1,ap-northeast-1 it replicates the code to all three regions and sets up latency-based routing behind a single global URL.
  • Canary deployments without extra tooling. An endpoint can split traffic between revisions, say 90% to the current one and 10% to the new one, with a single API call.
  • CloudFront integration. An endpoint can be a CloudFront origin and be protected with origin access control, just like an S3 bucket, so only your distribution can call it. Static content comes from the cache and dynamic requests go to the function.

The limitations

For now there’s only Node.js 24 on Arm64, in five regions: N. Virginia, Ohio, Oregon, Tokyo and Ireland. It can connect to a VPC, so the function can reach private databases. The announcement gives no prices; it points to the Lambda pricing page.

Starting with Node.js only makes sense, since that’s where Next.js, Hono, Fastify and friends live, but the “listen on a port” model has nothing JavaScript-specific about it. I’d expect Python, Go or PHP to follow soon.

My takeaway

Reading the announcement, the comparison that comes to mind is Cloud Run, which has had this model for years: a container listening on a port, several requests per instance, and billing only while it’s serving. AWS is late to this idea, but it arrives with its own strengths: Firecracker startup times, multi-region deployment with one flag and CloudFront integration.

And I think it’s good news that Lambda adapts to the code and not the other way round. For years, “serverless” has in practice meant “rewrite your app to fit our model”. With Web Functions, the app you run locally with node index.js is the same one you deploy, which makes it much easier to try Lambda and, if it doesn’t convince you, take the app somewhere else.

Source: Announcing AWS Lambda Web Functions, AWS News Blog.