経緯
Docker上でNext.jsの開発環境を構築した際にホットリロードが全くきかず、格闘したので記事にしておきます。なんとか解決はできました。
環境
Dockerfileは以下のとおりです。
FROM node:20-bullseye-slim AS builder
WORKDIR /app
# Install deps (including dev) and build
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-bullseye-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080
ENV HOSTNAME="0.0.0.0"
# Copy public assets
COPY --from=builder /app/public ./public
# Copy built Next.js standalone output
# This includes node_modules and the server
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 8080
CMD ["node", "server.js"]
解決策
上記の記事を参考にnext.config.tsを以下のようにしました。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
/* config options here */
webpack: (config, { dev }) => {
if (dev) {
config.watchOptions = {
poll: 1000, // 1秒ごとに差分チェック
aggregateTimeout: 200, // 変更連打時に200ms待ってまとめて再ビルド
};
}
return config;
},
};
export default nextConfig;
package.jsonを以下のように編集しました。
"scripts": {
"dev": "WATCHPACK_POLLING=true next dev --webpack",
...
解説
next.config.tsにはポーリングを確実に行うようにプログラムを追加しています。これにより、ファイルシステムのイベント通知に頼らず、1秒ごとに定期的にファイルの変更をチェック(ポーリング)するようになります。
Docker 環境では、ホスト OS とコンテナ間でファイル変更通知が正しく伝播しない場合があり、その結果ホットリロードが動作しないことがあります。
ポーリング方式を有効にすることで、この問題を回避できます。
package.jsonでWATCHPACK_POLLING=trueを設定すると、通知を待つのではなく、「変更はないですか?」と一定間隔でファイルをパトロール(ポーリング)するモードに切り替わります。 これにより通知が届かない環境でも、多少CPUを使いますが確実に変更を検知できるようになります。また--webpackを指定することで、Turbopackではなく、より安定したWebpackを使用してサーバーを動かしています。
感想
ありふれた問題だと思って舐めてかかったら痛い目にあいました(TT)、記事があるにはあるのですが、全然うまくいきませんでした。備忘録として残しておこうと思います。