はじめに
こんにちは。クラウドセントリック株式会社の山下です。普段はAWSを活用したフルスタックアプリ開発に携わっています。
私は実務で主にGitHub Actionsを使っていて、デプロイは手動実行(workflow_dispatch)か、特定のブランチへのマージをトリガーに自動デプロイで動かしていました。AWSのデプロイ用のサービスであるAWS CodePipelineも、たまに使う程度には触っています。それでも、CI/CDにおいて「CodePipelineとGitHub Actionsは何が違うのか」と聞かれたら答えられないな、と思ったので、それなら実際に比べてみよう、ということでこの記事を書きました。
同じAWS CDKアプリを、GitHub Actionsで完結させるデプロイと、CodePipelineを使ったデプロイの両方で動かす環境をAWSの検証用アカウントに作り、料金・速さ・承認の違いを中心に比べてみました。「こちらが良い」という一つの結論はなく、軸ごとに有利な方が違うので、最後に「こういう場合はこちら」という表にまとめています。良ければご覧ください。
結論
先に結論です。今回の構成・条件で測った結果であり、どのプロジェクトでも同じとは限りません。
| 比べたこと | 有利だった方 | 理由(数字) |
|---|---|---|
| 料金 | GitHub Actions | 1回あたり $0.012 対 $0.076(今回の計測、無料枠なしの計算で約6倍) |
| セットアップの手間 | GitHub Actions | CLI(コマンド)だけで完結する。CodePipelineは接続の承認でブラウザ操作が要る |
| デプロイの速さ | GitHub Actions | 1分07秒 対 6分32秒 |
| 承認 | CodePipeline | デプロイの直前で止めて人の判断を待つ仕組みが、標準で使える(GitHub Actionsで同じことをするには、公開リポジトリかEnterpriseでEnvironmentsの必須レビュアーが要る) |
| 失敗したときのエラーの読みやすさ | CodePipeline | 最初に失敗したリソースと理由が整理されて出る |
1. 検証の前提と構成
GitHub ActionsとCodePipelineを、同じアプリのデプロイで比べます。どちらもコードはGitHubのリポジトリに置いていて、違うのは「ビルドとデプロイをどこで動かすか」です。GitHub Actionsだけで完結させる(GitHub上で動く)か、CodePipelineで動かして、GitHubはソースとしてつなぐだけにする(AWS上で動く)か、を比べています。
検証したのは最小構成なので、複数のAWSアカウントにまたがるデプロイ、ECSなど複雑なデプロイ先、チーム開発での段階的なデプロイは試していません。
1-1. 同じCDKアプリを2つの方式でデプロイする
デプロイするのは、AWS CDK(TypeScriptでAWSのリソースを書く仕組み)で作った、非公開のAmazon S3バケットに index.html を置くだけのアプリです。同じアプリを2つのスタックに分け、片方はGitHub Actions(GitHub上で動く)から、もう片方はCodePipeline(AWS上で動き、GitHubはソースとしてつなぐだけ)からデプロイします。CodePipeline側はCDK Pipelines(CDKでパイプラインを組むためのライブラリ)で組みました。

