All writeups
DevOps

Multi-Stage Docker Builds for a Next.js App

Using standalone output and a three-stage Dockerfile to ship a small production image that runs as a non-root user.

Jul 2, 2026 · 5 min read

A single-stage Dockerfile for a Next.js app ships the whole toolchain: dev dependencies, source files and build cache. Splitting the build into stages keeps only what the server needs at runtime.

Enable standalone output

With standalone output, Next.js traces the files the server actually imports and copies them into .next/standalone, including a minimal node_modules.

ts
// next.config.ts
const nextConfig = {
  output: "standalone",
};

export default nextConfig;

The Dockerfile

dockerfile
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER node
EXPOSE 3000
CMD ["node", "server.js"]

Why it matters

  • Smaller images pull and deploy faster.
  • Fewer packages in the final image means a smaller attack surface.
  • Running as the node user limits what a compromised process can do.
DockerNext.jsCI/CD