Dockerizing Next.js with Multi-Stage Builds
Use a multi-stage Dockerfile to create lean, production-ready Next.js images. This separates build-time dependencies from the runtime environment, shrinking image size.
WHY IT EXISTS: A naive docker build on a Next.js project creates a huge, slow, and insecure image. It bundles build tools, source code, and development dependencies into the final container that runs in production. This is inefficient for storage, slow to deploy, and increases the potential attack surface.
THE MENTAL MODEL: Think of building a car. You have a massive factory (the build stage) with all the robots, tools, and raw materials. This corresponds to your devDependencies, TypeScript compiler, and linters. When the car is finished, you don't ship the whole factory. You just ship the car to the customer. A multi-stage Dockerfile lets you do this: build the app in a full-featured environment, then copy only the finished product to a minimal, clean runtime environment.
HOW IT WORKS: A multi-stage Dockerfile contains multiple FROM instructions, creating distinct stages. The first stage, often named builder, starts with a full Node.js image. It installs all dependencies (npm install) and builds the Next.js application (npm run build). A crucial step is enabling the standalone output mode in next.config.js. This tells Next.js to package only the necessary server files and a minimal node_modules into a special .next/standalone directory. The second and final stage starts from a minimal base image, like node:20-alpine. It copies only the essential artifacts from the builder stage: the .next/standalone directory, the .next/static folder, and the public folder. The final image is tiny because it contains none of the original source code or development dependencies.
WHEN TO USE IT: Always use multi-stage builds for creating production-ready Docker images for your Next.js application. This is the industry standard for creating optimized, secure, and fast-deploying containers. It guarantees a consistent environment from your CI/CD pipeline to production.
WHEN NOT TO USE IT: For local development, a multi-stage build is overkill and slow. During development, you prioritize a fast feedback loop. A better approach is using docker-compose with a volume mount (-v .:/app) to sync your local code changes into a running container without needing to rebuild the image constantly.
ONE CANONICAL EXAMPLE: A production Dockerfile for Next.js will have a builder stage that runs npm run build. The final stage will start FROM node:20-alpine and use COPY --from=builder to pull in three key directories: /public, /.next/static, and /.next/standalone. The final command will be something like CMD ["node", "server.js"] executed from within the copied standalone directory.
Read the original → docs.docker.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.