はじめに
2026年10月12日、AWS Lambda に「Web Functions」が一般提供(GA)になりました。
Announcing AWS Lambda Web Functions
Next.js や Express のような普通の Web サーバーを、アダプターなしで Lambda に載せられる新しい関数です。
私は普段 Google Cloud をメインに開発をしていて、Web アプリや API は Cloud Run で動かすことが多いです。Cloud Run も HTTP リクエストを受けて動くサーバーレスのサービスなので、Lambda Web Functions とどのくらい違うのかが気になりました。
そこで同じ Next.js 16 のアプリを Lambda Web Functions と Cloud Run の両方にデプロイして違いを調べてみました。
Lambda Web Functions とは
今回のアップデートで Lambda は3種類になり、従来のイベント駆動の Lambda は「Event Functions」という名前に変わりました。今回そこに「Web Functions」と「Lambda MicroVMs」が加わっています。
Web Functions は、アプリがポートで待ち受けて、普通の Web サーバーとしてリクエストを受ける形になっていて、API Gateway や Lambda 用のアダプターは必要ありません。
公式では Express、Hono、Fastify、Next.js などがそのまま動くと案内されています。
What's New: AWS Lambda Web Functions
主な仕組みは次のとおりです。
-
関数・リビジョン・エンドポイント:
- リビジョンはコードと設定の変更できないスナップショットで、エンドポイントが HTTPS の URL
- 1つのエンドポイントで、最大2つのリビジョンに重みを付けて振り分けられる
-
実行環境:
- Node.js 24、Arm64、2 vCPU / 2 GB で固定
- 1つの環境が、既定で64件(最大128件)まで同時にリクエストを受信
-
スケール:
- リクエストに応じて環境が増減し、ゼロまで縮みます。最小の環境数は設定不可
-
認証:
- エンドポイントごとに「なし(アプリ側で行う)」か「IAM(SigV4)」を選択
-
ネットワーク:
- VPC の中のリソースには、egress network connector を通して繋ぐ
-
リージョン:
- 東京(ap-northeast-1)を含む5リージョンで利用可能
Cloud Run との違い
Cloud Run はコードやコンテナを Google Cloud のインフラ上で動かせるフルマネージドのサービスです。中でも「サービス」は HTTPS のエンドポイントを持ち、リクエストの数に応じて自動でスケールし、リクエストがなければインスタンスがゼロまで縮むので、Lambda Web Functions と同じ立ち位置になります。
(もちろんサービスのコンセプトから異なる部分はあります)
以前の Cloud Functions も今は Cloud Run functions として Cloud Run に含まれているので、Google Cloud で Web アプリや API をサーバーレスで動かすなら、Cloud Run が比較の相手になると考えました。
今回は Cloud Run のサービスと比べていますが、主な違いは次のとおりです。
| Lambda Web Functions | Cloud Run | |
|---|---|---|
| 言語 | Node.js 24 のみ | コンテナなら何でも |
| サイズ | Arm64、2 vCPU / 2 GB 固定 | 0.08〜8 vCPU、最大 32 GiB |
| コードの渡し方 | .zip(S3)のみ | コンテナイメージ、ソース |
| 最小インスタンス | 設定なし | あり |
| WebSocket | 不可 | 可 |
| 課金 | 使った CPU 時間+使ったメモリ × 環境がある時間 | 処理中の時間(または起動中ずっと) |
Lambda 側は、2026年10月16日時点の開発者ガイドの内容をもとにしています。
表にしてみるとCloud Run と比べて選べる項目はかなり少ないことがわかります。
特に最小インスタンスが設定できない点と WebSocket が使えない点は、Cloud Run に慣れていると気になるところですね。
Next.js をデプロイする
Next.js は output: "standalone" でビルドし、起動用のファイルを1つ追加したら動きました。
Lambda は待ち受けるホストとポートを環境変数 AWS_LAMBDA_HTTP_ENDPOINT(host:port の形)で渡してきます。
一方で Next.js の standalone サーバーは HOSTNAME と PORT を読むので、起動前に値を詰め替えておく必要があります。
// Lambda は既定で index.js を起動する
if (process.env.AWS_LAMBDA_HTTP_ENDPOINT) {
const [host, port] = process.env.AWS_LAMBDA_HTTP_ENDPOINT.split(':');
process.env.HOSTNAME = host;
process.env.PORT = port;
}
require('./server.js');
準備: S3 バケットと実行ロール
コードは S3 経由で渡すため、バージョニングを有効にしたバケットを用意します。
実行ロールは lambda.amazonaws.com を信頼させ、権限は CloudWatch Logs への書き込みだけにしています。
aws s3api create-bucket --bucket <バケット名> --region ap-northeast-1 \
--create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-bucket-versioning --bucket <バケット名> \
--versioning-configuration Status=Enabled
aws iam create-role --role-name lwf-verify-exec-role \
--assume-role-policy-document file://exec_role_trust_policy.json
aws iam put-role-policy --role-name lwf-verify-exec-role \
--policy-name lwf-verify-logs --policy-document file://exec_role_permissions_policy.json
ビルドしてデプロイ
ビルドしたらstandalone の出力に静的ファイルと先ほどの index.js をまとめて、aws lambda-web deploy に渡します。
npm run build
cp -R .next/static .next/standalone/.next/static
cp -R public .next/standalone/public
# .next/standalone/index.js を置く(上のコード)
# Mac でビルドするなら Linux arm64 用の sharp も足す(npm install --os=linux --cpu=arm64 sharp)
aws lambda-web deploy --name lwf-verify-next --code ./.next/standalone \
--bucket-name <バケット名> \
--execution-role-arn arn:aws:iam::<アカウントID>:role/lwf-verify-exec-role \
--create
実行して14秒ほどで、HTTPS の URL が返ってきました。
Waiting for revision rev-jj9hm to become active (timeout 600s) ...
https://xxxxxxxx.kfn.lambda-web.ap-northeast-1.on.aws
コンソールを開いて確認してみると、関数と最初のリビジョン、HTTPS のエンドポイント(dev)がまとめて作成されていることがわかります。
今回は認証なしのエンドポイントなので、この URL をそのままブラウザで開けます。
今回は確認しやすいように、ページには実行環境の情報を表示させています。
arm64、Node.js v24、CPU 2つの環境で動いていることがわかります。
ログは CloudWatch Logs の /aws/lambda/web/<関数名> に出力されます。
起動(initStart)、listen、リクエストの順に並ぶため、コールドスタートだったかどうかもここで見分けられます。
Cloud Run 側は、同じコードを使って次のコマンドでデプロイしています。
gcloud run deploy lwf-verify-next --source ./next-app \
--region asia-northeast1 --cpu 2 --memory 2Gi --no-invoker-iam-check
Cloud Run と比べてみた
デプロイにかかった時間は Lambda が14秒、Cloud Run が2分50秒でした。
Cloud Run はソースからコンテナをビルドする工程がある分、時間がかかっていますね。
コールドスタートは20分放置して両方のインスタンスがゼロに戻ってから、最初の1件の応答時間を測ってみました。
4回とも新しい環境で起動していて、平均は Lambda が1.14秒、Cloud Run が2.54秒と、Lambda が半分以下という結果になりました。
負荷時のスケーリングについて
次にSSR のページに同時50件のリクエストを30秒流して応答速度やスケーリングの違いについて確認してみました。
p50 はどちらも約1.54秒ほどで応答の速さに差はありませんでしたが、裏側の動きはそれぞれでかなり違いがありました。
Lambda は環境を36個立ち上げていて、1つの環境が同時に処理していたのは最大でも2件となっていました。
一方で Cloud Run は、1台のインスタンスで全部を受けていました。
Cloud Run は、新しく作ったサービスだと1台が同時に受けられるリクエストの数が既定で「80 × vCPU 数」(今回は160件)となっているようで、CPU 使用率が6割、または同時に受けているリクエストが上限の6割を超えるとインスタンスを増やす仕組みのようです。
今回の50件は1台で十分に受けられる量だったため、インスタンスが増えなかったのだと思いますが、最大インスタンス数(既定100)はあくまで上限なので、ここを変えても結果は同じになると思います。
Lambda も1つの環境で同時に受けられるのは既定で64件なので、1つの環境にもっと詰め込めるはずですがLambda は環境を増やして分散させる動きをするようです。
この動きで影響が出るのが DB につなぐ場合で、同時100件のリクエストで Aurora への接続が 145本 になりました。
DB につなぐときは接続数に注意
Lambda は環境を増やして分散させるため、環境ごとに持つ接続プールがそのまま積み上がります。今回は同時100件で環境が72個まで増え、Aurora への接続は145本になりました。
本番で使うなら、RDS Proxy を挟むか、maxEnvironments で環境の数に上限をかけておきたいところですね。
Cloud Run でやっていることはできるか
普段 Cloud Run で動かしているような構成が、Lambda Web Functions でも組めるかを試しました。
| やりたいこと | 結果 | メモ |
|---|---|---|
| AI エージェント(Bedrock の Claude Haiku 4.5) | ○ | 1件5.8秒のうち、CPU は0.2秒 |
| VPC 越しの DB(Aurora Serverless v2) | ○ | 接続数に注意(上記) |
| IAM 認証 | ○ |
IamAuth にすると署名なしは 403 |
| カナリアリリース | △ |
deploy は新リビジョンに100%切り替える。重みの反映に15〜30秒 |
| コンテナイメージ | × | .zip のみ |
| WebSocket | × |
Upgrade ヘッダーが消される |
AI エージェントは、Express のアプリから Bedrock の Claude Haiku 4.5 を呼び、ツールを使わせる形で作りました。
ログを見てみると1件5.8秒のうちモデルの応答待ちが4.3秒、ツールの待ちが1.5秒で、CPU を使っていたのは0.2秒だけでした。処理時間のほとんどが待ち時間になっていることがわかります。
エンドポイントの認証を IamAuth にすると、ブラウザから署名なしで開いた場合は 403 が返ってきます。
DB を使う3層構成では、VPC の egress network connector 経由で Aurora Serverless v2 につなぎ、IAM 認証で接続しました。
ブラウザのフォームから書き込んだメモが、VPC の中の Aurora に保存され、一覧に表示されています。
注意点として、VPC への接続はリビジョンの設定になるため、deploy コマンドでは付けられません。create-web-function-revision で、接続を設定したリビジョンを作成します。
カナリアリリース
今回の検証で一番手間がかかったのがカナリアリリースです。
aws lambda-web deploy は新しいリビジョンに100%切り替えてしまうので、create-web-function-revision でリビジョンを作ってから、エンドポイントの重みを変更する方針で進めています。
aws lambda-web update-web-function-endpoint \
--function-name lwf-verify-ops --endpoint-name dev \
--revision-weights revisionId=rev-l04as,weight=90 revisionId=rev-x5oct,weight=10
設定自体は4秒で終わりますが、実際に振り分けが効き始めるまでには15〜30秒かかりました。
設定の直後に1,000件流したところ、新しいリビジョンに届いたのは6件だけでした。
Cloud Run の gcloud run services update-traffic --to-revisions では、設定の直後から約1割が新しいリビジョンに振り分けられていたので、ここは Cloud Run のほうが扱いやすいと感じました。
料金
Lambda Web Functions は、CPU は使った時間だけ、メモリは「使っている量 × 環境がある時間」で課金されます。
ここでメモリが割り当ての 2 GB で計算されるのか、実際に使った量で計算されるのかが気になったので、請求(Cost Explorer)で確認しました。
負荷テスト1時間分の請求は 3,951 GB 秒で、メトリクスから計算した実使用量(約3,800 GB 秒)とほぼ一致しています。
2 GB で計算されていれば約4万3,600 GB 秒になるはずなので、実際に使った量で課金されていることがわかります。
AWS の料金ページに載っている4つのワークロードの例を、東京リージョンの単価で両方に当てはめて試算してみました。
待ち時間が長い処理ほど、Lambda のほうが安くなる結果になりました。
AI エージェントのように、処理時間のほとんどがモデルの応答待ちになる用途では、CPU の課金がほとんど発生しないためです。
逆に、短いリクエストが大量に来るような用途では、Cloud Run のほうが安くなっています。
まとめ
今回は、GA になったばかりの Lambda Web Functions に Next.js をデプロイして、Cloud Run と比較してみました。
Next.js は数行のファイルを追加するだけで動き、コールドスタートも Cloud Run の半分以下だったので、思っていた以上に普通の Web アプリの置き場所として使えると感じました。
個人的には、新しく作る AI エージェントや、外部 API の応答を待つことが多い API から試してみるのがよさそうです。DB を使う場合は、環境がどんどん増える前提で、接続数の絞り方を最初に決めておくと安心です。
デプロイの手順やコマンドも載せているので、ぜひ手元でも試してみてください。















