TL;DR
- Turborepo の Remote Cache を S3 + GitHub Actions で自前構築
- Vercel の有料プラン不要で、コストとセキュリティを改善
- キャッシュヒット時に CI ビルド時間を最大 90% 削減(5分 → 30秒)
- OIDC 認証で安全に AWS リソースにアクセス
- GitHub Actions Cache との違い: ブランチ制限なし、タスクハッシュベースの自動キャッシュ無効化
- Docker ビルドとの併用で、複数キャッシュ戦略を最大活用
- 主要ツール:
trappar/turborepo-remote-cache-gh-action+ S3
はじめに
前回の記事では、CDK から GitHub Actions に移行して Docker ビルドを並列化し、CI/CD 時間を約 80 分から 20 分に短縮した方法を紹介しました。その記事の最後で触れた「Turborepo の Remote Cache」について、今回は詳しく解説します。
モノレポ構成のプロジェクトでは、複数のパッケージやアプリケーションを管理するため、ビルドやテストに時間がかかりがちです。Turborepo は、タスクの依存関係を理解し、変更のないパッケージのビルドをスキップすることで、ビルド時間を大幅に短縮してくれます。
しかし、Local Cache だけでは CI 環境でその恩恵を受けることができません。CI の各ジョブは毎回クリーンな状態から始まるため、過去のビルド結果を再利用できないからです。
この問題を解決するのが Remote Cache です。ビルド結果をリモートストレージに保存し、CI の各ジョブ間で共有することで、キャッシュヒット時にビルドをスキップできます。
この記事では、Vercel の有料 Remote Cache サービスを使わずに、S3 と GitHub Actions で自前の Remote Cache を構築した方法を紹介します。
Turborepo Remote Cache とは
Local Cache の限界
Turborepo は、タスクの実行結果をキャッシュすることで、同じ入力に対する再実行をスキップします。デフォルトでは、このキャッシュは .turbo ディレクトリにローカル保存されます。
# 初回実行: キャッシュミス
$ turbo run build
...
Tasks: 5 successful, 5 total
Cached: 0 cached, 5 total
Time: 2m 30s
# 2回目実行: キャッシュヒット(変更がない場合)
$ turbo run build
...
Tasks: 5 successful, 5 total
Cached: 5 cached, 5 total
Time: 0.5s # 劇的に高速化しかし、CI 環境では毎回新しいランナーが割り当てられるため、.turbo ディレクトリは空の状態から始まります。つまり、ローカル開発では享受できるキャッシュの恩恵が、CI では得られないという課題があります。
Remote Cache で解決
Remote Cache は、ビルド結果をリモートストレージ(S3、Azure Blob、Google Cloud Storage など)に保存し、異なるマシン間で共有する仕組みです。