1-2. 使った技術と環境
- IaC: AWS CDK v2(TypeScript、aws-cdk-lib 2.270.0、CDK CLI 2.1143.0)
- GitHub Actions:
ubuntu-latest、Node.js 22(actions/setup-node)、actions/checkout@v7・actions/setup-node@v7・aws-actions/configure-aws-credentials@v6 - CodePipeline: AWS CodePipeline V2(CDK Pipelines、
aws-cdk-lib/pipelines)、AWS CodeBuild(BUILD_GENERAL1_SMALL) - デプロイ先: Amazon S3(非公開バケット)と
BucketDeployment - リージョン:
ap-northeast-1(東京)
1-3. GitHubは個人アカウントの無料プラン・非公開リポジトリ
今回は無料プランでやりますが、プランによって使える機能が違います。詳細は以下の表です(出典: Environmentsと必須レビュアー、Environmentsの作成、ブランチ保護、Rulesets)。
| 機能 | 個人 Free・非公開(今回) | 公開リポジトリ | Pro / Team・非公開 | Enterprise・非公開 |
|---|---|---|---|---|
| Environments(環境)の作成 | 使えない | 使える | 使える | 使える |
| Environmentsの必須レビュアー(承認) | 使えない | 使える | 使えない | 使える |
| ブランチ保護・Rulesets | 使えない | 使える | 使える | 使える |
つまりこの記事のGitHub側は、承認もブランチ保護も使えない条件での比較です。公開リポジトリかGitHub Enterpriseなら承認(必須レビュアー)が、Pro/Teamならデプロイできるブランチの制限が使えるので、4章の承認のしかたの結果は変わる可能性があります。
2. セットアップの手間を比べる
1章の構成を動かすには、それぞれ最初に何が必要か。まずは、動かすまでの準備の手間を順に見ていきます。
2-1. GitHub Actions: CLIだけで完結する
必要だったのは、IAMロールの作成(cdk deploy で58〜65秒)、ロールのARNのリポジトリ変数への登録(gh variable set AWS_ROLE_ARN)、ワークフローのYAMLの3つです。すべてコマンドとファイルで済み、ブラウザでの操作は要りませんでした。GitHubのOIDCプロバイダ(AWSがGitHubを信頼するための登録)は既にあったので作成を省きました。ワークフローの要点は次のとおりです。
permissions:
id-token: write # OIDCトークンを発行するために必要
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- run: npx cdk deploy test-cicd-site-actions-yamashita --require-approval never
OIDCの sub の形式でつまずいた
OIDC(GitHubが発行したトークンで、AWSに「このリポジトリの実行です」と証明する仕組み)のトークンの sub という項目は、GitHubの変更により、2026年7月15日より後に作った(または改名・移管した)リポジトリでは名前だけでなくオーナーとリポジトリの数値IDが入る形式になります。IAMロールの信頼ポリシーはこの形式で書かないと、AssumeRoleWithWebIdentity で弾かれます。従来形式で書かれた手順も多いので、新しいリポジトリでは注意してください。
2-2. CodePipeline: 接続の承認でブラウザ操作が要る
CodePipeline側は、GitHubとの接続(CodeConnections)の作り方に手間がかかりました。aws codeconnections create-connection でCLIから接続を作ると、ステータスは PENDING(保留中)になります。CLIだけでは使える状態にならず、コンソールで承認(GitHub Appのインストールとリポジトリの選択)が必要です。コンソールで承認すると AVAILABLE になります。GitHub Actionsのように、CLIだけでは完結しませんでした。

その後、パイプライン自体を作るために手元から cdk deploy を1回だけ実行します。一度作ってしまえば、パイプラインの構成(ステージやアクションの並び。今回はCDKのコードで書いています)を変えるときも、コードを直してpushするだけで済みます。パイプラインが自分自身を更新する仕組み(self-mutation)があるためです。
2-3. cdk init するとCodePipelineは既定でV2になる
CodePipelineにはV1(コード変更が流れたパイプライン1本あたり月額 $1.00)とV2(アクションの実行時間に応じた課金)があります(CodePipelineの料金)。cdk init で新規に作ったプロジェクトでは、cdk.json に @aws-cdk/aws-codepipeline:defaultPipelineTypeToV2(CDKのfeature flag)が true で生成され、pipelineType を指定しなくてもsynth結果は PipelineType: V2 でした。
古いプロジェクトは cdk.json を確認
古いプロジェクトはV1のままの可能性があります。料金の仕組みが変わるので、cdk.json の値を確認することをおすすめします。
3. デプロイの速さを比べる
ここからは、実際に動かして比べた結果です。まずは、pushしてからデプロイが終わるまでの時間です。
計測では、index.html に1行足したコミットを1回pushして、2つの方式を同時に動かしました。pushから実行が始まるまでは、どちらも2〜3秒でした。

