SevyDevy

Guide · Backend

AWS Lambda Web Functions Explained: One Environment, Many Concurrent Requests

13 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • AWS Lambda Web Functions, launched on 12 October 2026, run a normal Node.js web server, such as an Express app, with no adapter or rewrite. Lambda is Amazon's service that runs your code without you managing servers; an adapter is a layer that turns Lambda events into HTTP requests.
  • An execution environment is one running copy of your server. A Web Function environment serves up to 128 requests at once, 64 by default; a classic Lambda environment on default compute serves one.
  • Those requests share one Node.js process, so global variables, database connection pools and the 2 GB of memory are shared too. Per-request data must never sit in a global.
  • You pay for requests, active CPU time (time the processor really works), memory while each environment exists, and data transfer. Waiting on a database costs almost no CPU.
  • Beginners can stop after the section on how one environment handles many requests. Engineers then get sizing with Little's law (requests in flight equal arrival rate times time open) and a race where many first requests each open a database pool. They also get real prices, safe rollouts, and why the default deploy creates a public endpoint.

What goes wrong when you run an Express API on classic Lambda?

Classic AWS Lambda, Amazon's service that runs your code without you managing servers, gives every open request its own copy of your server. A busy API then runs hundreds of copies that mostly sit waiting. AWS Lambda Web Functions let one copy serve many requests at once.

Picture a common setup. A team runs an Express API for office lunch orders, and orders arrive between 11:30 am and 1:30 pm. The setup is made up, but the problem is real.

The API runs on classic Lambda behind the Lambda Web Adapter, an open-source AWS tool that turns Lambda events into HTTP requests. At the peak, about 200 requests are open at once.

Classic Lambda runs one request per copy, so that means about 200 copies. Each copy opens its own PostgreSQL connections. PostgreSQL allows about 100 connections by default, so the database starts refusing new ones.

Most of each 250 ms request is spent waiting for PostgreSQL. Classic Lambda bills that wait, because it charges for the whole time your code runs. The team wants fewer copies and a smaller bill.

Which words do you need before learning Lambda Web Functions?

You need six words, each with a plain meaning.

  • Execution environment: an isolated small machine where Lambda runs one copy of your code. Here, one running Express server.
  • Concurrency: how many requests are being handled at the same moment.
  • Event Function: the new AWS name for the Lambda functions you already know, where Lambda calls your handler with one event.
  • Revision: a frozen snapshot of your code and settings that never changes, so you can always send traffic back to it.
  • Endpoint: the HTTPS address that receives requests and decides which revision handles them.
  • Cold start: the wait while Lambda starts a new environment and your server begins listening.

What are AWS Lambda Web Functions and how do they work?

AWS Lambda Web Functions are a new kind of Lambda function where your code is a plain HTTP server listening on a port. Lambda gives you an HTTPS endpoint, starts copies of your server and spreads requests across them.

The launch post names three kinds of Lambda function. Event Functions are the classic handler model. Web Functions are built for HTTP and serve many requests per environment. MicroVMs are isolated, stateful sandboxes for untrusted code.

Think of a restaurant kitchen. Classic Lambda hires one cook per order, and that cook stands idle while the rice boils. A Web Function cook watches up to 64 pots, stirring whichever one needs attention.

The cook is the Node.js process, a pot is a request, and the rice boiling is a database call. Where the picture breaks: a real cook can leave a pot mid-stir when another boils over. Node.js switches only when your code waits, so a request that never waits freezes every other pot.

How a lunch order reaches a Web Function

Lambda owns the endpoint, the weights and the environments. You own only the Express code inside each environment. Notice the database sees one pool per environment, not one per request.

Diagram connections:

  • Lunch ordering app to CloudFront (optional)
  • CloudFront (optional) to Web Function endpoint (HTTPS on port 443)
  • Web Function endpoint to Revision weights
  • Revision weights to Revision 41 environments (90 percent)
  • Revision weights to Revision 42 environments (10 percent)
  • Revision 41 environments to PostgreSQL
  • Revision 42 environments to PostgreSQL
  • Lunch ordering app: Sends POST /orders over HTTPS
  • CloudFront (optional): Adds a custom domain and AWS WAF
  • Web Function endpoint: Ends TLS, checks auth if set, applies rateLimit
  • Revision weights: For example 90 percent old, 10 percent new
  • Revision 41 environments: Express, up to 64 requests each by default
  • Revision 42 environments: New code, small share of traffic
  • PostgreSQL: One shared pool per environment, not per request