これにより:
- CI Job A でビルドした結果が S3 に保存される
- CI Job B が同じコードをビルドする際、S3 からキャッシュを取得してスキップ
- CI Job C も同様にキャッシュを活用
GitHub Actions Cache との比較
GitHub Actions には標準で actions/cache によるキャッシュ機能が提供されています。では、なぜ Turborepo Remote Cache を別途導入するのでしょうか?
| 観点 | GitHub Actions Cache | Turborepo Remote Cache |
|---|---|---|
| 容量制限 | リポジトリあたり 10GB(デフォルト) | ストレージ依存(S3 なら実質無制限) |
| 保持期間 | 7 日間アクセスがないと自動削除 | 自分で設定可能(ライフサイクルルール) |
| キャッシュキー | 手動でキーを設計(ファイルハッシュ等) | タスクの inputs から自動計算 |
| ブランチ制限 | 現在のブランチ、デフォルトブランチ、PR のベースブランチのみ | ブランチ制限なし |
| キャッシュ無効化 | キーの変更で手動管理 | inputs の変更で自動無効化 |
| 用途 | 汎用的(node_modules、ビルドツール等) | Turborepo タスク専用 |
GitHub Actions Cache の特徴
GitHub 公式ドキュメントによると、GitHub Actions Cache には以下の制約があります:
- 容量: リポジトリあたり 10GB(2025年11月のアップデートで、Pro/Team/Enterprise プランでは従量課金で 10GB 超も可能に)
- 保持期間: 7 日間アクセスがないキャッシュは自動削除
- ブランチスコープ: キャッシュは作成されたブランチにスコープされ、他のブランチからは制限付きでのみアクセス可能
- キー長制限: キャッシュキーは最大 512 文字
参考: GitHub Actions cache size can now exceed 10 GB per repository
Turborepo Remote Cache の特徴
Turborepo 公式ドキュメントによると、Turborepo Remote Cache は以下の特徴があります:
- タスクハッシュベース:
turbo.jsonで定義したinputsからハッシュを自動計算し、キャッシュキーを生成 - ブランチ制限なし: どのブランチからでも同じハッシュならキャッシュを共有
- 自動無効化:
inputsに含まれるファイルが変更されると自動的にキャッシュが無効化 - ストレージ選択: S3、Azure Blob、GCS など、任意のストレージを使用可能
使い分けの指針
GitHub Actions Cache が適しているケース:
node_modulesのキャッシュ(package-lock.jsonをキーに使用)- ビルドツールのキャッシュ(Gradle、Maven など)
- 単一パッケージのプロジェクト
Turborepo Remote Cache が適しているケース:
- モノレポ構成でパッケージ間の依存関係がある
- ビルド成果物を異なるブランチ間で共有したい
- キャッシュの自動無効化を Turborepo に任せたい
今回のケースでは、両方を併用しています:
node_modules→ GitHub Actions Cache- Turborepo タスクの成果物 → Turborepo Remote Cache (S3)
なぜ自前の S3 なのか
Turborepo は公式に Vercel Remote Cache を提供しています。しかし、今回のケースでは自前の S3 を選択しました。その理由は以下の通りです:
| 観点 | Vercel Remote Cache | 自前 S3 |
|---|---|---|
| コスト | 有料プラン必要(Pro/Enterprise) | S3 の従量課金のみ |
| データの場所 | Vercel のインフラ | 自社の AWS アカウント |
| カスタマイズ | 制限あり | 自由にカスタマイズ可能 |
| セキュリティ | Vercel に依存 | 自社ポリシーで管理 |
特に、機密性の高いコードを外部サービスに送信したくない場合や、既存の AWS インフラを活用したい場合は、自前構築が適しています。
アーキテクチャ概要
自前 Remote Cache の全体構成は以下の通りです:

