Shrink Next.js Deploys with Standalone Output
06 Jan 2026A default next build output includes your entire node_modules folder in the deploy artifact. On a real project that’s often 300-500MB of dependencies your running app never touches at runtime — devDependencies, unused transitive packages, source maps. Standalone output cuts that down to what’s actually needed to run node server.js.
What standalone output actually does
Next.js traces your app’s real runtime dependency graph and copies only those files, plus a minimal server.js, into .next/standalone. It does not bundle everything into one file — it’s a pruned copy of node_modules sized to what your server actually imports.
Enable it in next.config.js:
/** @type {import('next').NextConfig} */
module.exports = {
output: 'standalone',
}
Run the build:
npm run build
You now have .next/standalone/server.js, a trimmed node_modules, and your compiled app. That’s the whole deploy artifact.
The two things people forget to copy
Standalone output does not automatically include your public/ folder or the static assets in .next/static. Miss this and your images 404 and your CSS loads unstyled in production, even though the build “succeeded.”
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/static
Put this in your Dockerfile or deploy script, not as a manual step you’ll forget the second time.
A Dockerfile that uses it
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
This runner stage never installs node_modules itself — the standalone output already carries a pruned copy. That’s the entire size win: no npm ci in the final image, no devDependencies, no build tools.
What the size difference looks like
A typical mid-size Next.js app:
| Output | Approx. image size |
|---|---|
Full node_modules copied in |
700MB-1.2GB |
| Standalone output | 150-250MB |
Smaller images mean faster deploys, faster cold starts if you’re on serverless containers, and less to pull on every rolling restart.
The gotcha: native modules and monorepos
If you use sharp, bcrypt, or anything with native bindings, verify they survive the trace. Next.js’s file tracing sometimes misses platform-specific binaries in monorepos or when a package’s package.json doesn’t declare its native dependency cleanly. Test the standalone build in a container matching your production OS before you trust it — don’t just run node server.js locally on macOS and assume Linux will behave the same.
If you’re in a monorepo (Turborepo, Nx), set outputFileTracingRoot in next.config.js to point at your repo root, or the trace will miss shared packages:
module.exports = {
output: 'standalone',
outputFileTracingRoot: require('path').join(__dirname, '../../'),
}
Without this, you’ll get a Cannot find module error at runtime for a package that clearly exists — it just wasn’t traced because Next.js only looked inside the app’s own directory.
The takeaway
Standalone output is the difference between shipping your whole node_modules and shipping only what your server calls. Turn it on, copy public/ and .next/static manually, and verify native modules in a Linux container before you deploy.