DEV Community

Sohana Akbar
Sohana Akbar

Posted on

Multi-Stage Builds for a Next.js App — Reduce Image Size by 70%

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.

Top comments (0)