Endpoints accept HTTPS on port 443 only. Lambda handles TLS, the encryption behind HTTPS, and forwards each request to Express as plain HTTP/1.1. Your code never touches a certificate.

Lambda Web Functions vs classic Lambda functions: which should you use for an HTTP API?

Use Web Functions for Node.js HTTP traffic and Event Functions for events such as queue messages or file uploads. The table compares them on the default Lambda compute.

Lambda Web Functions vs Lambda Event Functions, as of 19 October 2026
QuestionWeb FunctionsEvent Functions (classic Lambda)
What does your code look like?A normal HTTP server listening on a portA handler that Lambda calls with one event
Requests per environmentMany at once: 1 to 128, default 64One at a time
Who shares a process?All requests in that environmentNobody: one request per environment
How is compute billed?Active CPU time, plus memory used while the environment existsDuration of each call, waiting included, times memory configured
LanguagesNode.js 24 onlyMany runtimes
Adapter needed for Express?NoYes, an event-to-HTTP adapter

One exception matters for experts. Lambda Managed Instances, an option that runs Event Functions on EC2 instances in your account, also allows many requests per environment. It uses EC2 instance pricing plus a management fee.

So Web Functions are a Lambda Web Adapter alternative. The runtime docs say you do not need that adapter or any other event-to-HTTP adapter.

How does one Lambda Web Functions environment handle many requests at once?

By default, Node.js runs your JavaScript on one main thread with an event loop, which switches to other work while a task waits. While one request waits for PostgreSQL, the loop picks up the next. So one environment can hold many waiting requests.

The revision setting maxConcurrencyPerEnvironment sets the ceiling, from 1 to 128, with 64 as the default. Lambda also keeps its own dynamic limit, based on resource use. That limit can be lower than your setting but never higher.

Global variables are now shared

On classic Lambda, a global variable served one request at a time. Here, every request in the environment shares it. Another request can run each time your code waits with await, so a global can change halfway through your function.

Pause and predict. A handler saves the current user in a global, then awaits the database. User A waits 100 ms and user B waits 10 ms, at the same time. Whose orders does user A get?

We ran this on Node.js 24.22 with Express 5.3.0. User A's response came back marked as user B's. The same handler with the user kept in a local variable returned the right user both times.

What just happened? User B's request overwrote the global while user A's request was waiting. When user A resumed, it read user B's value. On classic Lambda this bug stays hidden, because only one request ever runs.

Database pools and the 2 GB memory budget

A connection pool created once per environment now serves up to 64 requests instead of one. The 200 open requests fit in 4 environments at 64 each, so PostgreSQL sees 4 pools instead of 200 copies. A brand-new Web Function starts with a conservative dynamic limit that Lambda raises as it learns, so plan for more environments and pools on the first busy day.

Memory is shared too. Each environment has 2 GB, and on Web Functions the default Node.js heap, where JavaScript objects live, is 1,024 MB. The heap, buffers, the runtime and every open request must all fit in 2 GB.

The local disk is shared as well. All requests share one 512 MB /tmp folder, so give temporary files unique names and delete them.

One slow request slows every request

Heavy CPU work blocks the event loop for everyone in that environment. In our runs, a fast request took 4 to 33 ms alone. Sent while another request ran a 500 ms CPU loop, it took about 510 ms.

For CPU-heavy work, the docs suggest Node.js worker threads, which run code on other threads, or a lower maxConcurrencyPerEnvironment. This is the main trade: fewer copies, but less isolation between requests.

Beginners can stop here. The rest is for engineers who will deploy, size, price and roll out a Web Function.

How do you run an Express app on AWS Lambda?

You keep your Express app and change only how it starts. The server must listen on the host and port that Lambda passes in the AWS_LAMBDA_HTTP_ENDPOINT environment variable, written as host:port.

javascript
const express = require('express');
const app = express();

app.get('/menu', async (req, res) => {
  // loadTodaysMenu is your own data function
  res.json(await loadTodaysMenu());   // waits on the database, uses little CPU
});

