🔄

DockerでNext.jsの開発環境を作成した際、Fast Refreshが効かない問題を解決

2024/08/12に公開

DockerでNext.jsの開発環境を作成した際、Fast Refreshが効かない事象に遭遇しました。

Fast Refreshとはいわゆるホットリロードのことで、
ページの中身を書き換えたときにいちいちリロードしなくても
ファイルの変更をブラウザの表示に反映してくれる機能のことです。

Docker上でFast Refreshを動作させる方法はいくつかありますが、
今回は、私が調べて見つけた方法の中で、
上手くいった方法、逆に上手くいかなかった方法、
そして、上手くいった方法の中でメリットデメリットを簡単に比較検討したうえでどれを選べばいいのか私の意見を述べたいと思います。

開発環境

Next.js: 14.2.5
React: 18.3.1
node.js: 20.9.0
npm: 9.2.0
Windows: 11

原因

おそらく、「WSL2はWindowsのファイル変更イベントを検知できないから」です。
WSLというのは、Windowsの中でLinuxを動かす仕組みで、
Windowsの中にLinuxをおいてその中でDockerが動いています。

Fast Refreshが効かないのは、
Fast Refreshはソースの変更ではなくファイルの変更イベントをトリガーに発火するものであり、
例えボリュームマウントを行ってホストとコンテナのファイルの同期が出来ていても、
コンテナ内部ではホストマシンでのファイルの変更イベントを検知できないため、
Fast Refreshが走らないのではないかと思います。

どの方法を採用するのが良いか

先に結論を述べると、compose.ymlでWATCHPACK_POLLINGをtrueに設定する
もしくは、package.jsonのscriptsでnext devにWATCHPACK_POLLING=trueを渡してあげるのが良いのではないかと思いました。

next.config.mjsの設定でFast Refreshを機能させる方法もありますが、
pollなどは短くすればするだけホットリロードが早くなるわけでもありませんし、
compose.ymlやpackage.jsonに環境変数を設定する方が簡単なので良いのではないかと思います。

ちなみにnode_modulesなどのディレクトリをwebpackの監視対象から外さないと、
たとえpollやaggregateTimeoutを短くしていたとしても、
場合によってはFast Refreshが反映されるのにかかる時間が非常に長くなる可能性もあります。
その場合、next.config.mjsのwebpackのwatchOptionsのignoredにnode_modulesを設定して管理対象から外さなくてはなりません。

上手くいった方法

next.config.mjsでのpoll, aggregateTimeoutの設定

next.config.mjsにwebpackのpoll, aggregateTimeoutの設定を追加することでDocker環境でホットリロードを機能させることが出来ます。

next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config) => {
    config.watchOptions = {
      poll: 300,
      aggregateTimeout: 300,
    };
    return config;
  },
};

export default nextConfig;

pollは、webpackが何秒おきにファイルの変更をチェックするかを設定する項目です。

aggregateTimeoutは、ファイルの変更があった後に再ビルドを走らせるまでの時間を設定できる項目です。

package.jsonでのscriptsの設定

WATCHPACK_POLLINGとは、Webpackの内部で使用されるファイル監視ライブラリであるWatchpackのポーリング機能を有効にするための環境変数です。

これを、trueにしてnext devコマンドに渡し、
DockerfileのCMDかcompose.ymlのcommandでnpm run devしてあげることで
ホットリロードを機能させることが可能です。

package.json

{
  ~
  ~
  ~
  "scripts": {
    "dev": "WATCHPACK_POLLING=true next dev",
    ~
    ~
    ~
  }
}

compose.ymlのenvironmentにWATCHPACK_POLLING=trueを設定

さきほどのWATCHPACK_POLLINGはcompose.ymlのenvironment句で設定しても有効です。

compose.yml

services:
  nextjs:
    ~
    ~
    environment:
      - WATCHPACK_POLLING=true
    ~
    ~

ほかに試したが上手く動作しなかったもの

compose.ymlでのCHOKIDAR_USEPOLLING=trueの設定

CHOKIDAR_USEPOLLINGとは、
node環境で動作するファイル監視ライブラリであるchokidarが,
ファイルシステムの変更を検出するためにポーリングを使用するかどうかを決める環境変数です。

これを有効にすることでchokidarがポーリングを使用してくれるようです。

chokidarは、ファイルの変更を検知してそれをトリガーに処理を実行する際に用いられるようです。

コンテナ内部ではファイル変更のイベントを検知できないのでこの設定ではFast Refreshが動作しなかったのではないかと思います。

compose.yml

services:
  nextjs:
    build: .
    ports:
      - 3000:3000
    volumes:
      - .:/nextjs
    environment:
      - CHOKIDAR_USEPOLLING=true
    command: npm run dev

また、いろいろググったところ「Windowsを使用していてreact-scripts 5.xx以降を使用している場合CHOKIDAR_USEPOLLINGは機能しない」という記述も多くみられました。

react-scriptsはcreate-react-appで作成されたReactプロジェクトで使用されるパッケージのようです。

ですが、create-react-appは既に使用が非推奨になっていますし、
package.jsonなどにもreact-scriptsの記述はありませんでしたので、
react-scriptsまわりがDocker上でFast Refreshが動作しない原因とは考えにくいですね。

やはり、コンテナ内部とホストマシンでのファイルシステムの違いや
webpackの挙動が原因なんだと思います。

参考

https://stackoverflow.com/questions/71297042/react-hot-reload-doesnt-work-in-docker-container
https://webpack.js.org/configuration/watch/
https://zenn.dev/koyataniguchi/articles/windows-docker-hotreload

Discussion