Generated banner for AWS Lambda Web Functions
Generated logo for AWS Lambda Web Functions

AWS Lambda Web Functions

AWSOfficialPublished by AWS

  • Skill
  • Tool integration
  • Cloud and DevOps
  • Apache-2.0
  • Low risk

From the author

Creates and deploys Node.js HTTP web applications on AWS Lambda Web Functions. Guides users through building web apps and APIs with Express, Fastify, or Hono, packaging, deploying via the aws lambda-web CLI, and iterating. Triggers when user wants to deploy a website or web app to AWS as serverless, deploy a Node.js HTTP server to AWS, build a serverless website or API with Node.js, deploy Express/Hono/Fastify apps, set up multi-region HTTP endpoints, connect a Node.js web app to private VPC resources, or explicitly mentions Lambda Web Functions. Does NOT trigger for non-Node.js runtimes or frameworks such as Flask or Django, event-driven patterns (SQS, Kinesis, S3 events), or container deployments.
— the description by AWS

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a workflow for deploying Node.js HTTP applications, such as Express, Fastify, or Hono, directly to AWS Lambda Web Functions. It guides users through the entire lifecycle, including local development, packaging, deployment via the aws lambda-web CLI, and managing revisions. The approach allows applications to handle HTTP requests natively without requiring handler rewrites or adapter libraries, supporting features like response streaming and multi-region endpoints.

What it does

  • Scaffolds Node.js web applications for AWS Lambda Web Functions.
  • Packages applications with production dependencies for deployment.
  • Deploys code using the aws lambda-web CLI.
  • Manages function revisions and endpoint configurations.
  • Configures authentication types including ApplicationManaged and IamAuth.
  • Supports multi-region endpoint setup and VPC egress connectivity.

When to use it

  • When deploying a Node.js HTTP server (Express, Fastify, Hono) to AWS.
  • When you need a serverless web application or API that scales to zero.
  • When your application requires response streaming or chunked transfers.
  • When you need a globally reachable HTTPS URL without manual infrastructure setup.
  • When your workload is I/O-bound and benefits from shared execution environments.

Advantages

  • No need to rewrite code or use adapter libraries for Lambda compatibility.
  • Supports standard HTTP frameworks like Express, Fastify, and Hono.
  • Simplifies deployment with a dedicated CLI tool.
  • Enables global reach with multi-region endpoint support.
  • Allows for efficient resource usage with I/O-bound request handling.

Usage examples

“Deploy my Express app to AWS Lambda Web Functions.”
The agent will guide you through packaging your Express application, setting up the necessary IAM roles, and using the aws lambda-web CLI to deploy your code.
“How do I update my existing Lambda Web Function with new code?”
The agent will provide the steps to package your updated files, create a new revision using the CLI, and shift traffic to the new version.
“Can I connect my Lambda web app to a private RDS instance?”
The agent will explain how to configure an egress network connector in your revision to allow your function to reach resources within your VPC.
“How do I secure my public web function endpoint?”
The agent will help you choose between ApplicationManaged auth or IamAuth and provide guidance on implementing security headers and input validation.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/aws/aws-lambda-web-functions/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip aws-lambda-web-functions.zip -d .agents/skills

Documentation by AWS

AWS Lambda Web Functions

The AWS MCP server is recommended for sandboxed execution and audit logging.

If aws lambda-web is not recognized, update the AWS CLI (see references/deployment.md). A missing command means an outdated CLI, not an unavailable feature.

Help users build and deploy Node.js HTTP applications on AWS Lambda Web Functions. Your app listens on a port, handles HTTP natively, and gets a global HTTPS URL. Develop locally with node index.js, deploy the same code to AWS.

When to Use Web Functions

  • Interactive HTTP traffic someone is waiting on: web app, HTTP API, or SSR site.

  • The app is already a port-listening HTTP server (Express, Fastify, Hono) and should stay one — no handler rewrite, no adapter library.

  • Handlers are I/O-bound (database, cache, another API, a model endpoint), so many requests share one execution environment and time spent waiting on I/O is not billed as CPU.

  • Response streaming matters: SSE, chunked transfer, or proxying LLM tokens as they are produced.

  • The endpoint should be reachable worldwide on one hostname without assembling CloudFront, API Gateway and Route53 by hand.

  • Traffic is spiky or unpredictable and should scale to zero.

Hard preconditions

Any "no" rules Web Functions out. Invoke the aws-serverless skill to find a suitable service.

  • Runtime is Node.js — nodejs24.x. Python, Java, Go, .NET and Rust are not supported. Confirm the supported runtime list with aws lambda-web help or the AWS docs before routing a caller away.

  • Every native dependency has an arm64 (Graviton) build.

  • The work is an HTTP request/response (or stream) that a caller is waiting on. Queue, event-stream source (Kinesis, DynamoDB Streams), schedule, or event trigger are not supported.

  • One request completes within the 900 s (15 min) request timeout.

  • The app fits a 512 MB package and a 2 vCPU / 2 GB environment (see Service Limits).

  • Environments are stateless — any environment can serve any request. There is no affinity between an end user's session and a specific environment.

  • The app tolerates sharing an environment with concurrent requests. Strict per-request isolation is not supported.

Use Cases

"What I'm building" → how it lands here.