使用する主なツール:
- trappar/turborepo-remote-cache-gh-action: GitHub Actions 内で Remote Cache サーバーを起動
- ducktors/turborepo-remote-cache: 上記アクションが内部で使用する Remote Cache サーバー実装
- AWS S3: キャッシュの永続化ストレージ
- AWS IAM (OIDC): GitHub Actions からの安全な認証
実装手順
1. AWS リソースの準備
まず、キャッシュを保存する S3 バケットと、GitHub Actions からアクセスするための IAM ロールを作成します。
S3 バケットの作成
# S3 バケットを作成(バケット名は一意にする必要があります)
aws s3 mb s3://your-turborepo-cache-bucket --region ap-northeast-1
# ライフサイクルルールを設定(古いキャッシュを自動削除)
aws s3api put-bucket-lifecycle-configuration \
--bucket your-turborepo-cache-bucket \
--lifecycle-configuration '{
"Rules": [
{
"ID": "DeleteOldCache",
"Status": "Enabled",
"Filter": {},
"Expiration": {
"Days": 30
}
}
]
}'IAM ロールの作成(OIDC 認証)
GitHub Actions の OIDC プロバイダーを使用することで、長期的なアクセスキーを保存せずに、安全に AWS リソースにアクセスできます。
まず、AWS アカウントに GitHub の OIDC プロバイダーを登録します(未登録の場合):
# OIDC プロバイダーの作成
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1次に、IAM ロールを作成します:
// trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:*"
}
}
}
]
}// s3-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::your-turborepo-cache-bucket/*",
"arn:aws:s3:::your-turborepo-cache-bucket"
]
}
]
}# IAM ロールの作成
aws iam create-role \
--role-name turborepo-remote-cache-role \
--assume-role-policy-document file://trust-policy.json
# S3 アクセスポリシーをアタッチ
aws iam put-role-policy \
--role-name turborepo-remote-cache-role \
--policy-name S3CacheAccess \
--policy-document file://s3-policy.json2. カスタム GitHub Action の作成
Remote Cache サーバーの起動を再利用可能なアクションとしてカプセル化します。
# .github/actions/run-remote-cache-server/action.yml
name: 'Run Remote Cache Server'
description: 'Run server for remote cache'
inputs:
remote_cache_s3_bucket_name:
description: 'S3 bucket name for remote cache'
required: true
remote_cache_role_arn:
description: 'IAM role ARN for remote cache'
required: true
remote_cache_port:
description: 'Port for remote cache server'
required: false
default: '34444'
remote_cache_host:
description: 'Host for remote cache server'
required: false
default: 'http://127.0.0.1'
runs:
using: 'composite'
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.remote_cache_role_arn }}
aws-region: ap-northeast-1
- name: Start Turbo Cache Server
uses: trappar/turborepo-remote-cache-gh-action@main
with:
storage-provider: s3
storage-path: ${{ inputs.remote_cache_s3_bucket_name }}
port: ${{ inputs.remote_cache_port }}
host: ${{ inputs.remote_cache_host }}このアクションは以下のことを行います:
- AWS 認証の設定: OIDC を使用して一時的な認証情報を取得
- Remote Cache サーバーの起動:
trappar/turborepo-remote-cache-gh-actionが内部で ducktors/turborepo-remote-cache サーバーを起動 - 環境変数の自動設定:
TURBO_API、TURBO_TOKEN、TURBO_TEAMが自動的に設定される
3. CI ワークフローへの統合
作成したカスタムアクションを CI ワークフローで使用します。
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC トークンの取得に必要
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
# Remote Cache サーバーを起動
- name: Run remote cache server
uses: ./.github/actions/run-remote-cache-server
with:
remote_cache_s3_bucket_name: ${{ secrets.REMOTE_CACHE_S3_BUCKET_NAME }}
remote_cache_role_arn: ${{ secrets.REMOTE_CACHE_ROLE_ARN }}
# Turborepo でビルド(キャッシュが自動的に使用される)
- name: Build
run: npm run build
- name: Test
run: npm run test
- name: Lint
run: npm run lintGitHub Secrets に以下を設定します:
REMOTE_CACHE_S3_BUCKET_NAME: 作成した S3 バケット名REMOTE_CACHE_ROLE_ARN: 作成した IAM ロールの ARN
4. Turborepo 設定
turbo.json でキャッシュ対象を適切に設定します。
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**"
],
"inputs": [
"src/**",
"package.json",
"tsconfig.json"
]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"inputs": [
"src/**",
"__tests__/**",
"vitest.config.*"
]
},
"lint": {
"outputs": [],
"inputs": [
"src/**",
"eslint.config.*"
]
},
"dev": {
"cache": false,
"persistent": true
}
},
"globalDependencies": [
".npmrc",
"package-lock.json",
"turbo.json"
]
}ポイント:
outputs: キャッシュに保存するファイル/ディレクトリinputs: これらのファイルが変更されるとキャッシュが無効化されるglobalDependencies: 全タスクに影響する依存ファイルcache: false:devタスクはキャッシュしない(常に実行)
並列実行時の工夫
動的ポート割り当て
並列実行される複数のジョブが同じランナー上で動作する場合、同じポートを使用すると競合が発生します。これを避けるため、ランダムなポート番号を動的に割り当てます。
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ランダムなポート番号を生成(40000-50000 の範囲)
- name: Set random port
id: set-port
run: echo "port=$((40000 + RANDOM % 10000))" >> $GITHUB_OUTPUT
- name: Run remote cache server
uses: ./.github/actions/run-remote-cache-server
with:
remote_cache_s3_bucket_name: ${{ secrets.REMOTE_CACHE_S3_BUCKET_NAME }}
remote_cache_role_arn: ${{ secrets.REMOTE_CACHE_ROLE_ARN }}
remote_cache_port: ${{ steps.set-port.outputs.port }}
- name: Lint
run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 別のジョブでも同様にランダムポートを使用
- name: Set random port
id: set-port
run: echo "port=$((40000 + RANDOM % 10000))" >> $GITHUB_OUTPUT
# ... 以下同様マトリクスビルドでの固定ポート戦略
前回の記事で紹介した Matrix Strategy と組み合わせる場合、各マトリクスジョブに固定の一意なポート番号を割り当てます。
jobs:
build-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
image:
- name: app
remote-cache-port: 32001
- name: api
remote-cache-port: 32002
- name: worker
remote-cache-port: 32003
max-parallel: 5
fail-fast: false
steps:
- uses: actions/checkout@v4
# マトリクスで定義した固定ポートを使用
- name: Run remote cache server
uses: ./.github/actions/run-remote-cache-server
with:
remote_cache_s3_bucket_name: ${{ secrets.REMOTE_CACHE_S3_BUCKET_NAME }}
remote_cache_role_arn: ${{ secrets.REMOTE_CACHE_ROLE_ARN }}
remote_cache_port: ${{ matrix.image.remote-cache-port }}
- name: Build
run: npm run build:${{ matrix.image.name }}Docker ビルドでの活用
Docker イメージのビルド時に Turborepo の Remote Cache を活用する場合、いくつかの追加設定が必要です。
Docker Buildx の network=host 設定
Docker コンテナ内から localhost で動作している Remote Cache サーバーにアクセスするには、network=host オプションが必要です。
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host # localhost へのアクセスを許可環境変数の渡し方
Dockerfile 内で Turborepo を実行する場合、環境変数を build-args として渡します。
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
build-args: |
TURBO_API=${{ env.TURBO_API }}
TURBO_TOKEN=${{ env.TURBO_TOKEN }}
TURBO_TEAM=${{ env.TURBO_TEAM }}# Dockerfile
FROM node:22-alpine AS builder
ARG TURBO_API
ARG TURBO_TOKEN
ARG TURBO_TEAM
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build # turbo run build が実行され、Remote Cache が使用される複数キャッシュ戦略の併用
最大限の効果を得るには、複数のキャッシュ戦略を組み合わせます:
- GitHub Actions Cache:
node_modulesのキャッシュ - Turborepo Remote Cache (S3): ビルド成果物のキャッシュ
- Docker Layer Cache (GitHub Actions): Docker イメージレイヤーのキャッシュ
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
# Docker レイヤーキャッシュ(GitHub Actions Cache を使用)
cache-from: type=gha,scope=${{ matrix.image.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.image.name }}
build-args: |
# Turborepo Remote Cache
TURBO_API=${{ env.TURBO_API }}
TURBO_TOKEN=${{ env.TURBO_TOKEN }}
TURBO_TEAM=${{ env.TURBO_TEAM }}効果測定
キャッシュヒット率の確認
Turborepo のログからキャッシュヒット率を確認できます:
$ turbo run build
Tasks: 10 successful, 10 total
Cached: 8 cached, 10 total # 10 タスク中 8 タスクがキャッシュヒット
Time: 15.234s導入前後の比較
今回のケースでは、Remote Cache 導入により以下の効果が得られました(※環境により異なります):
| シナリオ | 導入前 | 導入後 | 効果 |
|---|---|---|---|
| フルビルド(初回) | 約 5 分 | 約 5 分 | 変化なし |
| 変更なし(キャッシュヒット) | 約 5 分 | 約 30 秒 | 90% 削減 |
| 一部変更(部分キャッシュヒット) | 約 5 分 | 約 1-2 分 | 60-80% 削減 |
特に、同じコードベースに対する複数の PR や、main ブランチへのマージ後のデプロイなど、キャッシュが有効なシナリオで大きな効果を発揮します。
トラブルシューティング
キャッシュがヒットしない場合
- 環境変数の確認:
TURBO_API、TURBO_TOKEN、TURBO_TEAMが正しく設定されているか確認 - turbo.json の inputs 確認: 意図しないファイルが
inputsに含まれていないか確認 - globalDependencies の確認: 頻繁に変更されるファイルが含まれていないか確認
# ドライランでハッシュを確認
turbo run build --dry=json
# 出力される JSON で taskHash を確認
# 同じ入力であれば同じハッシュになるはずS3 への接続エラー
- IAM ロールの権限確認: S3 への適切なアクセス権限があるか確認
- OIDC 設定の確認: trust-policy の
sub条件がリポジトリと一致しているか確認 - リージョンの確認: S3 バケットと IAM ロールのリージョンが一致しているか確認
まとめ
S3 と GitHub Actions を使用して Turborepo Remote Cache を自前構築することで、以下のメリットを得ることができました:
- コスト削減: Vercel の有料プランを使わずに Remote Cache を実現
- セキュリティ: 機密性の高いコードを外部サービスに送信しない
- 柔軟性: 自社の AWS インフラと統合し、カスタマイズ可能
- CI 高速化: キャッシュヒット時に最大 90% のビルド時間削減
参考文献
- Turborepo - Remote Caching - Turborepo 公式ドキュメント
- trappar/turborepo-remote-cache-gh-action - GitHub Actions 用の Remote Cache アクション
- ducktors/turborepo-remote-cache - オープンソースの Remote Cache サーバー実装
- Configuring OpenID Connect in Amazon Web Services - GitHub Actions の OIDC 認証設定
- Caching dependencies to speed up workflows - GitHub Actions Cache 公式ドキュメント
- GitHub Actions cache size can now exceed 10 GB per repository - GitHub Changelog(2025年11月)
- CDKからGitHub Actionsに移行してDockerビルドを並列化、CI/CD時間を80分から20分に短縮した話 - 前回の記事
