nextjs - ✅(Solved) Fix Turbopack serverExternalPackages: relative symlinks in .next/node_modules break in multi-stage Docker builds [1 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#89851Fetched 2026-04-08 00:21:13
View on GitHub
Comments
2
Participants
3
Timeline
9
Reactions
5
Author
Timeline (top)
labeled ×3commented ×2cross-referenced ×2referenced ×1

Error Message

  1. Observe HTTP 500 with Error: Cannot find module 'pino-28069d5257187539'

Fix Action

Fix / Workaround

There is also a secondary issue: the server runtime emits require('pino-28069d5257187539') — a hashed module name that Node.js cannot resolve through standard module resolution, even if the symlink target were valid. This requires NODE_PATH=.next/node_modules as a workaround.

Workaround: Rewrite symlinks in the runtime Dockerfile stage and set NODE_PATH:

PR fix notes

PR #2371: fix(web): preserve Turbopack symlinks in Docker standalone copy

Description (problem / solution / changelog)

Summary

  • Turbopack 16.1.x creates relative symlinks in .next/standalone/node_modules for serverExternalPackages (e.g. dd-trace, bullmq, pg). Docker COPY in multi-stage builds does not preserve these symlinks, breaking module resolution at runtime and causing the web pod to crash loop.
  • Replace COPY --from=builder with RUN --mount + cp -a (archive mode) which preserves symlinks. All 592 symlinks in standalone are relative and resolve within the tree, so they work in the final image.
  • Verified locally: server starts in 170ms with all modules resolving correctly.

References: vercel/next.js#89851

Test plan

  • Docker build succeeds locally
  • Server starts without hang or crash (Ready in 170ms)
  • dd-trace and mangled dd-trace-7beb4d2325fcd252 both resolve correctly
  • styled-jsx resolves through Next.js require hooks (server starts)
  • Deploy to staging and verify pod stability

Changed files

  • apps/web/docker/Dockerfile (modified, +5/-2)

Code Example

Builder:  /workspace/app/.next/node_modules/pino-<hash> -> ../../../node_modules/pino  ✅ resolves to /workspace/node_modules/pino
Runtime:  /app/.next/node_modules/pino-<hash> -> ../../../node_modules/pino            ❌ resolves to /../node_modules/pino

---

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0
  Available memory (MB): 49152
  Available CPU cores: 12
Binaries:
  Node: 22.14.0
  npm: 11.4.1
  Yarn: N/A
  pnpm: 10.28.2
Relevant Packages:
  next: 16.1.6 // Latest available version is detected (16.1.6).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

---

RUN find .next/node_modules -type l | while read link; do       target=$(readlink "$link" | sed 's|^\.\./||');       ln -sf "$target" "$link";     done
ENV NODE_PATH=/app/.next/node_modules
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/kyeb/turbopack-symlink-repro

To Reproduce

  1. Clone the repo: git clone https://github.com/kyeb/turbopack-symlink-repro
  2. Build the Docker image: docker build -t repro .
  3. Run it: docker run -p 3000:3000 repro
  4. Request the page: curl http://localhost:3000/
  5. Observe HTTP 500 with Error: Cannot find module 'pino-28069d5257187539'

The Dockerfile simulates a standard monorepo multi-stage Docker build: the Next.js app is built at /workspace/app/ with node_modules hoisted to /workspace/, then .next and node_modules are copied to /app/ in the runtime stage (one level shallower).

Current vs. Expected behavior

Current: next build with Turbopack creates .next/node_modules/pino-28069d5257187539 as a relative symlink (../../../node_modules/pino) based on the build-time directory depth. When .next is copied to a shallower path in the runtime stage of a multi-stage Docker build, the symlink overshoots the filesystem root and breaks:

Builder:  /workspace/app/.next/node_modules/pino-<hash> -> ../../../node_modules/pino  ✅ resolves to /workspace/node_modules/pino
Runtime:  /app/.next/node_modules/pino-<hash> -> ../../../node_modules/pino            ❌ resolves to /../node_modules/pino

There is also a secondary issue: the server runtime emits require('pino-28069d5257187539') — a hashed module name that Node.js cannot resolve through standard module resolution, even if the symlink target were valid. This requires NODE_PATH=.next/node_modules as a workaround.

Expected: Symlinks in .next/node_modules/ should survive being copied to a different directory depth (e.g., use symlinks relative to .next itself, or copy the actual files). The hashed module names should also be resolvable without NODE_PATH.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0
  Available memory (MB): 49152
  Available CPU cores: 12
Binaries:
  Node: 22.14.0
  npm: 11.4.1
  Yarn: N/A
  pnpm: 10.28.2
Relevant Packages:
  next: 16.1.6 // Latest available version is detected (16.1.6).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack, Module Resolution, Output

Which stage(s) are affected? (Select all that apply)

next build (local), next start (local)

Additional context

This affects any monorepo using serverExternalPackages with a multi-stage Docker build where .next is copied to a different directory depth than where it was built. This is a common pattern with pnpm workspaces where node_modules is hoisted to the workspace root.

Workaround: Rewrite symlinks in the runtime Dockerfile stage and set NODE_PATH:

RUN find .next/node_modules -type l | while read link; do       target=$(readlink "$link" | sed 's|^\.\./||');       ln -sf "$target" "$link";     done
ENV NODE_PATH=/app/.next/node_modules

The repro repository is 8 files — a minimal Next.js app with pino in serverExternalPackages and a multi-stage Dockerfile that demonstrates the breakage. See the README for full details and a diagram.

extent analysis

Quick Fixes

| Problem | Fix | One‑

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - ✅(Solved) Fix Turbopack serverExternalPackages: relative symlinks in .next/node_modules break in multi-stage Docker builds [1 pull requests, 2 comments, 3 participants]