// Lambda sets AWS_LAMBDA_HTTP_ENDPOINT; the fallback is only for your laptop
const [host, port] = (process.env.AWS_LAMBDA_HTTP_ENDPOINT || 'localhost:3000').split(':');
app.listen(port, host);

We ran this sample on Node.js 24.22 with Express 5.3.0 and a stub loadTodaysMenu. It listened on the address in the variable and returned HTTP 200. If the server listens anywhere else, requests never reach it.

Never hard-code localhost on Lambda. The troubleshooting guide says listening on 127.0.0.1 or localhost there causes a listen timeout or HTTP 502. The fallback runs only on your laptop.

  1. Name the entry file

    The runtime loads index.js at the package root unless you set AWS_LAMBDA_NODEJS_ENTRYPOINT to another file.

  2. Start listening fast

    Your server must begin listening within 10 seconds of startup. Do only what the server needs before listening.

  3. Create the pool lazily, once

    If creating the pool awaits anything, such as fetching a secret, store the promise, not the finished pool. In our test, 64 first requests then opened 64 pools with a naive check and 1 with a shared promise.

  4. Clear a failed start

    If creating the pool fails, delete the stored promise so the next request tries again. In our test, a cached failed promise failed every later call too.

  5. Build for Arm64

    Web Functions run only on Arm64 Graviton processors. Pure JavaScript needs no change, but native modules must be built for Linux arm64.

  6. Deploy with the CLI

    Run aws lambda-web deploy with a name and your code. It bundles the code, creates the function and a revision, and attaches an HTTPS endpoint.

  7. Send a test request

    Run curl -i against the endpoint after every deploy. Expect status 200 and a lambda-web-request-id header. An Active revision alone does not prove your server started.

  8. Handle SIGTERM briefly

    Before removing an environment, Lambda sends SIGTERM, a stop signal, and allows up to 1 second. Flush logs there, but never rely on it to save data.

Lambda patches the nodejs24.x runtime for you. As of 19 October 2026, the runtime docs say you cannot pin a patch version or turn patching off.

How do cold starts and scaling work in Lambda Web Functions?

Lambda adds environments as traffic grows and removes them as it falls. With no traffic it scales to zero, so the next request waits for a cold start. You cannot set a minimum number of warm environments.

Lambda also scales ahead of demand to keep spare capacity, called headroom. Small traffic rises land on warm environments. If a new environment is still starting, requests queue for it instead of failing at once.

What happens to one request at the lunch peak

Follow the No arrows: they show the ways a request ends in HTTP 429 (Too Many Requests). A rateLimit of 0 returns HTTP 503 instead. Only the cold-start path makes a request wait instead of fail.

Diagram connections:

  • New order request to Within rate limits?
  • Within rate limits? to HTTP 429 returned (No)
  • Within rate limits? to Any environment free? (Yes)
  • Any environment free? to Served by Express (Yes)
  • Any environment free? to Can Lambda add one? (No)
  • Can Lambda add one? to Wait for cold start (Yes)
  • Can Lambda add one? to HTTP 429 returned (No: a limit is reached)
  • Wait for cold start to Served by Express (Environment ready)
  • New order request: Arrives at 12:45 pm
  • Within rate limits?: Endpoint rateLimit or account request quota
  • Any environment free?: Below its dynamic concurrency limit
  • Can Lambda add one?: maxEnvironments, vCPU quota, starts in progress
  • Wait for cold start: Request queues while the new server starts
  • Served by Express: Shares its environment with other requests
  • HTTP 429 returned: Client retries with backoff and jitter

HTTP 429 is the status code for Too Many Requests. Lambda returns it for four reasons: the endpoint rate limit, the endpoint's maxEnvironments, the account's Regional request or vCPU quota, or too many environments starting at once. The lambda-web-error-type response header names which one. Read it, because the too-many-starts case does not appear in the throttle metrics.

When the lunch app gets a 429 on POST /orders, the client retries with backoff and jitter, meaning growing, randomised waits. It sends an idempotency key, a unique ID per order, so a retry never places the order twice. The client also retries an HTTP 503 that has no lambda-web-error-type header, which can mean Lambda is shedding load.

The settings and quotas that cap capacity