| 方式 | 合計 | 内訳 |
|---|---|---|
| GitHub Actions | 1分07秒 | npm ci 6秒 / 認証 1秒 / cdk deploy 49秒 |
| CodePipeline | 6分32秒 | Source 8秒 / Synth 95秒 / SelfMutate 125秒 / Assets 94秒 / Prepare 33秒 / Deploy 33秒 |
今回の計測回は、CodeBuildの順番待ち(ビルドを受け付けてから、実行環境が割り当てられて動き出すまでの待ち時間。ビルドの記録では QUEUED として残り、この時間も課金されます)が合計約100秒あった回です。ほかの実行も含めると、CodePipelineは3分25秒〜6分32秒(承認待ちを除く)、GitHub Actionsは48秒〜2分14秒でした。
GitHub Actionsは1つのジョブで cdk deploy を1回叩くだけですが、CDK PipelinesはSynth・SelfMutate・Assetsの段階ごとにCodeBuildが起動し、起動と準備に1回20秒前後かかるため、時間が積み重なります。特に目立ったのがSelfMutate(パイプライン自身の更新)で、パイプラインの構成は変えていないのに毎回このステージを通り、今回の計測では125秒かかりました(ほかの実行では30〜60秒程度)。何度も直して試したい開発中は、体感の差が出る数字だと思います。
4. 承認のしかたを比べる
ここまでは、準備の手間と速さの比較でした。次は、デプロイの前に人が止めて判断する「承認」を、2つの方式でどう作れるかを比べます。
4-1. CodePipelineの手動承認
CodePipeline側では、デプロイの直前に手動承認のステップを足しました。コードに次の数行を足してpushするだけで、手元からの再デプロイは要りませんでした。
pipeline.addStage(new SiteStage(this, 'Site'), {
pre: [new pipelines.ManualApprovalStep('ApproveDeploy')],
});
pushで始まった実行は、SelfMutateでパイプラインを更新した時点で Cancelled になり、更新後のパイプラインで新しい実行が自動で始まって、ApproveDeploy で止まります。

パイプラインは、人が操作するまで止まって待ちます。承認は、コンソールの「レビュー」ダイアログから行います。

「承認します」を選んで送信すると(コメント欄は任意で、今回は LGTM と入れました)、Prepare、Deployと進みました。承認の記録(list-action-executions)には、承認した人のIAMロールとセッション名、時刻、コメントが残っていました。誰が・いつ・何とコメントして承認したかが、パイプラインの履歴に残るわけです。承認できる人は、IAMの codepipeline:PutApprovalResult の権限で決まります。
4-2. 無料プランの非公開リポジトリでは workflow_dispatch で代わりにする
GitHubには、デプロイの前に指定した人の承認を待つ機能(Environmentsの必須レビュアー)がありますが、無料プランの非公開リポジトリでは使えません。そこで、実務でやっていた workflow_dispatch を「承認の代わり」として試しました。main から手動実行すると、48秒で成功しました。
実行履歴には、実行した人(actor)と時刻が残ります。
この方法では、「途中で止まって判断を待つ」ことはできず、押した時点から最後まで動きます。
手動実行できるのは、ワークフローのファイルがデフォルトブランチ(今回は main。リポジトリによって名前は違います)にあるときだけで、そのうえで書き込み権限も必要です(公式ドキュメント、手動実行の手順)。コラボレーターがいない個人の非公開リポジトリでは、実質、本人だけのゲートです。
ブランチを切ってPRで進める運用では、PRをデフォルトブランチにマージしてから使えるようになります。なお、今回のワークフローのように push: branches: [main] をトリガーにしていれば、マージした時点で自動でも動きます。
余談
コメントを残す欄は標準ではありませんが、workflow_dispatch には inputs でコメント欄などを用意することができます(最大25個。公式ドキュメント)。
4-3. 信頼ポリシーをmainに絞って、別ブランチからの実行を止める
有料プラン(Pro/Team)であれば、「デプロイできるブランチの制限」を付けたEnvironmentをジョブに指定して防げますが、今回は無料プランなので、IAMロールの信頼ポリシーの sub を main に絞ってみました。効果を確かめるため、main 以外のブランチ(test-dispatch)を指定して workflow_dispatch で手動実行しました(画面のプルダウンでもブランチを選べます)。実行すると、configure-aws-credentials で AssumeRoleWithWebIdentity の権限がないエラーが出て、狙いどおり失敗してくれました。

