DH
4 min read

Containerizing Next.js 15 (App Router) for Local and Production

Dockerize a Next.js 15 project created with the App Router, using multi-stage builds and Docker Compose.

dockernextjs

Next.js 15 introduces continued improvements to the App Router, and the create-next-app CLI now provides flags like --app, --src-dir, and --typescript to streamline project setup. In this tutorial, we’ll containerize such a project for both local development (with hot reloading) and production builds via Docker.

1. Creating a Next.js 15 Project

We’ll use the official CLI options:

npx create-next-app@latest my-next-app \
--app \
--src-dir \
--typescript

This instructs the CLI to:

  • --app: Use the new App Router (app/ directory).
  • --src-dir: Place code in a src directory for clearer organization.
  • --typescript: Enable TypeScript from the start.

You’ll end up with a folder structure like:

my-next-app/
├─ src/
│ ├─ app/
│ │ ├─ layout.tsx
│ │ └─ page.tsx
├─ package.json
├─ tsconfig.json
├─ next.config.ts
└─ ...

Then, verify everything runs:

cd my-next-app
npm run dev

Visit http://localhost:3000 to see the starter page.

2. next.config.ts

Your new Next.js project includes a TypeScript-based config (next.config.ts). A typical minimal configuration looks like:

import { NextConfig } from "next";

const nextConfig: NextConfig = {
reactStrictMode: true,
};

export default nextConfig;

When you run npm run build or npm run dev, Next.js automatically detects and uses next.config.ts.

3. Dockerfile for Production (Multi-Stage)

Create a Dockerfile in your project root:

# 1. Use Node 18 Alpine as the build base
FROM node:23-alpine AS builder

# 2. Create and set working directory
WORKDIR /app

# 3. Copy dependency manifests
COPY package*.json ./

# 4. Install dependencies
RUN npm install

# 5. Copy the source code (including next.config.ts)
COPY . .

# 6. Build the Next.js app
RUN npm run build

# 7. Use a fresh Node 18 Alpine image for final stage
FROM node:23-alpine AS runner

# 8. Set the working directory
WORKDIR /app

# 9. Copy only necessary files
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.ts ./

# 10. Expose port 3000
EXPOSE 3000

# 11. Install only production dependencies
RUN npm install --production

# 12. Start the Next.js server
CMD ["npm", "start"]

Highlights

  • Multi-stage: We install and build in builder, then copy only compiled files to the smaller runner image.
  • App Router: .next includes server/client components for Next.js 15’s new structure.
  • next.config.ts: Copied so server code can reference it in the final container.

4. Optional Dockerfile for Local Development

If you want to develop inside Docker with hot reloading, create a Dockerfile.dev:

FROM node:23-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "dev"]

Running this container with volume mounts ensures that changes on your host will trigger reloads in the container.

5. Docker Compose Configuration

5.1 Local Development

Create or update docker-compose.yml:

version: '3.8'
services:
nextjs-dev:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./:/app
- /app/node_modules
  • ./:/app: Maps your local directory to the container, enabling hot reload.
  • /app/node_modules: Prevents overwriting the container’s node_modules with an empty directory from your host.

Run:

docker-compose up --build

Then visit http://localhost:3000.

5.2 Production with Docker Compose

If you want Compose to handle production too:

version: '3.8'
services:
nextjs-prod:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
# environment:
# NODE_ENV: production

6. Build and Run in Production (Without Compose)

If you prefer to go manual:

  1. Build:
    docker build -t my-next15-app:latest .
  2. Run:
    docker run -p 3000:3000 my-next15-app:latest

Open http://localhost:3000.

7. Common Pitfalls

  • Forgetting public/: Make sure static assets are copied into the final container.
  • Hot-reload Performance: In some OSes, dev in Docker can be slower due to file sync overhead.
  • App Router: The new structure differs from older pages/ setups; ensure .next and next.config.ts are copied properly.

8. Conclusion

By Dockerizing your Next.js 15 project (created with --app, --src-dir, and --typescript):

  • You ensure a consistent environment for dev and production.
  • Multi-stage builds keep production images lightweight.
  • Docker Compose simplifies both hot-reload dev and orchestrated production.

With this setup, you’ll have a smooth, repeatable workflow to leverage all the App Router features in Next.js 15—free from environment mismatches or manual deployment hassles.

Damian Hodgkiss

Damian Hodgkiss

Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.

Creating Freedom

Join me on the journey from engineer to solopreneur. Learn how to build profitable SaaS products while keeping your technical edge.

    Proven strategies

    Learn the counterintuitive ways to find and validate SaaS ideas

    Technical insights

    From choosing tech stacks to building your MVP efficiently

    Founder mindset

    Transform from engineer to entrepreneur with practical steps