examples/with-docker-export-output/README.md
A production-ready example demonstrating how to Dockerize Next.js applications using static export mode. This example showcases two different approaches for serving static Next.js sites: Nginx and serve package.
Nginx is ideal when you need:
docker compose up nextjs-static-export --build
Access: http://localhost:8080
docker build -t nextjs-static-export .
docker run -p 8080:8080 nextjs-static-export
Access: http://localhost:8080
serve is ideal when you need:
# OR run with serve npm package
docker compose up nextjs-static-export-with-serve --build
Access: http://localhost:3000
docker build -t nextjs-static-export-serve -f Dockerfile.serve .
docker run -p 3000:3000 nextjs-static-export-serve
Access: http://localhost:3000
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 # Nginx-based Dockerfile (port 8080)
├── Dockerfile.serve # serve-based Dockerfile (port 3000)
├── compose.yml # Docker Compose with both services
├── nginx.conf # Nginx configuration for static export
├── next.config.ts # Next.js configuration (static export 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: "export":
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
images: {
unoptimized: true, // Required for static export
},
};
export default nextConfig;
Static export mode generates a fully static HTML/CSS/JavaScript site that can be served by any static hosting service or web server. When enabled, Next.js generates an out directory containing all static files. This results in:
Note: Static export has limitations:
Learn more about Next.js static export in the official documentation.
Dockerfile)dependencies), build (builder), and runtime (runner) stagesnginxinc/nginx-unprivileged:alpine3.22 for serving static files (~50MB final image)nginx user for securityDockerfile.serve)dependencies), build (builder), and runtime (runner) stagesnode:24.13.0-slim for running serve package[email protected] for serving static filesnode user for securityNode.js version maintenance: Uses Node.js 24.13.0-slim (latest LTS at time of writing). Update the NODE_VERSION ARG to the latest LTS version for security updates.
Nginx image maintenance: Uses nginxinc/nginx-unprivileged:alpine3.22. Update the NGINXINC_IMAGE_TAG ARG to the latest version for security updates.
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:
To switch to Alpine, simply change the NODE_VERSION ARG in the Dockerfile to 24.11.1-alpine.
[!IMPORTANT] Version Maintenance:
- Node.js: 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_VERSIONARG in the Dockerfile to the latest Node.js LTS version. Check the latest version at Node.js official website and browse available Node.js images on Docker Hub.- Nginx: The Nginx Dockerfile uses
nginxinc/nginx-unprivileged:alpine3.22. Regularly check and update theNGINXINC_IMAGE_TAGARG to the latest version. Browse available Nginx images on Docker Hub.- serve package: The serve Dockerfile uses
[email protected]. Update to the latest version as needed for bug fixes and features.
Both Dockerfiles support multiple package managers:
package-lock.json)yarn.lock)pnpm-lock.yaml)The Dockerfiles automatically detect which lockfile is present and use the appropriate package manager.
This example can be deployed to any container-based platform:
Use Nginx (Dockerfile) when:
Use serve (Dockerfile.serve) when: