pnpm + Next.js standalone + Docker で5回ハマった話 [第9回]
この記事で分かること pnpmのsymlink構造がNext.js standaloneでなぜ壊れるのか cp -rLで解決できない場合の対処法 Docker multi-stage buildでのsymlink解決パターン 5回のfix PRから得た教訓 背景 Saruは5つのNext.jsフロントエンド(Landing / System / Provider / Reseller / Consumer)をpnpmモノレポで管理している。開発環境ではvolume mountで動かすのでDockerfileは不要だが、本番・デモ環境へのデプロイにはDockerイメージが必要になる。 Next.jsの output: 'standalone' を使えば、必要なファイルだけを .next/standalone/ にトレースしてくれる。これをAlpineベースのrunnerステージにコピーすれば軽量なイメージができる——はずだった。 ハマり1: MODULE_NOT_FOUND(#557) 症状 Error: Cannot find module 'next/dist/compiled/next-server/app-page.runtime.prod.js' コンテナを docker run した瞬間にクラッシュ。 原因 pnpmは node_modules をsymlinkベースで構築する。例えば: standalone/node_modules/next → ../../node_modules/.pnpm/next@14.2.x/node_modules/next Next.jsの @vercel/nft(Node File Tracing)はこのsymlink構造をそのままstandalone出力にコピーする。builderステージ内ではsymlink先が存在するので問題ないが、COPY --from=builder でrunnerステージに持ってくると、symlink先のディレクトリが存在しない。 builder (standalone作成時) runner (COPYした後) ├── standalone/ ├── standalone/ │ └── node_modules/ │ └── node_modules/ │ └── next → ../../.pnpm/... │ └── next → ../../.pnpm/... └── node_modules/.pnpm/... ✅ 存在 └── (なし) ❌ 試した解決策 1 RUN cp -rL /app/apps/system/.next/standalone /app/standalone cp -rL はsymlinkを辿って実ファイルをコピーするPOSIXコマンド。これで一発解決——と思った。 ...