A production-ready example demonstrating how to Dockerize Next.js applications using standalone mode. This example showcases best practices for containerizing Next.js apps with Docker.
- Multi-stage Docker build for optimal image size
- Next.js standalone mode for minimal production builds
- Security best practices (non-root user)
- Slim Linux base image for optimal compatibility and smaller size
- BuildKit cache mounts for faster builds
- Production-ready configuration
The compose.yml includes both Node.js and Bun configurations. Run one service at a time to avoid port conflicts.
Node.js:
# Run with Node.js
docker compose up nextjs-standalone --buildBun:
# OR run with Bun
docker compose up nextjs-standalone-with-bun --buildStop the application:
docker compose downNode.js:
# Build the image
docker build -t nextjs-standalone-image .
# Run the container
docker run -p 3000:3000 nextjs-standalone-imageBun:
# Build the image
docker build -f Dockerfile.bun -t nextjs-standalone-bun-image .
# Run the container
docker run -p 3000:3000 nextjs-standalone-bun-imageOpen your browser: Navigate to http://localhost:3000
To add Docker support to your existing Next.js project:
- Copy the
Dockerfile(orDockerfile.bunfor Bun) to your project root. - Copy the
.dockerignoreto your project root. - Add the following to your
next.config.js(ornext.config.ts):
// next.config.js
module.exports = {
output: "standalone",
};This will build the project as a standalone app inside the Docker image.
nextjs-docker/
├── app/ # Next.js App Router directory
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Home page with example content
│ └── globals.css # Global styles with Tailwind CSS v4
├── public/ # Static assets
│ └── next.svg # Next.js logo
├── Dockerfile # Multi-stage Docker configuration (Node.js)
├── Dockerfile.bun # Multi-stage Docker configuration (Bun)
├── compose.yml # Docker Compose configuration (Node.js & Bun services)
├── next.config.ts # Next.js configuration (standalone mode)
├── postcss.config.js # PostCSS configuration for Tailwind CSS
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── README.md # This file
The next.config.ts file is configured with output: "standalone":
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;The standalone output mode creates a minimal, self-contained production build optimized for containerized deployments. When enabled, Next.js generates a .next/standalone directory containing only the essential files needed to run your application, excluding unnecessary dependencies and files. This results in significantly smaller Docker images and faster container startup times.
Learn more about Next.js standalone output in the official documentation.
- Multi-stage build: Separates dependency installation (
dependencies), build (builder), and runtime (runner) stages - Slim Linux: Uses
slimimage tag for optimal compatibility and smaller image size - BuildKit cache mounts: Speeds up builds by caching package manager stores (
/root/.npm,/usr/local/share/.cache/yarn,/root/.local/share/pnpm/store). See the Dockerfile for an optional.next/cachemount to speed up rebuilds. - Non-root user: Runs as
nodeuser for security - Optimized layers: Leverages Docker layer caching effectively
- Standalone output: Copies only the necessary files from
.next/standaloneand.next/static - Writable
.nextdirectory: The.nextdirectory is created and owned by thenodeuser so the server can write prerender cache and optimized images at runtime - Node.js version maintenance: Uses Node.js 24.13.0-slim (latest LTS at time of writing). Update the
NODE_VERSIONARG to the latest LTS version for security updates.
- Multi-stage build: Same three-stage pattern optimized for Bun
- Official Bun image: Uses
oven/bun:1for optimal Bun performance - Non-root user: Runs as built-in
bunuser for security - Frozen lockfile: Uses
bun.lockfor reproducible builds - Standalone output: Same optimized output as the Node.js version, with writable
.nextdirectory for runtime cache
Why Node.js slim image tag?: The slim variant provides optimal compatibility with npm packages and native dependencies while maintaining a smaller image size (~226MB). Slim uses glibc (standard Linux), ensuring better compatibility than Alpine's musl libc, which can cause issues with some npm packages. This makes it ideal for public examples where reliability and compatibility are priorities.
When to use Alpine?: Consider using node:24.11.1-alpine instead if:
- Image size is critical: Alpine images are typically ~100MB smaller than slim variants (~110MB base vs ~226MB)
- Your dependencies are compatible: Your npm packages don't require native binaries that depend on glibc
- You've tested thoroughly: You've verified all your dependencies work correctly with musl libc
- Security-focused deployments: Alpine's minimal attack surface can be beneficial for security-sensitive applications
To switch to Alpine, simply change the NODE_VERSION ARG in the Dockerfile to 24.11.1-alpine.
Important
Node.js Version Maintenance: This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing. To ensure security and stay up-to-date, regularly check and update the NODE_VERSION ARG in the Dockerfile to the latest Node.js LTS version. Check the latest version at Nodejs official website and browse available Node.js images on Docker Hub.
This example can be deployed to any container-based platform:
- Google Cloud Run
- AWS ECS/Fargate
- Azure Container Instances
- DigitalOcean App Platform
- Any Kubernetes cluster
-
Install the Google Cloud SDK so you can use
gcloudon the command line. -
Run
gcloud auth loginto log in to your account. -
Create a new project in Google Cloud Run (e.g.
nextjs-docker). Ensure billing is turned on. -
Build your container image using Cloud Build:
gcloud builds submit --tag gcr.io/PROJECT-ID/nextjs-docker --project PROJECT-ID
This will also enable Cloud Build for your project.
-
Deploy to Cloud Run:
gcloud run deploy --image gcr.io/PROJECT-ID/nextjs-docker --project PROJECT-ID --platform managed --allow-unauthenticated
- You will be prompted for the service name: press Enter to accept the default name,
nextjs-docker. - You will be prompted for region: select the region of your choice, for example
us-central1.
- You will be prompted for the service name: press Enter to accept the default name,
- Next.js Documentation - Comprehensive Next.js documentation
- Next.js Templates - Browse and deploy Next.js templates
- Next.js Examples - Discover boilerplate example projects
- Deploy to Vercel - Instantly deploy your Next.js site
- Learn Docker - Get started with Docker fundamentals, containerization, and deployment
- Docker Documentation - Comprehensive Docker documentation and reference guides
- React.js Docker Guide - Official Docker guide for React.js applications following best practices for containerization