How to Create a Dockerfile for Next.js Projects
To create a
Dockerfile for a Next.js app, use a multi-stage build that first installs dependencies and builds the app, then creates a lightweight production image with the built files. This approach uses node images for building and node or alpine images for running the app efficiently.Syntax
A typical Next.js Dockerfile uses multi-stage builds with these parts:
- FROM: Defines the base image (usually a Node.js image).
- WORKDIR: Sets the working directory inside the container.
- COPY: Copies files from your project to the container.
- RUN: Runs commands like installing dependencies and building the app.
- EXPOSE: Opens the port the app will listen on.
- CMD: Defines the command to start the Next.js server.
dockerfile
FROM node:18-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine AS runner WORKDIR /app COPY --from=builder /app/package.json ./ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public EXPOSE 3000 CMD ["npm", "start"]
Example
This example Dockerfile builds a Next.js app and runs it in production mode on port 3000. It uses npm ci for clean installs and copies only necessary files to keep the image small.
dockerfile
FROM node:18-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine AS runner WORKDIR /app COPY --from=builder /app/package.json ./ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public EXPOSE 3000 CMD ["npm", "start"]
Output
Starting Next.js server on http://localhost:3000
Common Pitfalls
Common mistakes when creating a Dockerfile for Next.js include:
- Not using multi-stage builds, which leads to large images.
- Copying unnecessary files like
node_modulesfrom the host, causing conflicts. - Forgetting to expose the correct port (default is 3000).
- Running
npm startwithout building the app first.
Always build the app inside the container and copy only the build output and dependencies to the final image.
dockerfile
### Wrong approach (legacy): FROM node:18-alpine WORKDIR /app COPY . . RUN npm install CMD ["npm", "start"] ### Correct approach (multi-stage): FROM node:18-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine AS runner WORKDIR /app COPY --from=builder /app/package.json ./ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public EXPOSE 3000 CMD ["npm", "start"]
Quick Reference
Summary tips for Next.js Dockerfiles:
- Use
node:18-alpinefor a small, secure base image. - Use multi-stage builds to separate build and runtime environments.
- Run
npm cifor clean installs based onpackage-lock.json. - Copy only necessary files to the final image.
- Expose port 3000 and start the app with
npm start.
Key Takeaways
Use multi-stage Docker builds to keep Next.js images small and efficient.
Always build the Next.js app inside the container before running it.
Copy only necessary files like build output and dependencies to the final image.
Expose port 3000 in the Dockerfile to match Next.js default server port.
Use lightweight base images like node:18-alpine for production.