Two optional endpoint settings cap traffic in each Region. The maxEnvironments setting allows 2 to 10,000 environments. The rateLimit setting allows up to 10,000 requests per second, in steps of 100 up to 1,000 and steps of 1,000 above. A rateLimit of 0 blocks all traffic with HTTP 503 (Service Unavailable).

Peak concurrency per Region is at most maxEnvironments times maxConcurrencyPerEnvironment. The docs warn the dynamic limit is often lower, so plan below that number.

Account quotas apply per Region too, and you can ask AWS to raise both. As of 19 October 2026, the defaults are 100,000 requests per second across all endpoints and 2,000 vCPUs, or virtual processor cores. Each environment uses 2 vCPUs, so that is about 1,000 environments by default. New accounts start lower, and AWS raises their quotas as usage grows.

Sizing the lunch peak with Little's law

Little's law says requests in flight equal arrival rate times the time each request stays open. The team plans with it, then checks whether 64 requests fit in the heap.

python
import math

peak_rps = 800          # lunch peak, requests per second (made up)
avg_latency_s = 0.25    # average time one request stays open (made up)
per_env = 64            # maxConcurrencyPerEnvironment, the default
spare = 1.5             # keep 50 percent extra for bursts
mb_per_request = 14     # heap one open order request holds (made up)
base_mb = 250           # heap used by Express and the database pool (made up)

in_flight = peak_rps * avg_latency_s   # Little's law: rate x time
environments = math.ceil(in_flight / per_env * spare)
print(f"Requests in flight at peak: {in_flight:.0f}")
print(f"Plan for {environments} environments, {environments * 2} vCPUs")

heap_mb = base_mb + per_env * mb_per_request
print(f"Heap needed in one full environment: {heap_mb} MB")
if heap_mb > 1024:
    print("Above the default 1,024 MB heap: lower concurrency or raise the heap")

Run it with python3 and you should see 200 requests in flight, 5 environments and 10 vCPUs. The last lines warn that 64 requests at 14 MB plus a 250 MB base need 1,146 MB, above the 1,024 MB default heap.

The team has two fixes. Lower maxConcurrencyPerEnvironment to about 50, or raise the heap with NODE_OPTIONS, for example --max-old-space-size=1536, leaving room inside 2 GB. If memory runs out, the process stops and every request in it fails.

How does Lambda Web Functions pricing differ from classic Lambda?

Web Functions bill active CPU time, not wall-clock time, so waiting is cheap. They add a memory charge for every second an environment exists, even when it is idle.

Classic Lambda bills the duration of each call, waiting included, times the memory you configure. Web Functions bill CPU per vCPU-second, one processor core busy for one second. They add memory per GB-second, 1 GB held for one second, of memory each environment consumes.

The team ran the maths with US East (N. Virginia) list prices read on 19 October 2026, before free tier and data transfer. The workload is made up: 3 crore (30 million) requests a month, each open 250 ms but using 20 ms of CPU.

Monthly compute bill for the lunch API (US East prices as of 19 October 2026; workload made up)
Line itemClassic Lambda (Arm, 1 GB configured)Web Function (Arm)
Requests$6.00 at $0.20 per million$9.00 at $0.30 per million
Compute$100.00 for 7.5 million GB-seconds of duration$16.62 for 600,000 vCPU-seconds of active CPU
MemoryIncluded in duration$4.75 for 300 environment-hours at 1.2 GB (made up)
Total$106.00$30.37
Quiet tool: 100,000 requests, one environment alive all month$0.35$2.94

The last row is the worst case for Web Functions. One environment using 0.3 GB for 30 days costs $2.85 in memory alone. How fast Lambda removes an idle environment is not documented as of 19 October 2026.

Both columns use Arm rates, since Web Functions run only on Arm. On x86, the classic column rises to $131.00. The 300 environment-hours assume about 5 environments through each 2-hour lunch window, 30 days a month. Rerun these sums with your own numbers before you move anything.

How do you roll out and roll back Lambda Web Functions safely?

Every code change or revision setting change, such as timeout or concurrency, means a new immutable revision. The endpoint decides which revisions get traffic, through revision weights.

An endpoint routes to at most two revisions at once. Each weight is a whole number from 1 to 100, and the weights must add up to exactly 100.

