Next.js apps are heavy. A standard Docker build often exceeds 1 GB, wasting bandwidth, disk space, and deployment time.
The fix? Multi-stage builds. With a simple Dockerfile change, you can shrink that image by 70% or more.
Why is the default image so big?
A typical single-stage Dockerfile:
Installs node_modules (including devDependencies like TypeScript, ESLint, testing tools).
Keeps build caches and source maps.
Includes the entire build toolchain inside the final image.
You don’t need any of that in production.
The multi-stage approach
Split the build into three stages:
Dependencies – Install everything.
Builder – Run next build.
Production – Copy only the bare essentials.
Example Dockerfile
dockerfile
Stage 1: Dependencies
FROM node:18-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
Stage 2: Builder
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
Stage 3: Production
FROM node:18-alpine AS runner
WORKDIR /app
Copy standalone output (Next.js 12+)
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"]
Key line:
COPY --from=builder /app/.next/standalone ./ — This is the magic. It grabs only the compiled production output.
Results
Metric Single-stage Multi-stage Reduction
Image size 1.2 GB 280 MB 77%
Node modules Full (300 MB) None in final 100%
Build artifacts Kept Removed –
Pro tips for even smaller images
Enable standalone output – In next.config.js:
js
module.exports = {
output: 'standalone',
}
This creates a self-contained server.js with minimal dependencies.
Use Alpine Linux – node:18-alpine is ~50 MB vs node:18 (~1 GB).
Add .dockerignore – Exclude .git, .next, node_modules, Dockerfile.
Run as non-root – Add RUN addgroup --system --gid 1001 nodejs + USER nodejs for security.
What about caching?
Multi-stage still caches well. Docker caches each stage independently. Your CI will rebuild only changed layers.
The bottom line
Switching to multi-stage builds is 15 minutes of work for a 70%+ size reduction. Smaller images mean:
Faster deployments
Lower storage costs
Quicker container pulls (especially on Kubernetes)
Try it on your next Next.js project. Your DevOps team will thank you.
Need a working example? Check out the official Next.js Docker example in their GitHub repo.

























