Docker Configuration
Patterns for containerizing Next.js applications with Docker.
Overview#
Docker enables consistent deployments across environments. This pattern covers:
- Multi-stage Dockerfile
- Docker Compose for development
- Production configuration
- Health checks
- Prisma in Docker
Prerequisites#
# Docker Desktop or Docker Engine installed
docker --versionCode Example#
Multi-Stage Dockerfile#
1# Dockerfile
2FROM node:20-alpine AS base
3
4# Install dependencies only when needed
5FROM base AS deps
6RUN apk add --no-cache libc6-compat
7WORKDIR /app
8
9COPY package.json package-lock.json* ./
10RUN npm ci
11
12# Rebuild source code only when needed
13FROM base AS builder
14WORKDIR /app
15COPY /app/node_modules ./node_modules
16COPY . .
17
18ENV NEXT_TELEMETRY_DISABLED 1
19
20RUN npm run build
21
22# Production image
23FROM base AS runner
24WORKDIR /app
25
26ENV NODE_ENV production
27ENV NEXT_TELEMETRY_DISABLED 1
28
29RUN addgroup --system --gid 1001 nodejs
30RUN adduser --system --uid 1001 nextjs
31
32COPY /app/public ./public
33
34# Standalone output
35COPY /app/.next/standalone ./
36COPY /app/.next/static ./.next/static
37
38USER nextjs
39
40EXPOSE 3000
41ENV PORT 3000
42ENV HOSTNAME "0.0.0.0"
43
44CMD ["node", "server.js"]Next.js Standalone Configuration#
1// next.config.js
2/** @type {import('next').NextConfig} */
3const nextConfig = {
4 output: 'standalone'
5}
6
7module.exports = nextConfigDocker Compose for Production#
1# docker-compose.yml
2version: '3.8'
3
4services:
5 app:
6 build:
7 context: .
8 dockerfile: Dockerfile
9 ports:
10 - "3000:3000"
11 environment:
12 - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
13 - REDIS_URL=redis://redis:6379
14 depends_on:
15 db:
16 condition: service_healthy
17 redis:
18 condition: service_started
19 healthcheck:
20 test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
21 interval: 30s
22 timeout: 10s
23 retries: 3
24 start_period: 10s
25
26 db:
27 image: postgres:15-alpine
28 volumes:
29 - postgres_data:/var/lib/postgresql/data
30 environment:
31 - POSTGRES_USER=postgres
32 - POSTGRES_PASSWORD=password
33 - POSTGRES_DB=myapp
34 ports:
35 - "5432:5432"
36 healthcheck:
37 test: ["CMD-SHELL", "pg_isready -U postgres"]
38 interval: 10s
39 timeout: 5s
40 retries: 5
41
42 redis:
43 image: redis:7-alpine
44 volumes:
45 - redis_data:/data
46 ports:
47 - "6379:6379"
48
49volumes:
50 postgres_data:
51 redis_data:Development Docker Compose#
1# docker-compose.dev.yml
2version: '3.8'
3
4services:
5 app:
6 build:
7 context: .
8 dockerfile: Dockerfile.dev
9 volumes:
10 - .:/app
11 - /app/node_modules
12 ports:
13 - "3000:3000"
14 environment:
15 - NODE_ENV=development
16 - DATABASE_URL=postgresql://postgres:password@db:5432/myapp_dev
17 command: npm run dev
18
19 db:
20 image: postgres:15-alpine
21 environment:
22 - POSTGRES_PASSWORD=password
23 - POSTGRES_DB=myapp_dev
24 ports:
25 - "5432:5432"
26 volumes:
27 - dev_postgres_data:/var/lib/postgresql/data
28
29volumes:
30 dev_postgres_data:Development Dockerfile#
1# Dockerfile.dev
2FROM node:20-alpine
3
4WORKDIR /app
5
6# Install dependencies
7COPY package.json package-lock.json* ./
8RUN npm install
9
10# Copy source
11COPY . .
12
13# Generate Prisma client
14RUN npx prisma generate
15
16EXPOSE 3000
17
18CMD ["npm", "run", "dev"].dockerignore#
# .dockerignore
node_modules
.next
.git
.gitignore
*.md
.env*
!.env.example
.DS_Store
coverage
.nyc_output
dist
.turbo
Health Check Endpoint#
1// app/api/health/route.ts
2import { prisma } from '@/lib/db'
3
4export async function GET() {
5 try {
6 // Check database connection
7 await prisma.$queryRaw`SELECT 1`
8
9 return Response.json({
10 status: 'healthy',
11 timestamp: new Date().toISOString(),
12 checks: {
13 database: 'ok'
14 }
15 })
16 } catch (error) {
17 return Response.json(
18 {
19 status: 'unhealthy',
20 error: 'Database connection failed',
21 timestamp: new Date().toISOString()
22 },
23 { status: 503 }
24 )
25 }
26}Prisma in Docker#
1# Generate Prisma client during build
2FROM base AS builder
3WORKDIR /app
4
5# Copy Prisma schema first
6COPY prisma ./prisma/
7
8# Generate Prisma client
9RUN npx prisma generate
10
11# Copy dependencies and source
12COPY /app/node_modules ./node_modules
13COPY . .
14
15RUN npm run buildDatabase Migration Script#
1#!/bin/bash
2# scripts/docker-migrate.sh
3
4set -e
5
6echo "Waiting for database..."
7until pg_isready -h db -U postgres; do
8 sleep 1
9done
10
11echo "Running migrations..."
12npx prisma migrate deploy
13
14echo "Migrations complete!"1# docker-compose.yml - Add migration service
2services:
3 migrate:
4 build:
5 context: .
6 dockerfile: Dockerfile
7 environment:
8 - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
9 depends_on:
10 db:
11 condition: service_healthy
12 command: npx prisma migrate deploy
13 restart: on-failureDocker Build Arguments#
1# Dockerfile with build args
2ARG NODE_VERSION=20
3FROM node:${NODE_VERSION}-alpine AS base
4
5ARG NEXT_PUBLIC_API_URL
6ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
7
8# ... rest of Dockerfile# Build with args
docker build \
--build-arg NEXT_PUBLIC_API_URL=https://api.example.com \
-t myapp:latest .Multi-Architecture Build#
# Build for multiple platforms
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myapp:latest \
--push .Usage Instructions#
- Create the Dockerfile with multi-stage build
- Configure
next.config.jswithoutput: 'standalone' - Create docker-compose.yml for your services
- Add health check endpoints
- Set up database migrations
Best Practices#
- Multi-stage builds - Minimize final image size
- Non-root user - Run as non-privileged user
- Health checks - Enable container orchestration
- .dockerignore - Exclude unnecessary files
- Layer caching - Order commands for cache efficiency
- Environment variables - Never bake secrets into images
- Standalone output - Use Next.js standalone mode
Related Patterns#
- CI/CD - Automated deployments
- Environments - Environment management
- Monitoring - Container monitoring
- Database Migrations - Migration strategies