Master Docker multi-stage builds for production-ready containers. Learn layer optimization, caching strategies, security hardening, and minimal image creation for Node.js and Next.js apps.
# Docker Multi-Stage Builds Optimization
Create production-ready Docker images with multi-stage builds. Optimize for size, security, and build speed for Node.js and Next.js applications.
## Multi-Stage Build Fundamentals
### Next.js Production Dockerfile
```dockerfile
# Stage 1: Dependencies
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Copy package files
COPY package.json package-lock.json* ./
COPY prisma ./prisma/
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force
# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
# Copy deps from previous stage
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Set build-time environment variables
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
# Generate Prisma client and build
RUN npx prisma generate && \
npm run build
# Stage 3: Runner
FROM node:20-alpine AS runner
WORKDIR /app
# Security: Create non-root user
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Set production environment
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Copy only necessary files
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
# Set correct permissions
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
CMD ["node", "server.js"]
```
### next.config.js for Standalone
```javascript
// next.config.js
/** @type {import("next").NextConfig} */
const nextConfig = {
output: "standalone",
experimental: {
outputFileTracingRoot: path.join(__dirname, "../../"),
},
};
module.exports = nextConfig;
```
## Layer Optimization
### Efficient Layer Caching
```dockerfile
# Bad: Invalidates cache on any file change
COPY . .
RUN npm ci && npm run build
# Good: Separate dependency and source layers
COPY package*.json ./
RUN npm ci
COPY src ./src
COPY public ./public
RUN npm run build
```
### Reducing Image Size
```dockerfile
# Stage 1: Build with full Node
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
# Stage 2: Minimal runtime with distroless
FROM gcr.io/distroless/nodejs20-debian12 AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["dist/index.js"]
```
## Build Arguments and Secrets
### Secure Build Configuration
```dockerfile
# Dockerfile
FROM node:20-alpine AS builder
# Build args (visible in image history)
ARG NODE_ENV=production
ARG NEXT_PUBLIC_APP_URL
# Secrets (not stored in image layers)
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) \
npm ci
# Environment from build args
ENV NODE_ENV=$NODE_ENV
ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
```
### Docker Compose with Build Args
```yaml
# docker-compose.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
secrets:
- npm_token
ports:
- "3000:3000"
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
secrets:
npm_token:
file: ./.npm_token
```
## Caching with BuildKit
### Advanced Caching
```dockerfile
# syntax=docker/dockerfile:1.4
FROM node:20-alpine AS builder
WORKDIR /app
# Cache mount for npm
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Cache mount for Next.js
COPY . .
RUN --mount=type=cache,target=/app/.next/cache \
npm run build
```
### GitHub Actions with Layer Caching
```yaml
# .github/workflows/docker.yml
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
NEXT_PUBLIC_API_URL=${{ vars.API_URL }}
```
## Security Hardening
### Minimal Base Images
```dockerfile
# Option 1: Alpine (small but has shell)
FROM node:20-alpine AS runner
RUN apk add --no-cache dumb-init
USER node
ENTRYPOINT ["dumb-init", "--"]
# Option 2: Distroless (no shell, most secure)
FROM gcr.io/distroless/nodejs20-debian12
USER nonroot
# Option 3: Chainguard (verified, minimal)
FROM cgr.dev/chainguard/node:latest
```
### Security Scanning
```dockerfile
# Add to CI pipeline
FROM aquasec/trivy:latest AS scanner
COPY --from=builder /app /scan
RUN trivy filesystem --exit-code 1 --severity HIGH,CRITICAL /scan
```
## Development vs Production
```dockerfile
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# docker-compose.dev.yml
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
```
This Docker multi-stage guide provides production-ready patterns for building optimized, secure container images.This docker prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their docker implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For docker projects, consider mentioning your framework version, coding style, and any specific libraries you're using.