Zenn
🐱

Next.jsのスリムなコンテナイメージを作成するDockerfile

2025/03/23に公開
2

Next.jsをAWS ECSで運用する機会があり、その為のコンテナイメージを作成する為のDockerfileを整備したので、その備忘になります。

なるべくイメージサイズを小さくしようと整備した内容になります。

前提

  • Next.js v15系
  • Next.jsのビルドはスタンドアロンモードを利用する
  • Dockerのマルチステージビルドを利用する
  • とりあえずECSで動くように

Next.jsをスタンドアロンモードでビルドできるように設定

next.config.ts
const nextConfig: NextConfig = {
  output: 'standalone'
}

export default nextConfig

これだけです。

スタンドアロンモードでビルドを行うと、.next/standaloneディレクトリが作成され、実行に必要な最小限のファイルのみがそこにコピーされるようになります。
node_modulesもコピーされますが、中身はかなりスリムになっているはずです。

ちなみにスタンドアロンモードでビルドを行った場合の起動方法は、next startではなく、node server.jsのようになります。

マルチステージビルドのDockerfileを用意する

Dockerfile
FROM node:20.19.0-alpine AS base

# ---
# ビルド環境
# ---
FROM base AS builder
WORKDIR /app

# ソースをコピー
COPY . .

# ビルド
RUN yarn install && yarn cache clean
RUN yarn build

# ---
# 実行環境
# ---
FROM base AS runner
WORKDIR /app

# curlコマンドをインストール(ヘルスチェック用)
RUN apk add --no-cache curl

# 実行に必要なファイルをビルド環境からコピー
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
ENV PORT=3000

CMD ["node", "server.js"]

こんな感じです。

基本的にコメントで記載している通りですが、

  1. baseのイメージを定義。(FROMは環境用に変更してください
  2. ビルド用のステージ(builder)でビルド
  3. 実行用のステージ(runner)に実行に必要なファイルのみをbuilderからコピー
  4. alpineにはcurlコマンドが含まれていないので追加
  5. node server.jsで実行

こうすることで、実行用のコンテナイメージには実行に必要な最低限のファイルのみが含まれるようになり、イメージサイズを小さくすることができます。

自分の例だと、1.8GB → 250MB にできました。

curlコマンドをインストールしている理由

ECSのタスク定義でヘルスチェック用のパスを変更する場合、

CMD-SHELL,curl -f http://localhost/health || exit 1

のように設定するのですが、ここでcurlを利用する為です。
最初自分はなぜヘルスチェックが失敗するのか分からず詰まりました。(まさかcurlが使えないとは思わず。。。
wgetを利用するような記事もありましたが、自分はcurlぐらいは入れておこうかなと。

コンテナ実行時の環境変数にHOSTNAME=0.0.0.0を設定

コンテナを実行する環境変数にHOSTNAME=0.0.0.0を設定します。
ECSだとタスク定義の環境変数。

node server.jsで起動する際にはHOSTNAMEを参照してしまうようで、設定しないと下記のように起動してしまう為になります。

   ▲ Next.js 15.2.2
   - Local:        http://ip-xx-xx-xx-xx.ap-northxxx:80
   - Network:      http://ip-xx-xx-xx-xx.ap-northxxx:80

本来は下記のように起動して欲しい。

   ▲ Next.js 15.2.2
   - Local:        http://localhost:80
   - Network:      http://0.0.0.0:80
2

Discussion

ログインするとコメントできます