Skip to content

Docker with Next.js

Docker packages your Next.js app, its dependencies, and its runtime into a single, portable container image — useful for self-hosting or deploying to any cloud provider that supports containers. This lesson covers a production-ready setup.

The standalone Output Mode

Next.js includes a built-in output: "standalone" configuration specifically designed for Docker deployments. It produces a minimal folder containing only the files actually needed to run the app in production — including a trimmed node_modules — rather than requiring the entire project (dev dependencies included) inside the container image.

Combined with a multi-stage Dockerfile (building in one stage, copying only the final output into a lean runtime stage), this keeps container images significantly smaller and faster to deploy than a naive "copy everything" approach.

// next.config.js
module.exports = {
  output: "standalone",
};

This single config option is the prerequisite for the minimal, production-ready Dockerfile pattern shown below.

A Minimal Multi-Stage Dockerfile

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
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"]
  • The builder stage installs dependencies and runs next build with full dev dependencies available.
  • The runner stage only copies the standalone output, keeping the final image small.
  • node server.js (generated by the standalone build) starts the production server directly.
  • Static assets (.next/static and public/) must be copied explicitly alongside the standalone folder.

Docker Deployment Cheat Sheet

Key files and settings for a Next.js Docker setup.

Item Purpose
output: "standalone" Produces a minimal, self-contained build output
Multi-stage Dockerfile Keeps the final image small by separating build and runtime
.dockerignore Excludes node_modules/.git from the Docker build context
EXPOSE 3000 Documents the port the container listens on
Environment variables Passed at container runtime, not baked into the image

Using a .dockerignore File

Without a .dockerignore file, Docker copies your entire project directory (including node_modules and .git) into the build context, slowing down builds significantly. Excluding these directories mirrors what .gitignore already does for version control.

# .dockerignore
node_modules
.next
.git
.env.local

Passing Environment Variables at Runtime

Secrets and configuration should be passed to the running container (via docker run -e or your orchestration platform's secret management), not baked into the image at build time — this keeps the same image usable across different environments (staging, production) with different configuration.

docker run -p 3000:3000 -e DATABASE_URL="postgres://..." my-next-app

Common Mistakes

  • Forgetting output: "standalone", resulting in a much larger and less efficient container image.
  • Not using a .dockerignore file, bloating the build context unnecessarily.
  • Baking secrets directly into the image instead of passing them at container runtime.
  • Using a single-stage Dockerfile that ships dev dependencies into the production image.

Key Takeaways

  • output: "standalone" produces a minimal build output tailored for containerized deployments.
  • A multi-stage Dockerfile keeps the final production image small and efficient.
  • A .dockerignore file excludes unnecessary files from the Docker build context.
  • Environment variables should be passed at runtime, not embedded into the image.
  • Docker gives you a portable deployment target usable across any container-supporting cloud provider.

Pro Tip

Test your Docker image locally with docker run before deploying it anywhere — confirming the standalone build actually starts and serves your app correctly in an isolated container catches configuration issues (like missing environment variables) much earlier than discovering them in a remote deployment.