0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloud Run に Next.js をソースデプロイしたら 503 — 原因は next.config.ts と devDependencies だった

0
Posted at

TL;DR

  • Cloud Run のソースデプロイ(Buildpacks)は、ビルド後の実行イメージから devDependencies を削除する
  • 一方で next start は、起動時に next.config.ts をトランスパイルするために typescript を必要とする
  • 結果、ビルドは成功するのに起動だけが失敗して 503 になる。ローカルでは絶対に再現しない
  • 対処は next.config.tsnext.config.mjs(素のJS)に変換するのが最短・副作用なし

こんな条件がそろうと発生します

以下にすべて当てはまる場合、この記事のケースの可能性が高いです。

  • Next.js 15 系(14でも同様)を App Router で使っている
  • 設定ファイルが next.config.ts(TypeScript形式)
  • typescriptdevDependencies に入れている(通常はそう)
  • Cloud Run に gcloud run deploy --source .(Dockerfileなしのソースデプロイ)している
  • デプロイは成功と表示されるのに、アクセスすると 503

症状

デプロイコマンドは正常終了し、リビジョンも「serving 100 percent of traffic」になる。なのに:

$ curl -s -o /dev/null -w "%{http_code}" https://my-app-xxxx.asia-northeast1.run.app/
503

ログを確認すると、正体が見えます。

$ gcloud run services logs read my-app --region asia-northeast1 --limit 30
⨯ Failed to load next.config.ts, see more info here https://nextjs.org/docs/messages/next-config-error
[Error: Cannot find module 'typescript'
Require stack:
- /workspace/node_modules/next/dist/build/next-config-ts/transpile-config.js
...
⚠ Installing TypeScript as it was not found while loading "next.config.ts".

Next.js が健気にも実行時に TypeScript をインストールしようとしていますが、間に合わず、リクエストは 503 のままでした。

原因: 2つの仕様の合わせ技

1. Buildpacks は実行イメージから devDependencies を削除する

ソースデプロイで使われる Google Cloud Buildpacks は、ビルド段階では devDependencies 込みの全部入りをインストールしますが、実行イメージの作成時に本番用へプルーニングします。typescript は通常 devDependencies に置くので、実行イメージには存在しません。

これ自体は「実行イメージを軽くする」ための正しい挙動です。

2. next start は起動時に next.config.ts を読む

next.config.tsビルド時だけでなく、next start の起動時にも読み込まれ、そのたびにトランスパイルされます。このトランスパイルに typescript パッケージが必要です。

つまり「ビルド時には typescript がある → ビルド成功」「起動時には typescript がない → 起動失敗」という、ビルド環境と実行環境の差がそのまま事故になります。ローカル開発では devDependencies が常に入っているため、この問題には気づけません。本番で初めて踏むタイプの罠です。

対処法

おすすめ: next.config.mjs に変換する

設定がシンプルなら、素のJavaScriptに変換するのが一番きれいです。

変換前(next.config.ts):

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  eslint: { ignoreDuringBuilds: true },
};

export default nextConfig;

変換後(next.config.mjs):

/** @type {import('next').NextConfig} */
const nextConfig = {
  eslint: { ignoreDuringBuilds: true },
};

export default nextConfig;

ポイント:

  • @type の JSDoc コメントを付ければ、エディタの補完・型チェックは TS 時代とほぼ同等に効きます
  • next.config.ts は削除してください。両方あると .ts が優先されて意味がなくなります

別解との比較

方法 メリット デメリット
next.config.mjs に変換 副作用なし・最短 設定ファイル内でTSの型機能をフルに使いたい場合は不向き
typescript を dependencies に移す コード変更なし 実行イメージが数十MB太る。「なぜ本番依存にTSが?」という将来の混乱のもと
Dockerfile + standalone 出力 根本解決・イメージ最小化 ソースデプロイの手軽さを捨てることになる

個人開発や小規模プロジェクトなら next.config.mjs 一択、イメージサイズやビルドを厳密に管理したいチームなら standalone 化も検討、という使い分けです。

直ったことの確認

$ gcloud run deploy my-app --source . --region asia-northeast1 ...
$ curl -s -o /dev/null -w "%{http_code}" https://my-app-xxxx.asia-northeast1.run.app/
200

補足

  • Vercel では起きません。 Vercel はビルドと実行の環境をプラットフォーム側で整合させています。この問題は「Buildpacks系のソースデプロイ × next.config.ts」の組み合わせ特有です
  • 同じ理屈で、起動時に読み込まれるコードが devDependencies に依存していないかは、ソースデプロイ全般で意識しておくと事故が減ります
  • Cloud Run のトラブルシュートでは、まず gcloud run services logs read を叩く癖をつけると「デプロイ成功なのに動かない」系の原因が一瞬で見えます

まとめ

  • Cloud Run ソースデプロイ × Next.js で「デプロイ成功なのに503」が出たら、まずログで Cannot find module 'typescript' を疑う
  • 原因は「Buildpacksが実行イメージからdevDependenciesを削る」×「next startが起動時にnext.config.tsを読む」の合わせ技
  • 設定がシンプルなら next.config.mjs への変換が最短の解決策

この構成(Next.js + Supabase + Vertex AI Gemini + Cloud Run)で、家庭菜園のAI相談アプリ「なんとかなる菜園」を作りました。アーキテクチャ全体の話はZennに書いています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?