GitHub側に守りがない分、IAMロールの信頼ポリシーが最後の守りになることが確認できました。
4-4. 比較表
| 比べたこと | CodePipeline 手動承認 | GitHub Actions workflow_dispatch(個人Free・非公開) |
|---|---|---|
| 流れ | pushでビルドまで自動、デプロイ直前で止まって待つ。承認で再開 | 人が「Run workflow」を押したときに、最初から最後まで動く |
| 記録 | 承認者・時刻・コメントが履歴に残る | 実行者・時刻が残る(コメントはない) |
これまで承認の手順を踏んだことがなかったので、「ビルドまで済ませたうえで、デプロイだけ人が決める」形が標準機能で作れるのは、CodePipelineならではだと感じました。ただし、GitHub側でも公開リポジトリか、GitHub Enterpriseの非公開リポジトリであれば、Environmentsの必須レビュアーで似た承認を作れるようです。
なお、3章の速さと6章の料金(順番待ちがなかった回の計算を除く)は、承認のステップを入れる前の実行で測っています。承認を入れずにpushで自動デプロイするなら、CodePipelineもGitHub Actionsの push トリガーと同じ使い方になります。その場合の違いは、速さ(1分07秒 対 6分32秒)、料金、セットアップの手間になります。
5. 失敗したときの追いやすさを比べる
承認の次は、デプロイが失敗したときです。運用では、成功する場面だけでなく、失敗したときに原因へどれだけ早くたどり着けるかも気になります。そこで、わざと失敗させて、2つの方式の見え方を比べました。
わざと失敗するコミットを1回pushしました。SiteStack に、大文字入りのバケット名(test-cicd-INVALID-yamashita)のリソースを一時的に足しています。synthは通り、デプロイで落ちます。
GitHub Actionsは、2分05秒で失敗しました。cdk deploy のログに、CloudFormationのイベントがそのまま流れます。
CREATE_FAILED | AWS::S3::Bucket | BrokenBucket Resource handler returned message: "Bucket name should not contain uppercase characters"
原因までは、「失敗した実行を開く」「cdk deploy のステップを開く」の2手でした。
CodePipelineは、承認を経てDeployアクションが失敗しました。失敗したアクションの「概要」タブには、Failed to execute change set. というエラーに加えて、最初に失敗したリソース(BrokenBucket)とその理由が整理されて出ます。

