AWS Amplify Gen 2 のコスト最適化のためにビルド時間を半分に短縮してみた
いわさです。
先日、Kiro Web のオートメーションと Amplify Gen 2 の PR プレビュー機能を組み合わせて「寝ている間に AI がコードを書いて PR を出してくれる」仕組みを紹介しました。
この記事の中で課題として「Amplify デプロイに 15 分近くかかる」と書いていたのですが、今回はこれを解消します。
Amplify Hosting ではビルド分数に応じた従量課金が発生します。
PR プレビュー機能を有効にしていると、PR を作成するたびにプレビュー用のブランチデプロイも追加で走るため、ビルド回数はどんどん増えていきます。
Cost Explorer で確認すると APN1-BuildDuration の利用量がなかなかのものでした。

今回ビルドログを分析してデフォルトの buildSpec を見直したところ、ビルド時間を約半分に短縮できたので、やったことを紹介します。
対象アプリケーションは React 18 + Vite 6 + TypeScript のフロントエンドと、Amplify Gen 2(CDK ベース)のバックエンド(AppSync + Lambda × 11 + DynamoDB)で構成されています。
リポジトリのルートに単一の package.json があり、依存パッケージ数は 2975 です。
今回こちらを確認してみたので紹介します。
デフォルトの buildSpec には最適化の余地がある
デプロイ履歴を見ると、ビルド期間は 12分42秒〜16分33秒。概ね13〜16分の範囲に収まっています。

Amplify Gen 2 でアプリをセットアップすると、以下のような buildSpec がデフォルトで生成されます。
version: 1
backend:
phases:
build:
commands:
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
phases:
preBuild:
commands:
- npm ci --cache .npm --prefer-offline
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- .npm/**/*
- node_modules/**/*
ざっくり言うと、backend フェーズで npm ci + CDK デプロイを行い、frontend フェーズでもう一度 npm ci してからフロントエンドをビルドする、という流れです。
cache.paths には .npm(ダウンロード済み tarball)と node_modules(インストール済みパッケージ)の両方がキャッシュ対象に含まれています。
ビルドログからフェーズごとの時間を抽出すると、ボトルネックが見えてきます。
| フェーズ | 所要時間 |
|---|---|
| リポジトリクローン | 2秒 |
| キャッシュ取得 | 30秒 |
| キャッシュ展開 | 1分 |
Backend: npm ci |
4分10秒 |
Backend: npx ampx pipeline-deploy |
41秒 |
Frontend: npm ci(2回目) |
3分36秒 |
Frontend: npm run build(tsc + vite) |
34秒 |
| キャッシュ作成 + アップロード | 2分12秒 |
合計で約13.5分。
フロントエンドのビルド自体(Vite)は34秒で終わっているのに、依存関係のインストールとキャッシュ処理で大半の時間を消費しています。
npm ci が2回走っている
backend フェーズと frontend フェーズでそれぞれ npm ci が実行されており、同じ package-lock.json に対して2回フルインストールが走っています。
今回のアプリケーションはルートに package.json が1つの構成なので、backend フェーズで入った node_modules がそのまま frontend のビルドでも使えます。
2回目の npm ci は完全に無駄で、これだけで約4分も消費しています。
デフォルトテンプレートがこうなっているのは、backend と frontend で異なる package.json を持つモノレポ構成を想定しているためです。
単一の package.json 構成であれば1回で十分です。
node_modules をキャッシュしても npm ci が毎回消す
cache.paths に node_modules/**/* が含まれていますが、npm ci は設計上 node_modules ディレクトリを完全に削除してからクリーンインストールします。
つまり以下のことが毎回起きています。
- キャッシュから
node_modulesを1分かけて展開する npm ciがnode_modulesを削除してゼロからインストールする- ビルド後に
node_modules(2975パッケージ分)を2分かけて圧縮・アップロードする
展開も圧縮も無駄、無駄、無駄です。
.npm キャッシュだけ残しておけば、--prefer-offline によりネットワーク通信をスキップしてローカルのキャッシュからインストールできます。
対応内容と結果
変更後の buildSpec は以下です。
version: 1
backend:
phases:
build:
commands:
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
phases:
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- .npm/**/*
変更点は2つだけです。
frontend.phases.preBuildセクションを削除(2回目のnpm ciを撤廃)cache.pathsからnode_modules/**/*を削除
backend フェーズでインストールした node_modules がそのまま残っているので、frontend ではそのまま npm run build を実行するだけで済みます。
結果、デプロイ 89 で変更が反映されました。

| 変更前 | 変更後 | |
|---|---|---|
| Backend: npm ci | 4分10秒 | 3分42秒 |
| Backend: ampx pipeline-deploy | 41秒 | 2分21秒(実変更あり) |
| Frontend: npm ci | 3分36秒 | なし |
| Frontend: npm run build | 34秒 | 28秒 |
| キャッシュ作成 + アップロード | 2分12秒 | 8秒 |
| 合計 | 約13.5分 | 約9分 |
変更前は13〜14分かかっていたビルドが 8〜9分 に短縮されました。
なお、今回はバックエンドに実際のリソース変更(Lambda 関数の更新)が含まれていたため CDK デプロイに2分21秒かかっています。
フロントエンドのみの変更であれば CDK デプロイは no changes で数秒で済むので、通常時は 7〜8分 程度になる見込みです。
キャッシュの作成+アップロードが 2分12秒 → 8秒 になったのが特に効いています。
node_modules(2975パッケージ分)を圧縮・アップロードする処理がまるごと不要になったためです。
反映時の注意点:amplify.yml の優先順位
今回の作業中にひとつハマりポイントがありました。
最初に Amplify コンソール側の buildSpec を変更したのですが、PR プレビューのブランチではビルド時間が全く短縮されませんでした。
PR プレビュー機能が有効になっているため、PR を作成するたびにプレビュー用のビルド・デプロイが走ります。

PR プレビューのデプロイ履歴を見ると、ビルド期間が 17分46秒。
短くなるどころかむしろ長くなっています(PR ブランチは初回デプロイのため CloudFormation スタックの新規作成が発生するため)。

原因は Amplify の buildSpec の優先順位です。
- リポジトリのルートに
amplify.ymlがある場合、コンソール側の設定よりリポジトリのファイルが優先される - PR プレビューブランチには変更前の
amplify.ymlがまだ残っていたため、旧設定でビルドが走っていた
GitHub でリポジトリを確認すると、確かに amplify.yml がまだ存在しており、中身も旧設定のままでした。


最終的にリポジトリから amplify.yml を削除し、コンソール側の設定のみで運用する形にしました。
こうすることで、全ブランチに対して即座に buildSpec の変更が反映されます。
逆に、ブランチごとに異なる buildSpec を使いたい場合は amplify.yml をリポジトリに置く方が都合が良いですが、その場合は全ブランチに変更を反映するのに手間がかかる点に注意が必要です。
さいごに
本日は AWS Amplify Gen 2 のデフォルト buildSpec を見直してビルド時間を短縮してみました。
やっていることは今回は npm ci の重複削除と不要なキャッシュ対象の除外でしたが、対応すべき内容はアプリケーションスタックによって変わります。このあたりはビルドログを分析しつつワークロードにあわせて適宜対応方法は検討しましょう。
知っておくべきことは Amplify ではデフォルトのビルドフローになっているので最適化余地があるかもよという点です。








