開発完了
背景
2026年1月1日、GitHub ActionsでのデプロイがNode.js OOM(Out of Memory)エラーで連続失敗した。調査の結果、以下が判明:
- 3ヶ月分のコンテンツ(338ファイル)で約2GBのメモリを使用
- GitHub Actions無料枠のRAMは7GB
- SSGは全ページをメモリに読み込んで処理する
問題の本質
SSGはコンテンツ量に比例してビルド時メモリが増加する
単純計算での予測:
| 期間 | メモリ使用量 | GitHub Actions対応 |
|---|---|---|
| 3ヶ月 | 約2GB | OK |
| 1年 | 約8GB | 限界超過(7GB上限) |
| 3年 | 約24GB | 不可能 |
即時対応(実施済み)
- Node.jsヒープメモリを4GBに拡大
"generate": "NODE_OPTIONS='--max-old-space-size=4096' nuxt generate" - 巨大ファイルをプリレンダリングから除外
nitro: { prerender: { ignore: [/\?/, /\/prompts\//], } }
長期対策オプション
オプション1: ハイブリッドレンダリング(推奨)
最新コンテンツはSSG、古いコンテンツはISR(Incremental Static Regeneration)で処理。
// nuxt.config.ts
routeRules: {
// 最新1年分はSSG(事前生成、爆速)
'/2025-**': { prerender: true },
'/2026-**': { prerender: true },
// 古い記事はISR(初回アクセス時に生成してキャッシュ)
'/2024-**': { isr: 3600 }, // 1時間キャッシュ
'/2023-**': { isr: 86400 }, // 1日キャッシュ
}
メリット:
- ビルド時は最新1年分のみ生成 → メモリ問題解決
- 古い記事も初回アクセス後はキャッシュ → 2回目以降は爆速
- Cloudflare Workersのエッジキャッシュで高速配信
デメリット:
- 古い記事の初回アクセスは少し遅い(ただし、古いログを見る人は待てる)
cloudflare-pages-staticからcloudflare-pagesへの変更が必要
オプション2: ローカルビルド + デプロイ
ローカルマシン(16GB〜32GB RAM)でビルドし、結果をデプロイ。
方法A: distをGitHubにコミット
# ローカルでビルド
pnpm generate
# distをコミット
git add dist/
git commit -m "Build: update static files"
git push
# GitHub ActionsはdistをそのままCloudflareにデプロイ
方法B: ローカルから直接デプロイ(今回採用)
# ローカルでビルド&デプロイ
pnpm generate
wrangler pages deploy dist --project-name=mdx-playground
メリット:
- ローカルマシンのメモリを使用 → 制限なし
- 全コンテンツをSSGで事前生成可能
- 初期表示が常に爆速
デメリット:
- distディレクトリが大きい(数百MB〜数GB)
- 方法Aの場合、Gitリポジトリが肥大化
- 自動デプロイではなく手動操作が必要
オプション3: 完全SSRへの移行
全ページをサーバーサイドレンダリングに移行。
// nuxt.config.ts
nitro: {
preset: "cloudflare-pages", // staticを外す
}
メリット:
- ビルド時のメモリ問題が根本的に解消
- コンテンツ量に関係なくスケール可能
デメリット:
- 全ページで初期表示が遅くなる
- サーバーコスト増加の可能性
オプション4: ビルドマシン強化
GitHub Actions有料版やセルフホストランナーを使用。
- GitHub Actions有料版: 最大32GB RAMまで選択可能
- セルフホストランナー: 自前サーバーで無制限
メリット:
- 既存のSSG構成を変更不要
デメリット:
- コストがかかる
採用したアプローチ: ローカルビルド + 直接デプロイ
検討の結果、オプション2B(ローカルから直接デプロイ) を採用。
変更前の構成(GitHub Actions経由)
ローカル → GitHub push → GitHub Actions(ビルド)→ Cloudflare
問題点:
- GitHub Actions無料枠のRAM制限(7GB)
- 1年程度でメモリ上限に達する見込み
- ビルド待ち時間が発生
変更後の構成(ローカル直接デプロイ)
ローカル → GitHub push(ソースコードのバージョン管理のみ)
ローカル(ビルド)→ Cloudflare直接デプロイ
メリット:
- ローカルマシンのメモリを使用(16GB〜32GB)→ 何年分でもビルド可能
- GitHub Actionsの待ち時間なし
- デプロイが速い(数秒〜数十秒)
- Cloudflare Pages無料枠で無制限に運用可能
デメリット:
- 自動デプロイではなくなる(手動でコマンド実行)
デプロイ手順
# ビルド&デプロイ(1コマンド)
pnpm deploy:cloudflare
または個別に:
pnpm generate # ビルド
wrangler pages deploy dist --project-name=mdx-playground # デプロイ
運用ルール(再現性確保):
- デプロイ前に
git statusでクリーンな状態を確認 - 未コミットの変更がある状態でデプロイしない
- どのコミットからビルドしたかはGitのログで追跡可能
Cloudflare Pages無料枠の制限
| 項目 | 制限 |
|---|---|
| 静的ファイルホスティング | 無制限 |
| 帯域幅 | 無制限 |
| リクエスト数 | 無制限 | | サイトサイズ | 実質無制限 | | ビルド回数 | 500回/月(ローカルビルドなら無関係) |
結論: ローカルビルド + 直接デプロイなら、何年でも無料で運用可能(ただしCloudflareの料金体系変更の可能性はある)
GitHub Actionsの役割変更
- 変更前: ビルド&デプロイ
- 変更後: CI(テスト、lint等)のみ、またはワークフロー削除
今後の選択肢
ローカルビルドが遅くなりすぎた場合(10分以上など):
- ハイブリッドレンダリング(オプション1)への移行を検討
- 最新1年分のみSSG、古い記事はISR
ハイブリッド移行時の注意点:
routeRulesの年指定(/2025-**等)は毎年手動更新が必要- 更新忘れを防ぐには、現在年から動的に計算するコードに変更する
関連ドキュメント
- GitHub Actions デプロイ失敗調査 - 今回のOOMエラーの詳細調査
補足: なぜログデータはISRで問題ないか
このサイトのコンテンツは日付ベースのログ/メモ形式。
- 最新の記事: 頻繁にアクセスされる → SSGで爆速が重要
- 1年以上前の記事: たまにしかアクセスされない → 初回だけ少し遅くても許容範囲
- 古いログを見る人: 「待てる」ユーザー層
この特性を活かして、ハイブリッドレンダリングが最適解となる。