Self-hosting Next.js 15 (App Router + Turborepo) without full monorepo dependencies #85099
-
|
Hello team, I am trying to self-host a Next.js 15 application (using the App Router) on my own NGINX-based server as per my organization’s requirements. The project is part of a Turborepo monorepo that includes multiple packages (shared UI, utilities, etc.). In my setup, I only want to deploy the production build of a single app ( However, I am facing the following issues:
I have already:
Despite that, the standalone build still seems to depend on the full workspace context. ❓ My QuestionWhat is the recommended way to fully self-host a Next.js 15 App Router app from a Turborepo, without deploying the entire monorepo?
⚙️ Environment
💡 ExpectationIdeally,
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: [
"@workspace/ui",
"@workspace/common-ui",
"@workspace/orpc",
],
output: "standalone",
outputFileTracingRoot: __dirname,
experimental: {
reactCompiler: true,
serverActions: {
bodySizeLimit: "10mb",
},
optimizePackageImports: ["@workspace/common-ui"],
authInterrupts: true,
},
images: {
domains: [
"example.com"
],
remotePatterns: [
{
protocol: "https",
hostname: "**.example.com",
},
],
},
};
export default nextConfig; |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Hi @girishcx07 👋 Short answer: You can deploy a single Next.js app from a Turborepo without the whole monorepo, but you must (1) build with output: 'standalone' and a correct outputFileTracingRoot (the workspace root), and (2) run the generated server from .next/standalone without reinstalling packages or using a custom server.js that calls require('next'). ✅ Correct build settings (monorepo) export default /** @type {import('next').NextConfig} / ({ outputFileTracingRoot must be the repo root, not the app dir, so Next can copy anything your app imports from @workspace/* into .next/standalone/node_modules. 🚀 Deploy steps (no full monorepo) On CI/build machine (inside apps/evm): pnpm build # produces .next/standalone and .next/static Copy to the server (only what you need): apps/evm/.next/standalone/ On the server: Do NOT run "pnpm install" here; .next/standalone already contains the traced node_modules. Use the generated .next/standalone/server.js. If you run your own custom server.js that calls require('next'), you’ll hit Cannot find module 'next'—standalone doesn’t need next at runtime. 🧩 Why you saw Cannot find module 'next' Moved the folder then re-installed: running pnpm install --prod inside .next/standalone can break the traced node_modules. Don’t install there. Custom server: any require('next') or import from next/dist/* bypasses standalone. Use the generated server.js. Tracing root: if outputFileTracingRoot wasn’t the monorepo root, Next couldn’t copy @workspace/* deps, so the standalone folder referenced packages that weren’t included. 🧵 If you want an even smaller artifact (sources pruned) Use turbo prune to produce the minimal subset for apps/evm, then build/deploy that subset: from repo root This keeps only the transitive deps needed by apps/evm. |
Beta Was this translation helpful? Give feedback.
Hi @girishcx07 👋
Short answer: You can deploy a single Next.js app from a Turborepo without the whole monorepo, but you must (1) build with output: 'standalone' and a correct outputFileTracingRoot (the workspace root), and (2) run the generated server from .next/standalone without reinstalling packages or using a custom server.js that calls require('next').
If you need a fully pruned set of sources/deps, use turbo prune.
✅ Correct build settings (monorepo)
// apps/evm/next.config.mjs
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default /** @type {import('next').…