Introduction
Running a Next.js application in production is not just about writing code. It’s about packaging it efficiently. Docker is commonly used for deployment, but a naive setup often results in large images, slow builds, and inefficient runtime behavior.
The goal is to create a lightweight, predictable, and production-ready container that only includes what is necessary to run the application.
The Problem
A typical mistake is using a single-stage Dockerfile that installs everything and ships it to production.
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["npm", "start"]
This approach includes development dependencies, increases image size, and slows down deployment.
- Large Docker image size
- Slow build times
- Unnecessary files in production
- Security risks from unused dependencies
System Design / Approach
The solution is to use a multi-stage Docker build and leverage Next.js standalone output.
- Build the app in one stage
- Copy only required files to final image
- Exclude dev dependencies
- Keep runtime image minimal
This reduces image size and improves performance without changing application logic.
Implementation
Step 1: Enable Standalone Output
Configure Next.js to generate a standalone build.
// next.config.js
module.exports = {
output: "standalone",
};
This allows Docker to run the app without installing full dependencies.
Step 2: Use Multi-Stage Build
Separate build and runtime environments.
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
CMD ["node", "server.js"]
The final image only contains compiled output, making it much smaller.
Step 3: Handle Environment Variables
Separate build-time and runtime environment variables.
ENV NODE_ENV=production
Avoid hardcoding secrets during build time.
Trade-offs
| Approach | Benefit | Cost |
|---|---|---|
| Multi-stage build | Smaller image size | More setup complexity |
| Standalone output | Simplified deployment | Requires config changes |
Real-World Impact
- Reduced Docker image size significantly
- Faster deployment times
- Improved startup performance
- Cleaner production environment