next-bun-compile handles monorepo layouts automatically. The adapter
assembles its traced output tree with the app path preserved
(e.g. apps/web/... relative to the repo root), and the compiled
binary lands at apps/web/dist/app.
One monorepo-specific requirement: Next.js resolves adapterPath
from its own package location, so a nested workspace dependency
needs resolving from the app dir:
// apps/web/next.config.ts
import { createRequire } from "node:module";
import type { NextConfig } from "next";
const req = createRequire(process.cwd() + "/");
const nextConfig: NextConfig = {
adapterPath: req.resolve("next-bun-compile"),
};
export default nextConfig;Bun workspaces
Standard bun workspaces setup, no special config:
// package.json (monorepo root)
{
"private": true,
"workspaces": ["apps/*", "packages/*"]
}// apps/web/package.json
{
"name": "web",
"scripts": {
"build": "next build"
},
"dependencies": {
"@example/shared": "*",
"next": "^16.2",
"next-bun-compile": "^1"
}
}bun install # at the monorepo root
cd apps/web && bun run build # produces apps/web/dist/appA full working monorepo + sharp example is in Recipes › Monorepo + sharp.
pnpm workspaces
Same idea — pnpm’s .pnpm/ hoisted store is handled the same way as
bun’s .bun/ store. next-bun-compile walks both.
Turborepo
Works as-is. If you use turbo prune --docker in CI to slim the
Docker context, the pruned tree still triggers the nested-layout
codepath. next-bun-compile doesn’t care about the workspace tool — only
about the standalone layout Next.js produces.
What next-bun-compile does for monorepos
- Preserves the nested layout. The adapter’s assembled tree keys
files relative to the repo root, so
apps/webnesting survives into the binary’s extraction layout. - Merges split
node_modules/. Traced outputs can place partial package copies at multiple levels; next-bun-compile merges every location per file so nothing is lost to hoisting. - Workspace packages survive. Anything Next.js’s trace pulled in
(including workspace
*-version deps) gets embedded from wherever it landed — including plain data files. A route reading../../packages/shared/fonts/body.ttfthroughfsworks, because the binary mirrors the assembled tree and runs from the app dir inside it, exactly asoutput: "standalone"does.
Where the runtime tree lands
The extracted tree keeps its workspace path, so an app at apps/web
extracts to $NBC_RUNTIME_DIR/apps/web, not $NBC_RUNTIME_DIR. That is
what keeps ../../packages/... resolvable at runtime. The build prints
it:
next-bun-compile: monorepo layout — runtime tree extracts to <NBC_RUNTIME_DIR>/apps/webIt only matters if you pre-extract with --extract or mount a volume
into the tree — see Docker. A single-app repo is
unaffected: there is no workspace path, so the tree extracts straight
into NBC_RUNTIME_DIR as before.
Common gotchas
“Cannot find module ‘next-bun-compile’”
Next resolves the bare adapterPath string from its own package
location, which in a monorepo may not see your app’s nested
dependency. Use the createRequire(process.cwd() + "/") pattern
shown above.
Mixed package managers
If your CI installs with bun but your repo also has a stale
package-lock.json, Next.js’s “multiple lockfiles” warning may
confuse workspace detection. Remove the unused lockfile.