The user is buildingShape on Web Functions
JSON HTTP API or REST backendExpress or Hono router, I/O-bound handlers, DynamoDB or RDS behind it → architecture-patterns.md
Server-side-rendered site or appRender HTML per request from index.js; no bundler needed unless you author JSX → architecture-patterns.md
LLM or AI gateway streaming tokens to a browserSSE or chunked responses; raise timeoutSeconds to cover the whole stream, not just the first byte → architecture-patterns.md
Webhook receiver (Stripe, GitHub, Slack)Verify the sender's signature against a signing secret from Secrets Manager before acting on the payload, then acknowledge fast and finish the work after responding — the environment stays running → architecture-patterns.md
Mobile or web backend-for-frontend aggregating several servicesOne environment fans several requests out concurrently, and time spent waiting is not billed as CPU
Single-page app plus its API from one deploymentexpress.static for the bundle, /*splat fallback for client routes → static-assets.md
Internal service-to-service HTTP endpointauthType: IamAuth, so only SigV4-signed callers reach it → iam-and-security.md
Globally low-latency endpoint on a single hostnameMultiRegion endpoint plus a region-agnostic app → domains-and-routing.md
App that calls RDS, ElastiCache or other private VPC resourcesAn egress network connector referenced from the revision; the endpoint stays public → networking.md

Workflow

Default to the aws lambda-web CLI — it's the quickest path. Use aws lambda-web deploy when the user asks for a single command or says they already deploy with it. It packages, uploads and creates or updates a project directory in one command, and --hello-world scaffolds and deploys a starter app (see Deploy (CLI) below). Otherwise, including for updates to an existing function, use the steps below. Use CloudFormation, CDK, SAM or Terraform when the user wants infrastructure-as-code or a repeatable production stack (see references/infrastructure-as-code.md).

  1. Scaffold - Express is inline below; for Hono or Fastify, read references/frameworks.md

  2. Choose endpoint auth — ask the user before deploying (see below); endpoints are public unless they pick IamAuth

  3. Test locally — node index.js

  4. Package — ZIP with production deps (up to 512 MB)

  5. Deploy — aws lambda-web create-web-function

  6. Verify — get endpoint URL, then curl

  7. Iterate — create a new revision

Public by default. A ApplicationManaged endpoint is reachable by anyone on the internet with the URL — there is no AWS-layer auth and no built-in WAF, and the URL is not a secret. Before deploying, ask the user which they want:

  • ApplicationManaged (public) — the app must implement its own authentication/authorization, input validation, and rate limiting.

  • IamAuth (internal) — only callers with valid IAM credentials (SigV4-signed) can reach it.

If the endpoint is public, the app owns its security. See references/iam-and-security.md for the security checklist (auth, input validation, rate limiting, WAF/CloudFront, least-privilege role, secrets).

Quick Start (Express)

// index.js
import express from 'express';
import helmet from 'helmet';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __dirname = dirname(fileURLToPath(import.meta.url));
const app = express();
// The runtime probes this exact host:port for readiness.
const [HOST, PORT] = (process.env.AWS_LAMBDA_HTTP_ENDPOINT || '0.0.0.0:3000').split(':');

app.use(helmet());
app.use(express.static(join(__dirname, 'public')));
app.use(express.json({ limit: '10mb' }));
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
app.listen(PORT, HOST, () => console.log(`Listening on ${HOST}:${PORT}`));
{
  "name": "my-web-app",
  "type": "module",
  "scripts": { "start": "node index.js", "dev": "node --watch index.js" },
  "dependencies": { "express": "^5.0.0", "helmet": "^8.0.0" }
}

helmet sets the security response headers (HSTS, CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) that no AWS layer sets for you here. On an ApplicationManaged endpoint the app is the only thing that can set them, so it belongs in the scaffold rather than being added later.

Resource Naming

All resources are project-scoped. Derive names from the function name:

ResourceDefault PatternExample
Function{name}my-api
IAM Role{name}-web-rolemy-api-web-role
S3 bucket{name}-web-{account}my-api-web-1234567890
S3 key{name}/function.zipmy-api/function.zip
DynamoDB{name}-{purpose}my-api-items

Function names must be unique across all Regions in the account — the same name cannot exist in two Regions, and a collision fails at create time with a conflict error.

Ask the user which S3 strategy they prefer:

  • Separate bucket per project (default) — fully isolated, easy cleanup

  • Shared bucket across projects (e.g., {account}-web-code) — fewer resources, key-prefix isolation

  • Custom — user provides their own bucket name and key prefix

aws lambda-web deploy names its own role (awscli-lambdaweb-{name}) and bucket (awscli-lambdaweb-{account}-{region}-an).

Deploy (CLI)

The aws lambda-web CLI is the primary, supported path — use it by default. The flow is: one-time S3 code bucket (versioning + Lambda read access), then npm ci --omit=dev → zip → aws s3 cp → aws lambda-web create-web-function with --revision-config and --endpoint-config, then wait for the endpoint to reach Active. If the project has a build step, run it before the prune — npm ci --omit=dev removes the bundler.

When the user asks for one command or already deploys with it, aws lambda-web deploy --name {name} --code . --create --auth-type {auth-type} (ask the user which auth type) zips, uploads, and creates the function, a code bucket, a role and a dev endpoint; rerun it without --create to ship changes. Install dependencies first.

For the full commands, EndpointConfig and RevisionConfig field rules, packaging, timeout, concurrency, environment variables, logging config and every deploy flag, see references/deployment.md. First-time setup (bucket, execution role, first deploy) is in references/getting-started.md. For CloudFormation, CDK or SAM, see references/infrastructure-as-code.md.

Multi-Region

Multi-region is supported: a MultiRegion endpoint serves your revision from several regions behind one hostname, with Route53 routing each caller to the nearest region. PerRegion additionally gives each region its own domain name. Both require autoDeploymentMode: Disabled, and the app must be region-agnostic — consecutive requests from one client can land in different regions, so keep state in a replicated store, never in the execution environment or /tmp.

See references/domains-and-routing.md for endpoint types, domains, traffic routing and the multi-region setup commands.

VPC Egress

By default a revision reaches the public internet over IPv4 and nothing private. To reach your VPC, put a version-qualified egress network connector ARN (aws lambda-core create-network-connector, compute type WebFunction) in the revision's serviceConfig.egressNetworkConnectorArn, serve it from a HomeRegion endpoint, and read the field back after every revision. See references/networking.md, or references/infrastructure-as-code.md to declare the connector and the revision in one stack.

Verify

aws lambda-web get-web-function-endpoint \
  --function-name {name} --endpoint-name default --region {region}

# curl the domainName from the response
curl https://{domainName}/api/health

A revision that fails to start still goes Active, so curl a real route after every deploy. aws lambda-web deploy prints the endpoint as endpointUrl and names its first endpoint dev.

Prefer waiters (aws lambda-web wait ...) over polling loops or sleep. Available: web-function-active, web-function-revision-active, web-function-endpoint-active, web-function-endpoint-updated, web-function-endpoint-deleted, web-function-deleted

Update Code

zip -r function.zip index.js package.json node_modules/ public/ \
  -x "*.test.*" -x ".git/*" -x ".env" -x "node_modules/.cache/*"
aws s3 cp function.zip s3://{bucket}/{name}/function.zip

aws lambda-web create-web-function-revision \
  --function-name {name} \
  --build-config '{
    "codeConfig": {"s3Object": {"bucket": "{bucket}", "key": "{name}/function.zip"}},
    "runtimeConfig": {"runtime": "nodejs24.x"}
  }' \
  --service-config '{"executionRoleArn": "arn:aws:iam::{account}:role/{name}-web-role"}' \
  --region {region}

Service Limits

Subject to change — check the Lambda quotas page for the authoritative values.

ScopeResourceLimit
AccountRegional vCPU limit2,000
AccountRegional account rate limit100,000 RPS
AccountRegional per-endpoint maximum rate limit10,000 RPS
Per functionEndpoints10
Per functionRevisions retained50 (oldest unreferenced revisions are deleted automatically)
Per functionEnvironment variables32 KB total
Per functionDeployment package512 MB
Per functionFunction and endpoint name64 characters
Per endpointEndpoint rate limitLimited by Regional per-endpoint maximum rate limit (above). User can optionally configure a lower rate limit on individual endpoints via throttleConfig.rateLimit
Per endpointActive revisions2
Per environmentvCPU2
Per environmentMemory2 GB
Per environmentConcurrency1–128 requests (see scaling-and-concurrency.md)
Per environmentNetwork bandwidth1 Gbps
Per environment/tmp512 MB
Per environmentFile descriptors1,024
Per environmentProcesses and threads1,024
Per environmentInit timeout10 seconds
Per requestTimeout3–900 s (default 30 s)
Per requestHeader count6,300

Cost Model

Four billing dimensions: CPU, memory, requests, and data transfer. Check the Lambda pricing page for current rates — do not quote rates from memory. What decides an architecture is the shape of the bill:

  • Active CPU, not wall-clock duration. Time a request spends blocked on I/O — a database call, another API, a model endpoint — is not billed as CPU. Event Functions bill GB-seconds of wall-clock duration instead, so the same handler bills differently on each.

  • Memory is billed on consumption, not on a provisioned setting. An environment is 2 vCPU and 2 GB (see Service Limits) and there is no memory dial, so the familiar Event Functions move of raising memory to shorten duration has no equivalent here.

  • Concurrency is the density lever. One environment serves up to maxConcurrencyPerEnvironment requests, so an I/O-bound app keeps many requests in flight per environment instead of occupying one environment each.

Two consequences worth planning around:

  • I/O-bound workloads are the economical case. A handler that spends most of its life waiting on the network, sharing an environment with other requests, costs less here than the same handler billed per-invoke on wall-clock duration.

  • CPU-bound workloads give that advantage back. Heavy rendering, image processing or crypto burns billable CPU wherever it runs, and an environment is allocated 2 vCPU — of which a single-threaded Node process uses only one (see Anti-Patterns). For sustained high-volume CPU work, especially where committed-use discounts apply, compare aws-lambda-managed-instances.

An endpoint's scaling controls scalingConfig.maxEnvironments and throttleConfig.rateLimit can be used to limit potential cost. Set them only when the user asks for a cap. See scaling-and-concurrency.md for more details.

IAM Setup

Trust policy principal: lambda.amazonaws.com

IAM actions use the lambda: prefix. Resource ARN: arn:aws:lambda:{region}:{account}:web-function/{name}

Action names match the API operations — lambda:CreateWebFunction, lambda:CreateWebFunctionRevision, lambda:CreateWebFunctionEndpoint, lambda:UpdateWebFunctionEndpoint, plus the Get*, List*, and Delete* variants. See references/iam-and-security.md for the full policy.

Resource ARN must include /* wildcard for endpoint/revision operations:

"Resource": ["arn:aws:lambda:{region}:{account}:web-function/{name}", "arn:aws:lambda:{region}:{account}:web-function/{name}/*"]

The caller also needs s3:GetObject + s3:GetObjectVersion on the code bucket (not just the execution role).

See references/iam-and-security.md for full policy examples.

Anti-Patterns

What it looks like → why it fails → what to do instead.

Anti-patternWhy it failsInstead
Expecting a CPU-bound handler to use the whole environmentAn execution environment is allowed 2 vCPU, but Node is single-threaded, so one process uses one of them and the second sits idleFor CPU-bound work, use worker_threads (or cluster) to occupy both. Leave I/O-bound handlers single-threaded — they are not CPU-limited
Zipping a node_modules built on an x86 machine (laptop or x86 CI)Execution environments are arm64, so an x86 native binary will not load. Packages such as sharp, canvas, bcrypt and other native addons ship per-architecture buildsInstall and build on arm64 — an arm64 CI runner, a Graviton host, or a container run with --platform linux/arm64 — then zip
Writing response bytes while the request body is still uploading (full-duplex streaming)The request path is half-duplex: the whole request body is forwarded before any response is read, so a large upload that expects an interleaved response stalls until the timeout. Small bodies fit socket buffers and appear to work, which hides it in testingRead the request to completion, then stream the response. Keep streaming in the server-to-client direction
Wrapping the app in a handler adapter (serverless-express, @vendia/serverless-express, aws-lambda-fastify)There is no handler to export. The runtime loads index.js and expects a listening HTTP server; an adapter never binds a port, so readiness never passes and the environment is not usableDelete the adapter and call app.listen(...) on the host and port from AWS_LAMBDA_HTTP_ENDPOINT
Hardcoding app.listen(3000), or binding localhost / 127.0.0.1The runtime probes the exact address it advertises. A mismatched port or a loopback-only bind never answers that probeParse AWS_LAMBDA_HTTP_ENDPOINT and bind that host and port
Module-level mutable state holding per-request data — a currentUser, a request-scoped cache, a counterOne execution environment serves many concurrent requests in a single process, so requests read each other's values. This is a correctness and data-leak bug, not a performance oneKeep per-request data in the request scope; share only immutable config and SDK clients at module level
Writing session or upload state to /tmp and expecting it on the next request/tmp is per environment and shared between concurrent requests; the next request may land on a different environment, or a different Region on a multi-region endpointKeep state in DynamoDB, S3 or ElastiCache; use unique filenames for genuinely temporary files
Treating the endpoint URL as access control — "nobody knows this domain"An ApplicationManaged endpoint is reachable by anyone on the internet with the URL. No AWS-layer auth, no built-in WAF, and the URL is not a secretImplement auth in the app, or use IamAuth; to let only CloudFront in, use IamAuth with CloudFront OAC and a resource-based policy → domains-and-routing.md; add WAF for L7 protection → iam-and-security.md
Secrets in serviceConfig.environmentVariablesget-web-function-revision returns environment variables in plaintext to anyone with read access to the revisionKeep the secret in Secrets Manager or SSM, pass only its name or path, fetch at runtime
Running npm ci --omit=dev before the build stepPruning devDependencies deletes the bundler, so the build then fails with esbuild: command not foundBuild first, prune afterwards — or zip the bundled output → deployment.md
Relying on the runtime for @aws-sdk/*, or marking it external (externalModules, --external:@aws-sdk/*), as on Event FunctionsThe Web Functions runtime does not include the AWS SDK, so the import fails at startup and every request returns 502Package the @aws-sdk/* clients you import like any other dependency → deployment.md
Expecting an update-* call to change code, runtime, timeout, env vars or concurrency in placeAll of those live on the immutable revisionCreate a new revision. Only endpoint-level settings (auth, weights, scaling, throttle) update in place
Re-uploading to the same S3 key and expecting a redeployA revision is pinned to an object version; CloudFormation reports "No changes to deploy"Use a new key, or pass the new versionId
Concluding the feature does not exist because aws lambda-web is missingAbsence means an outdated CLI, not a missing capabilityUpdate the AWS CLI → deployment.md
Reusing one function name in two RegionsFunction names are unique across all Regions in an account; the second create fails with a conflictSuffix the Region, or use one function with a MultiRegion endpoint
app.get('*', ...) as an SPA fallback on Express 5path-to-regexp v8 rejects a bare * and throws at startup, so the app never bootsUse '/*splat' → static-assets.md

Troubleshooting — Start Here

The five that account for most failed first deploys. Full symptom → cause → fix tables are in troubleshooting.md.

SymptomCauseFixVerify
Endpoint never reaches Active; state goes FailedThe app never bound the advertised host and port, so readiness never passedBind the host and port from AWS_LAMBDA_HTTP_ENDPOINTget-web-function-endpoint reports Active, then curl domainName
500 with Cannot find module '/var/task/index.js'Entry point is not index.js — .mjs, server.js, or nested in dist/Deploy with aws lambda-web deploy --entry-point {file}, or rename the entry point to index.js at the ZIP rootunzip -l function.zip lists index.js at the top level
Stuck in Pending past 10 minutesExecution-role trust policy principal is not lambda.amazonaws.comFix the trust policy, then create a new revisionaws iam get-role shows the correct principal
create-web-function-revision returns 500 with no messageThe caller lacks s3:GetObject / s3:GetObjectVersion on the code bucket — not the execution roleAdd both to the caller's policyThe revision reaches Active
invalid choice: 'lambda-web', or Unknown parameter "BuildConfig"Outdated AWS CLI; or PascalCase JSON keysUpdate the CLI; use camelCase (buildConfig, codeConfig)aws lambda-web help lists the web-function commands

Migration

Moving an existing Node.js HTTP app here is mostly a delete-code exercise — the adapter, the handler export and the API Gateway wiring all go away, and the router itself does not change.

Four source shapes each have a step-by-step path, a preflight checklist, the features with no equivalent, and a traffic-cutover procedure: an adapter-wrapped app, API Gateway + Lambda proxy, a Lambda function URL, and a container on Fargate, App Runner or EC2. See migration.md.

When to Load Reference Files

Match on meaning, not exact wording — any trigger word in a row should pull that file.

Read thisTrigger words
getting-started.mdfirst deploy, get started, set up, prerequisites, hello world, --hello-world, starter app, new project, from scratch, S3 code bucket, execution role, bootstrap, my first web function
frameworks.mdExpress, Fastify, Hono, TypeScript, .ts, type stripping, ESM, type: module, scaffold, project structure, entry point, index.js, router, middleware
deployment.mddeploy, redeploy, ship, release, CLI, aws lambda-web, aws lambda-web deploy, one command, revision, endpoint, packaging, ZIP, npm ci, timeout, environment variables, env vars, canary, blue/green, traffic weights, rollback, versionId
scaling-and-concurrency.mdscaling controls, concurrency, maxConcurrencyPerEnvironment, scalingConfig, maxEnvironments, throttleConfig, rateLimit, throttling, RPS limit, vCPU quota, load test, Platform.EndpointMaxEnvThrottle
infrastructure-as-code.mdCloudFormation, CFN, CDK, CfnResource, escape hatch, SAM, Terraform, HashiCorp, HCL, infrastructure as code, IaC, template, stack, AWS::Lambda::WebFunction, AWS::Lambda::NetworkConnector, VPC egress in a stack, AWS::Lambda::ResourcePolicy, repeatable deployment, pipeline
static-assets.mdstatic files, static assets, CSS, JS bundle, images, favicon, public/, express.static, SPA, single-page app, client-side routing, fallback route, bundling, esbuild, Vite, cache headers
domains-and-routing.mdmulti-region, multiregion, global, latency routing, Route53, domain, custom domain, hostname, HTTPS, TLS, endpoint type, HomeRegion, MultiRegion, PerRegion, CORS, auth type, ApplicationManaged, IamAuth, CloudFront, OAC, origin access control, resource-based policy
iam-and-security.mdIAM, policy, permissions, role, trust policy, PassRole, least privilege, AccessDenied, security, auth, authentication, authorization, public endpoint, WAF, DDoS, rate limiting, secrets, KMS, encryption, security headers, helmet
observability.mdlogs, logging, log group, log level, CloudWatch, metrics, monitoring, alarms, tracing, CloudTrail, audit, structured logging, health check, which region served, debug in production
architecture-patterns.mdSSR, server-side rendering, React, JSX, streaming, SSE, server-sent events, LLM, AI gateway, token streaming, long-lived connection, webhook, background task, fire and forget, API backend, DynamoDB, connection pooling, thread safety, concurrency model
troubleshooting.mderror, fails, failing, broken, not working, 500, 502, 403, 404, ECONNREFUSED, ERR_MODULE_NOT_FOUND, module not found, stuck, Pending, Failed, never becomes active, AccessDenied, ParamValidation, no logs, debug, why doesn't
networking.mdVPC, private subnet, RDS, Aurora, ElastiCache, private resources, VPC endpoint, PrivateLink, network connector, egress, egressNetworkConnectorArn, lambda-core, NAT gateway, security group, private DNS, private hosted zone, IPv6, DualStack, static outbound IP
migration.mdmigrate, migration, move, port, switch, convert, coming from, replace, serverless-express, adapter, wrapper, handler export, function URL, API Gateway proxy, Fargate, App Runner, containerized, lift and shift

Guardrail — where this skill's own files live (MCP vs local install)

This skill can be loaded two ways, and they resolve the skill's own bundled files — the references/ documents — from different places. Determine how the skill was loaded before you read a reference:

  • Loaded through the AWS MCP retrieve_skill tool call. The skill is not installed on the local filesystem; its reference files do not exist on disk. You MUST fetch each reference through the same retrieve_skill tool by passing the file parameter (for example, file="references/deployment.md"). Do NOT file_read these paths from the local or working directory, and do NOT search the filesystem for them — they are not there, and any local file that happens to match the name is unrelated to this skill.

  • Installed locally (the skill lives in a local skills directory such as .claude/skills/aws-lambda-web-functions/, ~/.claude/skills/aws-lambda-web-functions/, or .kiro/skills/aws-lambda-web-functions/). Read references from the local skill directory using the relative paths shown throughout this document.

This distinction applies only to the skill's own packaged files. The user's application code — index.js, package.json, public/, the function.zip you build — is always read from and written to the user's working directory regardless of how the skill was loaded. Never fetch or write customer data through retrieve_skill.

Critical Rules

  1. Node 24 only — nodejs24.x is the only runtime; runtime support can change, so confirm with aws lambda-web help or the AWS docs before assuming a newer version is unavailable

  2. Entry point is index.js — the runtime loads /var/task/index.js, not .mjs. When your entry file has another name (server.js, app.mjs), aws lambda-web deploy detects it or takes --entry-point and ships a small index.js that imports it; a ZIP you build yourself needs index.js at its root

  3. Bind the host and port from AWS_LAMBDA_HTTP_ENDPOINT — the runtime probes that exact address, so a hardcoded port that does not match it, or a loopback-only bind, fails readiness and the environment is not usable. There is no PORT env var

  4. "type": "module" in package.json — required for ESM imports from index.js

  5. No handler exports — your app is an HTTP server

  6. Write thread-safe code — one execution environment serves many concurrent requests; avoid global mutable state

  7. Code, runtime, timeout, env vars and concurrency all live on the revision — changing any of them requires a new revision, not an in-place update

  8. Background tasks work natively — the environment stays running after response

  9. Streaming native — chunked transfer-encoding, SSE out of the box

  10. TypeScript works natively — Node 24 type stripping, use index.js shim + import type

  11. Code comes only from a ZIP in S3 — deploy builds and uploads it for you; otherwise supply it through codeConfig.s3Object and bundle shared code into the package. For container images and a layers equivalent, confirm against aws lambda-web create-web-function-revision help or the API reference rather than assuming either is available

Files

references/deployment.md

Deployment

CLI Setup

If aws lambda-web is not recognized, update to the latest AWS CLI version. Verify:

aws lambda-web help  # lists the web-function commands

CLI Commands (aws lambda-web)

CommandPurpose
deployPackage a local directory and create or update the function in one step (see One-Command Deploy below)
create-web-functionCreate function + first revision + endpoint
create-web-function-endpointAdd additional endpoints
create-web-function-revisionDeploy new code
get-web-functionGet function state
get-web-function-endpointGet endpoint details (domainName, state, weights)
get-web-function-revisionGet revision details
list-web-functionsList all functions
list-web-function-endpointsList endpoints for a function
list-web-function-revisionsList all revisions
update-web-function-endpointUpdate auth, deploy mode, revision weights
delete-web-functionDelete function
delete-web-function-endpointDelete an endpoint
delete-web-function-revisionDelete a revision

Create Function

aws lambda-web create-web-function \
  --function-name {name} \
  --revision-config '{
    "buildConfig": {"codeConfig": {"s3Object": {"bucket":"{b}","key":"{k}"}}, "runtimeConfig": {"runtime":"nodejs24.x"}},
    "serviceConfig": {"executionRoleArn": "arn:aws:iam::{acct}:role/{name}-web-role"}
  }' \
  --endpoint-config '{
    "endpointName":"default","endpointType":"HomeRegion",
    "authType":"ApplicationManaged","autoDeploymentMode":"LatestRevision"
  }' --region {region}

Verify Endpoint

aws lambda-web get-web-function-endpoint \
  --function-name {name} --endpoint-name default --region {region}
# Returns domainName, state, revisionWeights, regions

New Revision (Update Code)

aws s3 cp function.zip s3://{bucket}/{name}/function.zip
aws lambda-web create-web-function-revision \
  --function-name {name} \
  --build-config '{"codeConfig":{"s3Object":{"bucket":"{b}","key":"{k}"}},"runtimeConfig":{"runtime":"nodejs24.x"}}' \
  --service-config '{"executionRoleArn":"arn:aws:iam::{acct}:role/{name}-web-role"}' \
  --description "Add /metrics endpoint" \
  --region {region}

autoDeploymentMode decides whether a new revision starts serving on its own: LatestRevision shifts all traffic to each new revision as you create it, while Disabled leaves traffic where it is until you set revisionWeights yourself (canary, blue/green, multi-region).

LatestRevision has no health gate. The traffic shift is not conditional on the revision starting successfully. A revision whose code fails to load takes 100% of traffic and still reports state: Active with stateReason: "The revision is active.", so polling revision state does not detect this. Neither does create-web-function, which returns a function already in Active while the revision is still Pending.

Two things actually detect it:

  • platform.initReport in CloudWatch. A healthy start logs {"level":"INFO","type":"platform.initReport","record":{"status":"success",…}}. A failed start logs "level":"WARN" with "status":"failure" and an errorType such as Application.InitCodeError. This is the reliable signal.

  • An external smoke test against the endpoint domain after the shift, checking a real response and not just a TCP connect.

For anything serving real traffic, prefer Disabled with explicit revisionWeights so you choose when traffic moves, and roll back by pointing the weights at the previous revision:

aws lambda-web update-web-function-endpoint \
  --function-name {name} --endpoint-name default \
  --endpoint-config '{"revisionWeights":[{"revisionId":"{previous-revision-id}","weight":100}]}' \
  --region {region}

Also wait on the right thing. wait web-function-active returns almost immediately because the function is Active before the revision and endpoint are; wait web-function-endpoint-active is the one that means the endpoint can serve.

One-Command Deploy (aws lambda-web deploy)

deploy packages a local directory, uploads it, and either creates the function (first run) or creates a new revision and points the endpoint at it (every later run). Use it when the user asks for a single command or already deploys with it. It replaces the zip, S3 upload and create calls above for a project directory on disk; use those calls when you need your own bucket, key or role layout, or already have a built ZIP.

npm ci --omit=dev                    # deploy uploads the directory as it is; it does not install
aws lambda-web deploy --name {name} --code . --create --auth-type {auth-type} --region {region}   # first deploy; ask the user: IamAuth or ApplicationManaged (public)
aws lambda-web deploy --name {name} --code . --region {region}              # every deploy after
FlagEffect
--nameFunction name (required)
--codeLocal directory to package. Required on create unless --hello-world is used. On an existing function, omit it to change only configuration: nothing is packaged or uploaded, and the new revision keeps the deployed code
--createConfirms creating a function or endpoint that does not exist. A non-interactive shell stops without it; an existing function never needs it
--hello-worldScaffolds a starter HTTP server and deploys it, into --code, or ./{name} when --hello-world is used without --code. New functions and empty directories only; it never overwrites code
--entry-pointEntry file relative to --code, when auto-detection does not find it
--execution-role-arnUse this role. On create, omitting it creates awscli-lambdaweb-{name}; on update the existing role is kept
--bucket-nameUse this existing bucket for the code ZIP instead of the managed awscli-lambdaweb-{account}-{region}-an
--env KEY=VAL / --unset-env KEYRepeatable. Merged into the existing environment on update; --unset-env removes a key. Not for secrets: command-line values land in shell history and process listings, and revision environment variables are readable in plaintext. Keep secrets in Secrets Manager or Parameter Store and pass only the name (see Environment Variables below)
--timeout-seconds, --max-concurrency-per-environment, --application-log-level, --system-log-level, --kms-key-arn, --revision-descriptionRevision settings. On update, anything you omit keeps its current value. --kms-key-arn encrypts the code and environment variables, not the log group; to encrypt logs with your own key, see observability.md
--endpoint-nameEndpoint whose settings the command creates or updates (first one defaults to dev). Naming one that does not exist adds it, with --create. The new revision is served by every endpoint in LatestRevision mode, not only this one; pin other endpoints with autoDeploymentMode: Disabled
--endpoint-type, --regionsHomeRegion (default), MultiRegion or PerRegion, used only when the endpoint is created
--auth-typeApplicationManaged (default, public) or IamAuth. Ask the user before choosing. A public endpoint has no AWS-layer auth or WAF: the app owns authentication, input validation, rate limiting and security headers, and CloudFront + AWS WAF in front adds L7 protection — see the checklist in iam-and-security.md
--auto-deployment-mode, --max-environments, --rate-limit, --descriptionEndpoint settings, applied on create or when they differ from the existing endpoint
--tags KEY=VALResource tags, on create only
--include-hidden-filesInclude dotfiles in the bundle. They are left out by default, and .env is always left out
--no-waitReturn after the create or revision call instead of waiting for it to become active

Run aws lambda-web deploy help for the complete list.

VPC egress. deploy has no connector option, so it cannot give a function VPC egress in the first place; use create-web-function-revision for that. It does not lose one either: a redeploy builds the new revision from the previous one and carries serviceConfig.egressNetworkConnectorArn forward, so shipping a code change keeps VPC egress. The ARN it carries forward is version-qualified, so a redeploy after a connector update keeps the old version. See networking.md.

Entry point. deploy looks for index.js, server.js (and their .mjs / .cjs forms), their dist/ and build/ variants, and package.json main. When the entry is not index.js it adds a small index.js that imports it, because the runtime always loads index.js.

Output. With --output json the result carries functionArn, endpointUrl (a hostname, without https://), roleArn, bucketName, objectKey, and on updates revisionId. Progress lines go to stderr, so --query endpointUrl --output text gives a clean hostname for scripts. --no-wait returns before the endpoint URL is known.

A successful exit does not mean the app started. The command waits for the revision and endpoint to become Active, which happens even when the code fails to listen (see the LatestRevision note above). Curl a real route after every deploy.

Packaging

npm ci --omit=dev
zip -r function.zip index.js package.json node_modules/ public/ \
  -x "*.test.*" -x ".git/*" -x ".env" -x "node_modules/.cache/*"

Include: index.js, package.json, node_modules/, public/. See Service Limits in SKILL.md for the package size cap.

Package the AWS SDK. The Web Functions runtime does not include the AWS SDK, so package the @aws-sdk/* clients your code imports like any other dependency: list them in dependencies (the npm ci --omit=dev step removes devDependencies), or bundle them. Do not mark them external (externalModules: ['@aws-sdk/*'], --external:@aws-sdk/*), which is common for Event Functions. A missing client fails the import at startup and every request returns 502.

If you have a build step (esbuild for JSX or TypeScript), build before pruning. npm ci --omit=dev deletes the bundler along with the other devDependencies, so a later npm run build fails with esbuild: command not found:

npm install        # devDependencies included
npm run build      # produces index.js
zip -r function.zip index.js

A bundled index.js inlines its dependencies, so it needs no node_modules/ in the zip at all.

Code Source

Code always comes from S3 — codeConfig.s3Object is the only source, and it is required.

"codeConfig": {"s3Object": {"bucket": "{bucket}", "key": "{name}/function.zip", "versionId": "{version}"}}

versionId is optional and works the same way on create-web-function and create-web-function-revision, so you can deploy updated code by re-uploading to the same key and passing the new version instead of changing the key.

RevisionConfig Fields

description and kmsKeyArn are optional on both create commands, but they are passed differently: inside --revision-config for create-web-function, and as separate --description / --kms-key-arn flags for create-web-function-revision, which takes no --revision-config.

FieldPurpose
descriptionFree-text label for the revision (helps identify revisions in list-web-function-revisions)
kmsKeyArnCustomer-managed KMS key ARN to encrypt the code artifact / environment variables (defaults to an AWS-managed key if unset)
"revisionConfig": {
  "description": "Add /metrics endpoint",
  "kmsKeyArn": "arn:aws:kms:{region}:{account}:key/{key-id}",
  "buildConfig": { "...": "..." },
  "serviceConfig": { "...": "..." }
}

EndpointConfig Reference

FieldRequiredValues
endpointNameYesAny string (typically "default")
endpointTypeYesHomeRegion, MultiRegion, PerRegion
authTypeYesApplicationManaged, IamAuth
autoDeploymentModeNoLatestRevision (default), Disabled
revisionWeightsConditionalRequired when autoDeploymentMode is Disabled, and rejected when it is LatestRevision. Array of 1–2 {"revisionId", "weight"} entries — revision IDs must be unique, each weight is 1–100, and the weights must sum to exactly 100.
regionsConditionalThe home region is always added automatically. For HomeRegion, omit it or list only the home region; any other region is rejected. For MultiRegion / PerRegion, at least 2 distinct regions are required — listing the home region alongside the others is fine, so it is enough to add 1 other region.
scalingConfigNo{"maxEnvironments": N} — max concurrent execution environments for the endpoint.
throttleConfigNo{"rateLimit": N} — max requests per second for the endpoint.

Constraint: MultiRegion and PerRegion endpoints require autoDeploymentMode: Disabled. LatestRevision is only supported for HomeRegion endpoints.

Timeout

Request timeout is configurable via serviceConfig.timeoutSeconds (see Service Limits in SKILL.md for the range and default). Changing this requires a new revision.

"serviceConfig": {
  "executionRoleArn": "...",
  "timeoutSeconds": 120
}

Concurrency

Each execution environment handles multiple concurrent requests. Write thread-safe application code — avoid global mutable state.

Configure a non-default concurrency limit via serviceConfig.maxConcurrencyPerEnvironment (see Service Limits in SKILL.md for the range and default). Changing this requires a new revision.

"serviceConfig": {
  "executionRoleArn": "...",
  "maxConcurrencyPerEnvironment": 64
}

See scaling-and-concurrency.md for more details.

Endpoint Scaling and Throttling

Concurrency above is configured on the revision; these two are configured on the endpoint, so they can be changed with update-web-function-endpoint without creating a revision.

{
    "scalingConfig": {"maxEnvironments": 20},
    "throttleConfig": {"rateLimit": 1000}
}

See scaling-and-concurrency.md for more details.

Environment Variables

Set via serviceConfig.environmentVariables (not buildConfig — this is a common mistake). Changing this requires a new revision.

"serviceConfig": {
  "executionRoleArn": "...",
  "environmentVariables": {"TABLE_NAME": "my-table", "NODE_ENV": "production"}
}

Important UX notes:

  • There is no standalone "update env vars" command. Changing any environment variable requires creating a new revision with the full --build-config + --service-config.

  • Keys starting with AWS_LAMBDA_ are reserved and will be rejected with InvalidParameterValueException.

  • NODE_ENV: production is a Node.js optimization flag, not a deployment-stage label. Express caches view templates and returns the status-code message instead of err.stack only when it is exactly production; libraries test === 'production', so staging silently selects the development path and leaks stack traces to callers. Keep it production in deployed revisions — see Express: Set NODE_ENV to production.

  • Do not put secrets here. Environment variables are returned in plaintext by get-web-function-revision, so anyone with read access to the revision can read them. Keep API keys, database credentials and tokens in AWS Secrets Manager or SSM Parameter Store, pass only the secret name or parameter path as an environment variable, and fetch the value at runtime via the SDK. Grant the execution role read access to just that secret or parameter.

Runtime-Injected Environment Variables

These are set by the runtime and readable from customer code (but not overridable):

Env VarValuePurpose
AWS_LAMBDA_HTTP_ENDPOINT0.0.0.0:3000Host:port the runtime probes for HTTP readiness
AWS_LAMBDA_INITIALIZATION_TYPEnative-httpDistinguishes Web Functions from Event Functions
AWS_LAMBDA_METADATA_API169.254.100.1:9001Internal metadata endpoint
AWS_LAMBDA_METADATA_TOKEN(unique per execution environment)Auth token for metadata API
AWS_LAMBDA_RUNTIME_API169.254.100.1:9001Runtime API endpoint

Logs

CloudWatch Logs, centralized to the function's home region. The log group and log levels are configurable via serviceConfig.telemetryConfig.loggingConfig (defaults to /aws/lambda/web/{function-name} at INFO if unset). Each function also gets a {logGroup}.local group holding just the logs of the region that served them. See observability.md for the full logging config.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server, Needs network access, Needs a command-line tool, Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
AWS
Publisher
AWS (Official)
Source
aws/agent-toolkit-for-aws · skills/specialized-skills/serverless-skills/aws-lambda-web-functions · commit 0a218f8
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • NOTICE (added by AutoGineer from NOTICE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by AWS or AWS.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download aws-lambda-web-functions.zip