Dockerizing Next.js

Containerize your Next.js application with Docker

🐳 What is Docker?

Docker packages your Next.js app with all dependencies into a container that runs consistently anywhere. It ensures your app works the same in development, testing, and production environments, eliminating "works on my machine" problems.


# Build Docker image
docker build -t my-nextjs-app .

# Run container
docker run -p 3000:3000 my-nextjs-app
                                    

Docker Benefits

🔄

Consistency

Same environment everywhere

Dev Staging Production
📦

Portability

Run anywhere Docker runs

Cloud Local Server
🚀

Scalability

Easy to scale horizontally

Multiple Instances Load Balance Auto Scale
🔒

Isolation

Apps run in isolated containers

Security No Conflicts Clean State

🔹 Basic Dockerfile

Create a Dockerfile to containerize your Next.js app. This multi-stage build optimizes image size by separating dependencies installation from the final runtime. It uses Node.js Alpine for a smaller footprint and faster deployments.

# Dockerfile
FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

# Build the app
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production

COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]

🔹 Configure Next.js for Docker

Enable standalone output mode in Next.js configuration. This creates a minimal production build with only necessary files, reducing Docker image size significantly. The standalone mode includes a built-in server for running your app.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  // Optional: Reduce image size further
  experimental: {
    outputFileTracingRoot: undefined,
  },
}

module.exports = nextConfig

🔹 Create .dockerignore

Exclude unnecessary files from Docker build context. This speeds up builds and reduces image size by preventing node_modules, git files, and other development artifacts from being copied into the container.

# .dockerignore
node_modules
.next
.git
.gitignore
README.md
.env*.local
.vscode
.idea
npm-debug.log
yarn-error.log
.DS_Store

🔹 Build and Run Docker Image

Build your Docker image and run it locally. The build process installs dependencies, compiles your Next.js app, and packages everything into a container. You can then run multiple instances or deploy to any Docker-compatible platform.

# Build the Docker image
docker build -t my-nextjs-app .

# Run the container
docker run -p 3000:3000 my-nextjs-app

# Run with environment variables
docker run -p 3000:3000 \
  -e DATABASE_URL="your-db-url" \
  -e API_KEY="your-api-key" \
  my-nextjs-app

# Run in detached mode (background)
docker run -d -p 3000:3000 --name nextjs-container my-nextjs-app

# View running containers
docker ps

# Stop container
docker stop nextjs-container

🔹 Docker Compose Setup

Use Docker Compose to manage multi-container applications. This is useful when your Next.js app needs a database, Redis, or other services. Docker Compose orchestrates all services with a single command.

# docker-compose.yml
version: '3.8'

services:
  nextjs:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - NODE_ENV=production
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:
# Start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop all services
docker-compose down

🔹 Optimize Docker Image

Reduce image size and improve build times with optimization techniques. Use multi-stage builds, Alpine Linux base images, and layer caching. Smaller images deploy faster and use less storage.

Optimization Tips:

  • Multi-stage builds: Separate build and runtime stages
  • Alpine images: Use lightweight Alpine Linux base
  • Layer caching: Order commands to maximize cache hits
  • .dockerignore: Exclude unnecessary files
  • Standalone output: Use Next.js standalone mode
# Check image size
docker images my-nextjs-app

# Remove unused images
docker image prune -a

# Build with no cache (fresh build)
docker build --no-cache -t my-nextjs-app .

🧠 Test Your Knowledge

What Next.js config option is needed for Docker standalone builds?