トラス構造が幾何学的に組まれた建築物のファサード
技術解説

Next.jsコンテナデプロイ先の選び方 standalone出力とVercel比較

目次を見る

Next.jsアプリを自前のインフラにデプロイしようとして、ビルドしたDockerイメージが1GBを超えて困った経験はないでしょうか。

原因の多くはnext.config.jsの設定不足です。デフォルトのビルドはnext startに必要なファイルだけでなく、node_modulesフォルダ全体をイメージに含めてしまいます。この記事では、コンテナ基盤でNext.jsを動かす開発者やSREに向けて、デプロイ方式の判断材料を整理します。PaaS(Platform as Service、インフラ管理を肩代わりするホスティング形態)を使うか、Kubernetes(コンテナの運用を自動化するオーケストレーションツール)ベースの基盤を自分で選ぶか、という判断軸も含めて確認していきます。

standalone出力はなぜ必要か

Next.jsにはoutput: "standalone"という設定があります。これをnext.config.jsに追加すると、ビルド成果物が変わります。

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone",
};
module.exports = nextConfig;

この設定を入れると、.next/standaloneという自己完結型のフォルダが生成されます。サーバー実行に必要なファイルと、最小限のserver.jsエントリーポイントだけが含まれ、プロジェクト全体やnode_modulesをコピーする必要がなくなります。

コンテナ環境では、この差がイメージサイズに直結します。マルチステージビルド(依存関係のインストールとビルドを別ステージに分け、最終イメージを軽量化する手法)と組み合わせれば、最終イメージから開発時依存や中間ファイルを排除できます。

判断軸1: ビルド時変数とランタイム変数の分離をどう扱うか

Next.jsにはNEXT_PUBLIC_というプレフィックス(接頭辞)付きの環境変数があります。これはクライアント側のJavaScriptバンドルに埋め込まれる変数です。

重要なのは、この変数がビルド時にしか反映されないという点です。デプロイ後にダッシュボードで値を変更しても、すでにビルド済みのバンドルには反映されません。

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build

このようにDockerのARGとしてビルド引数で渡す必要があります。一方、NEXT_PUBLIC_が付かない変数(APIキーやDB接続文字列など)は、コンテナ起動時に注入するランタイム変数として扱えます。

この仕組みを理解せずにCI/CDパイプラインを組むと、「環境変数を変更したのに本番に反映されない」という事象にハマりがちです。デプロイ先を選ぶ際は、ビルド時変数とランタイム変数を明確に分けて設定できるかどうかを確認しておくと安心です。

判断軸2: マネージドDBの要否とORM利用状況

Prisma(Node.js向けのORM)やDrizzleなどを使ってデータベースにアクセスするAPI Routeがあるなら、Postgresなどの実データベースが必要です。

自前でKubernetes上にPostgresを構築・運用するコストと、マネージドサービスを使うコストを比較する必要があります。例えばOwnkube(コンテナアプリのデプロイとマネージドDBを提供するプラットフォーム)では、Postgresインスタンスが月額4ドルからストレージ0.25ドル/GBという料金体系で提供されており、バックアップやポイントインタイムリカバリも選択できます。

こうした料金感は、Vercelのマネージドデータベース連携やSupabaseのような専業サービスと比較する際の目安になります。自社でDB運用チームを持たない場合、マネージド型を選んだ方がSREの運用負荷を抑えられます。

判断軸3: TLS・ホスティングの自動化レベル

デプロイ先によって、TLS証明書の発行やドメイン割り当てが自動化されているかどうかが異なります。

Ownkubeの場合、Gitリポジトリをダッシュボードに接続するだけで、プッシュのたびにビルドが走り、*.ownkube.appのホスト名が割り当てられ、TLSも自動発行されます。リバースプロキシや証明書管理を手動で組む必要がありません。

okctl login
okctl up --name nextjs-app --port 3000

CLI(コマンドラインインターフェース)からのデプロイも可能で、okctlコマンド一発でイメージのビルドからホスティングまで完結します。自前のKubernetesクラスタでこれを再現する場合、Ingress Controller(外部からのアクセスをクラスタ内に振り分ける仕組み)とcert-manager(TLS証明書を自動更新するツール)の設定が別途必要になります。

判断軸4: トラフィックの偏りとオートスケーリング要件

Next.jsのSSR(Server-Side Rendering、サーバー側でHTMLを生成する方式)は、静的ページと違ってリクエストごとにCPUを消費します。

アクセスが均等でないアプリでは、水平オートスケーリング(負荷に応じてコンテナの台数を自動的に増減させる仕組み)を有効にする判断が必要になります。静的サイト中心のアプリなら、この要件は優先度が下がります。

選択肢の比較

観点PaaS型(Ownkube等)自前Kubernetes
TLS・ドメイン自動発行・自動割り当てcert-manager等を自前構築
DB運用マネージドPostgresを数クリックで追加StatefulSet等で自前運用
初期構築の手間Dockerfile+git pushで完結Ingress・CI/CD・監視を個別設計
既存クラスタとの統合限定的社内基盤に合わせて柔軟に統合可能

ケース別の推奨

小規模チームで、インフラ専任の担当者がいないなら、PaaS型を選ぶのが無難です。Dockerfileとstandalone出力さえ押さえれば、残りはgit push相当の運用で済みます。

社内にすでにKubernetesクラスタがあり、他のマイクロサービスと同じ監視基盤・ログ基盤に乗せたいなら、自前Kubernetesを選ぶ理由があります。この場合もstandalone出力は必須で、イメージサイズの削減効果は変わりません。

PrismaなどのORMを使うアプリで、DB運用チームを持たないなら、マネージドPostgresを提供するプラットフォームを優先的に検討してよいでしょう。

あえて見送るべき条件

特定リージョンへのデータ配置規制が厳しい場合や、既存のVPC(仮想プライベートネットワーク)内に閉じた通信経路が必須の場合は、小規模PaaSの導入を一旦見送るべきです。

また、すでに自社でTerraformなどのIaC(Infrastructure as Code、インフラ構成をコードで管理する手法)による標準化が進んでいる組織では、別系統のデプロイ基盤を追加すると管理対象が分散します。この場合は既存の仕組みへの統合を優先した方が、長期的な運用コストは下がります。

まとめ

Next.jsをコンテナでデプロイする際、output: "standalone"の有無でイメージサイズが大きく変わります。まずこの1行をnext.config.jsに追加できているか確認してください。

次に、NEXT_PUBLIC_変数をビルド引数として渡しているか、CI/CD設定を見直してみましょう。ここを誤ると、環境変数変更が反映されないトラブルにつながります。

デプロイ先の選定では、TLS自動化・マネージドDBの有無・オートスケーリング対応の3点を軸に、PaaS型と自前Kubernetesのどちらが運用負荷に見合うかを比較することをおすすめします。

参考

How to deploy a Next.js app on Ownkube

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。