An endpoint's auto-deployment mode decides what happens on each new revision. Disabled, the default, leaves traffic alone. LatestRevision sends 100 percent to each new Active revision, and works only for single-Region (HomeRegion) endpoints. The docs advise Disabled for production.

The team runs a canary release, where new code gets a small share first: 10 percent of orders go to revision 42 while errors are watched, then 50, then 100. To roll back, one endpoint update gives revision 41 a weight of 100.

A rollback is not instant capacity. If revision 41 has few warm environments left, requests wait for cold starts, and some can get HTTP 429.

Lambda's own metrics have no revision dimension, so log the revision ID with every request. Then you can see which code caused an error.

When should you not use Lambda Web Functions in 2026?

Skip Web Functions when your app is not Node.js, the work is not HTTP, or you need HTTP features they lack.

  • Other languages: for Python, Java or .NET, the docs point to Event Functions or Lambda MicroVMs.
  • Event-driven work: queue messages and file uploads belong on Event Functions.
  • Unusual HTTP needs: endpoints accept GET, HEAD, POST, PUT, PATCH, OPTIONS and DELETE over HTTP/1.1. Lambda strips the Upgrade header that opens a WebSocket, a long-lived two-way connection.
  • Custom domains: you need CloudFront, the AWS content delivery network, in front for your own domain or for AWS WAF, a web application firewall.

As of 19 October 2026, the Web Functions docs do not list WebSocket support.

With IamAuth, Lambda checks a signed request against IAM, the AWS permissions system, before your code runs. An ApplicationManaged endpoint stays reachable at its own domain even behind CloudFront.

How do you explain AWS Lambda Web Functions in a system design interview?

Frame it as a change in the unit of scaling, from one request per copy to many, then say what that breaks and what it saves.

  1. Name the model

    A Web Function runs a plain HTTP server, and one environment handles up to 128 requests at once, 64 by default.

  2. Size it with Little's law

    Peak requests per second times average latency gives requests in flight. Divide by per-environment concurrency, then add headroom.

  3. Call out shared state

    Globals, pools, the 512 MB /tmp folder and 2 GB of memory are shared across requests. Keep request data in request scope.

  4. Explain the bill

    Active CPU is billed, so waiting on databases is cheap. Memory is billed while environments exist, so idle capacity is not free.

  5. Cover failure and rollout

    Mention cold starts, HTTP 429 at limits and retryable HTTP 503s, with client retries that send idempotency keys. Add weighted revisions for canaries and rollback.

Questions people ask

What is the difference between Lambda Web Functions and Lambda functions?
A Web Function runs a normal HTTP server that handles many requests per environment, while a classic function handles one event at a time. AWS now calls classic functions Event Functions. Web Functions bill active CPU plus memory over time, while classic functions bill the full duration of each call.
Do I still need the Lambda Web Adapter to run Express?
No, not on a Web Function. The AWS runtime docs say you do not need the Lambda Web Adapter or any other event-to-HTTP adapter. Your Express app listens on the address in AWS_LAMBDA_HTTP_ENDPOINT and keeps its normal routes.
How many concurrent requests can one Lambda Web Function environment handle?
Up to 128, and the default is 64. You set this ceiling with maxConcurrencyPerEnvironment on each revision. Lambda also applies a dynamic limit based on resource use, which can be lower. Lower the setting for CPU-heavy or memory-heavy apps.
Do Lambda Web Functions still have cold starts?
Yes. A new environment starts whenever no warm one has capacity, including after scaling to zero. One warm environment serves many requests and Lambda keeps spare headroom, so small traffic rises usually avoid cold starts. There is no setting for a minimum of warm environments.
Is Lambda Web Functions pricing cheaper than classic Lambda?
It depends on how your requests spend their time. APIs that mostly wait on databases can cost much less, because only active CPU is billed. But memory is billed while each environment exists, even when idle, so a quiet tool can cost more.
How long can one request run on a Lambda Web Function?
Up to 900 seconds, which is 15 minutes. Each revision has a timeoutSeconds setting from 3 to 900, with a default of 30. A request that runs past it gets HTTP 504. Responses can also stream to the client as they are produced.

Sources and further reading

Practise this with Lala

Practise