AWS Amplify Gen 2 のコスト最適化のためにビルド時間を半分に短縮してみた

AWS Amplify Gen 2 のコスト最適化のためにビルド時間を半分に短縮してみた

前回紹介した「寝ている間にAIがコードを書いてPRを出してくれる仕組み」で、Amplifyデプロイに15分近くかかっていた課題を解消します。buildSpecを見直して、ビルド時間を約半分に短縮できたので、やったことを紹介します。
2026.07.05

いわさです。

先日、Kiro Web のオートメーションと Amplify Gen 2 の PR プレビュー機能を組み合わせて「寝ている間に AI がコードを書いて PR を出してくれる」仕組みを紹介しました。

https://dev.classmethod.jp/articles/kiroweb-amplifygen2-pr-kobito/

この記事の中で課題として「Amplify デプロイに 15 分近くかかる」と書いていたのですが、今回はこれを解消します。

Amplify Hosting ではビルド分数に応じた従量課金が発生します。
PR プレビュー機能を有効にしていると、PR を作成するたびにプレビュー用のブランチデプロイも追加で走るため、ビルド回数はどんどん増えていきます。
Cost Explorer で確認すると APN1-BuildDuration の利用量がなかなかのものでした。

1E64797A-8912-436B-B254-FB2D767778D6.png

https://aws.amazon.com/amplify/pricing/

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

デフォルトの buildSpec には最適化の余地がある

デプロイ履歴を見ると、ビルド期間は 12分42秒〜16分33秒。概ね13〜16分の範囲に収まっています。

D1E5740E-5232-40F0-9852-3C6AD9825D13_1_105_c.jpeg

Amplify Gen 2 でアプリをセットアップすると、以下のような buildSpec がデフォルトで生成されます。

amplify.yml
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.pathsnode_modules/**/* が含まれていますが、npm ci は設計上 node_modules ディレクトリを完全に削除してからクリーンインストールします。
つまり以下のことが毎回起きています。

  1. キャッシュから node_modules を1分かけて展開する
  2. npm cinode_modules を削除してゼロからインストールする
  3. ビルド後に node_modules(2975パッケージ分)を2分かけて圧縮・アップロードする

展開も圧縮も無駄、無駄、無駄です。
.npm キャッシュだけ残しておけば、--prefer-offline によりネットワーク通信をスキップしてローカルのキャッシュからインストールできます。

対応内容と結果

変更後の buildSpec は以下です。

amplify.yml
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つだけです。

  1. frontend.phases.preBuild セクションを削除(2回目の npm ci を撤廃)
  2. cache.paths から node_modules/**/* を削除

backend フェーズでインストールした node_modules がそのまま残っているので、frontend ではそのまま npm run build を実行するだけで済みます。

結果、デプロイ 89 で変更が反映されました。

F401EA59-FFEA-4B04-A574-18C0484B3C59.png

変更前 変更後
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 を作成するたびにプレビュー用のビルド・デプロイが走ります。

6C8085FD-42AC-470A-8164-81C254FE7A5B_1_105_c.jpeg

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

25E0A0E1-7EE9-4F0D-834D-568237654F98_1_105_c.jpeg

原因は Amplify の buildSpec の優先順位です。

  • リポジトリのルートに amplify.yml がある場合、コンソール側の設定よりリポジトリのファイルが優先される
  • PR プレビューブランチには変更前の amplify.yml がまだ残っていたため、旧設定でビルドが走っていた

GitHub でリポジトリを確認すると、確かに amplify.yml がまだ存在しており、中身も旧設定のままでした。

FA113670-C2A6-4648-9241-4FCBF11D4C19.png

C2FF037C-0157-4BC0-BD5B-E4D1B02167AC.png

最終的にリポジトリから amplify.yml を削除し、コンソール側の設定のみで運用する形にしました。
こうすることで、全ブランチに対して即座に buildSpec の変更が反映されます。

逆に、ブランチごとに異なる buildSpec を使いたい場合は amplify.yml をリポジトリに置く方が都合が良いですが、その場合は全ブランチに変更を反映するのに手間がかかる点に注意が必要です。

さいごに

本日は AWS Amplify Gen 2 のデフォルト buildSpec を見直してビルド時間を短縮してみました。

やっていることは今回は npm ci の重複削除と不要なキャッシュ対象の除外でしたが、対応すべき内容はアプリケーションスタックによって変わります。このあたりはビルドログを分析しつつワークロードにあわせて適宜対応方法は検討しましょう。
知っておくべきことは Amplify ではデフォルトのビルドフローになっているので最適化余地があるかもよという点です。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事