手順はどちらも2手ですが、要点が整理されて出るぶん、CodePipelineのほうが読みやすいと感じました。
今回は、直したコミットをpushして復旧しました(失敗した実行を再試行しても、同じ成果物で同じ失敗になるため)。git revert をpushすると、GitHub Actionsは57秒で成功しましたが、CodePipelineはデプロイの前に承認待ちで止まるので、復旧にも承認が1回必要でした。承認は安全弁としてはよいですが、hotfixだったりすると少し手間を感じてしまうのは正直なところです。
とはいえ、ログを読むのをコーディングエージェントに任せるなら、どちらでもよい気がしてしまいますが(笑)
6. 気になる月間コストはどれくらい?
最後の比較は料金です。3章で測った実行時間をもとに、1回あたりと月あたりのコストを試算します。
3章の実測時間に、各サービスの単価(2026年9月時点)を掛けて試算しました。デプロイ先(S3など)の料金は両方式で同じなので比較から外しました。パイプラインのアーティファクト保存などの少額の費用も外しています。
| 項目 | 単価 | 課金の単位 |
|---|---|---|
| CodePipeline V2 | $0.002 / アクション実行分 | アクションごとに分単位で切り上げ(手動承認は対象外) |
CodeBuild(general1.small、東京) |
$0.005 / 分 | ビルドごとに分単位で切り上げ |
| GitHub Actions(Linux 2-core、非公開リポジトリ) | $0.006 / 分 | ジョブごとに分単位で切り上げ |
1回のデプロイ(CodePipelineは実行全体の6分32秒、GitHub Actionsはジョブの63秒。課金はジョブ単位のため)で課金される分数は、CodePipelineが13分(アクション実行)と10分(CodeBuild)、GitHub Actionsが2分でした。30秒で終わるアクションも1分と数えられるので、アクション数が多いCDK Pipelinesは切り上げの影響を受けやすい構成です。
| 1回あたり(無料枠なし) | |
|---|---|
| CodePipeline + CodeBuild | $0.076(13 × $0.002 + 10 × $0.005) |
| GitHub Actions | $0.012(2 × $0.006) |
| 月のデプロイ回数 | CodePipeline + CodeBuild(無料枠あり) | 同(無料枠なし) | GitHub Actions(無料枠あり) | 同(無料枠なし) |
|---|---|---|---|---|
| 30回 | $1.58 | $2.28 | $0(60分) | $0.36 |
| 100回 | $6.90 | $7.60 | $0(200分) | $1.20 |
無料枠は、CodePipelineとCodeBuildが各月100分、GitHub Actions(個人Free)が月2,000分で、「無料枠あり」はほかに何も使っていない場合の数字です。今回の検証用アカウントは共有なので、実際は「無料枠なし」の列に近くなります。
今回の計測では、1回あたりの料金はCodePipeline側がGitHub Actionsの約6倍でした(CodeBuildの順番待ちがなかった回で計算すると約4.6倍)。差の大部分は、CDK Pipelinesが毎回通るSynth・SelfMutate・Assetsの分数と、アクションごとの切り上げです。ただし絶対額は小さく、月100回デプロイしても、CodePipeline側で月$7前後です。料金だけでは選ぶ理由になりにくく、デプロイ回数が非常に多い場合に効いてくる軸だと思います。
まとめ
比べた結果を、「どういう場合にどちらを選ぶか」の表にします。今回の条件(個人Free・非公開、S3の最小構成、CDK Pipelines)での整理です。
| こういう場合 | 選びやすい方 | 理由(この記事の実測) |
|---|---|---|
| 短い時間で何度もデプロイを試したい | GitHub Actions | 1分07秒 対 6分32秒 |
| 料金を抑えたい、または無料枠に収めたい | GitHub Actions | 1回 $0.012 対 $0.076。ただし絶対額は小さい |
| セットアップをコマンドだけで済ませたい | GitHub Actions | CLIだけで完結する。CodePipelineは接続の承認でブラウザ操作が要る |
| デプロイの前に人が止めて判断したい | CodePipeline | 手動承認が標準で使える。GitHubでデプロイ前の承認(Environmentsの必須レビュアー)を非公開リポジトリで使うには、GitHub Enterpriseが必要 |
| 失敗の原因を整理された形で見たい | CodePipeline | 最初に失敗したリソースと理由が概要タブに出る |
| 承認を入れずに、pushで自動デプロイしたい | どちらでもできる。速さ・料金・セットアップはGitHub Actions | 使い方は同じになり、違いは速さ(1分07秒 対 6分32秒)・料金($0.012 対 $0.076)・セットアップ(CLIだけで完結)になる |
実際に使ってみた感想も書いておきます。たまにCodePipelineを使うときは承認を入れずに使っていて、pushしてしばらく待てば自動で反映されるのは楽だなと感じていますが、反映に時間がかかる点はやはり気になってしまいます。
一方、GitHub Actionsは、マージを条件にして自動で動かしたり、手動で1ボタンでデプロイできるようにしたり、inputs でコメントの入力欄を用意したりと、カスタマイズ性があって個人的に好きです。