Dockerfile
Overview¶
This document provides comprehensive multi-stage Dockerfile templates for building optimized, secure container images across all languages and frameworks. Multi-stage builds reduce image size, improve security, and enable better layer caching.
Python Application¶
## Multi-stage build for Python application
FROM python:3.12-slim AS builder
## Install build dependencies
RUN apt-get update && apt-get install -y \
gcc \
g++ \
make \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
## Set working directory
WORKDIR /app
## Copy dependency files
COPY requirements.txt requirements-prod.txt ./
## Install dependencies
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements-prod.txt
## Production stage
FROM python:3.12-slim AS production
## Install runtime dependencies only
RUN apt-get update && apt-get install -y \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
## Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
## Set working directory
WORKDIR /app
## Copy installed packages from builder
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
## Copy application code
COPY --chown=appuser:appuser . .
## Switch to non-root user
USER appuser
## Expose application port
EXPOSE 8000
## Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
## Run application
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Node.js / TypeScript Application¶
## Multi-stage build for Node.js/TypeScript application
FROM node:20-alpine AS builder
## Set working directory
WORKDIR /app
## Copy package files
COPY package*.json ./
## Install all dependencies (including dev dependencies)
RUN npm ci
## Copy source code
COPY . .
## Build TypeScript application
RUN npm run build
## Prune dev dependencies
RUN npm prune --production
## Production stage
FROM node:20-alpine AS production
## Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
## Create non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
## Set working directory
WORKDIR /app
## Copy built application and production dependencies
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
## Switch to non-root user
USER nodejs
## Expose application port
EXPOSE 3000
## Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
## Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]
## Run application
CMD ["node", "dist/index.js"]
Go Application¶
## Multi-stage build for Go application
FROM golang:1.24-alpine AS builder
## Install build dependencies
RUN apk add --no-cache git ca-certificates
## Set working directory
WORKDIR /app
## Copy go mod files
COPY go.mod go.sum ./
## Download dependencies
RUN go mod download
## Copy source code
COPY . .
## Build application with optimizations
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags='-w -s -extldflags "-static"' \
-a \
-o /app/server \
./cmd/server
## Production stage - minimal image
FROM scratch AS production
## Copy CA certificates from builder
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
## Copy binary from builder
COPY --from=builder /app/server /server
## Expose application port
EXPOSE 8080
## Health check (note: scratch doesn't have shell, so limited options)
## For health checks, consider using a sidecar or external monitoring
## Run application
ENTRYPOINT ["/server"]
React / Next.js Application¶
## Multi-stage build for React/Next.js application
FROM node:20-alpine AS dependencies
## Set working directory
WORKDIR /app
## Copy package files
COPY package*.json ./
## Install dependencies
RUN npm ci
## Builder stage
FROM node:20-alpine AS builder
WORKDIR /app
## Copy dependencies from previous stage
COPY --from=dependencies /app/node_modules ./node_modules
## Copy application code
COPY . .
## Build application
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
## Production stage
FROM node:20-alpine AS production
## Install dumb-init
RUN apk add --no-cache dumb-init
## Create non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
WORKDIR /app
## Copy necessary files
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
## Set ownership
RUN chown -R nextjs:nodejs /app
## Switch to non-root user
USER nextjs
## Expose port
EXPOSE 3000
ENV PORT 3000
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
## Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
## Run application
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]
Django Application¶
## Multi-stage build for Django application
FROM python:3.12-slim AS builder
## Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
## Install build dependencies
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
## Copy dependency files
COPY requirements.txt ./
## Install Python dependencies
RUN pip install --upgrade pip && \
pip install -r requirements.txt
## Production stage
FROM python:3.12-slim AS production
## Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DJANGO_SETTINGS_MODULE=config.settings.production
## Install runtime dependencies
RUN apt-get update && apt-get install -y \
postgresql-client \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
## Create non-root user
RUN groupadd -r django && useradd -r -g django django
WORKDIR /app
## Copy installed packages
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
## Copy application code
COPY --chown=django:django . .
## Collect static files
RUN python manage.py collectstatic --noinput
## Switch to non-root user
USER django
## Expose port
EXPOSE 8000
## Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health/')"
## Run application with gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "config.wsgi:application"]
Nginx Static Site¶
## Multi-stage build for static site
FROM node:20-alpine AS builder
WORKDIR /app
## Copy package files
COPY package*.json ./
## Install dependencies
RUN npm ci
## Copy source
COPY . .
## Build static site
RUN npm run build
## Production stage with nginx
FROM nginx:1.25-alpine AS production
## Copy custom nginx config
COPY nginx.conf /etc/nginx/nginx.conf
## Copy built static files
COPY --from=builder /app/dist /usr/share/nginx/html
## Create non-root user
RUN addgroup -g 101 -S nginx && \
adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx
## Set ownership
RUN chown -R nginx:nginx /usr/share/nginx/html && \
chown -R nginx:nginx /var/cache/nginx && \
chown -R nginx:nginx /var/log/nginx && \
chown -R nginx:nginx /etc/nginx/conf.d
## Make nginx run as non-root
RUN touch /var/run/nginx.pid && \
chown -R nginx:nginx /var/run/nginx.pid
## Switch to non-root user
USER nginx
## Expose port
EXPOSE 8080
## Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1
## Run nginx
CMD ["nginx", "-g", "daemon off;"]
Development vs Production¶
Development Dockerfile¶
## Development Dockerfile with hot reload
FROM node:20-alpine
WORKDIR /app
## Install dependencies
COPY package*.json ./
RUN npm install
## Copy source (will be overridden by volume mount)
COPY . .
## Expose port for development server
EXPOSE 3000
## Enable hot reload
CMD ["npm", "run", "dev"]
Production Dockerfile¶
## Production Dockerfile (optimized)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
FROM node:20-alpine AS production
RUN apk add --no-cache dumb-init && \
addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
USER nodejs
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/index.js"]
Best Practices¶
Security¶
## 1. Use specific version tags
FROM node:20.10.0-alpine # Not :latest
## 2. Run as non-root user
RUN addgroup -g 1001 appgroup && adduser -S appuser -u 1001 -G appgroup
USER appuser
## 3. Scan for vulnerabilities
## Use tools like Trivy, Snyk, or Clair
## 4. Use minimal base images
FROM alpine:3.19 # Or scratch for Go apps
## 5. Don't include secrets
## Use build args or secret mounts instead
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm install
Optimization¶
## 1. Leverage layer caching - copy dependencies first
COPY package*.json ./
RUN npm ci
COPY . . # This changes more frequently
## 2. Use .dockerignore
## Create .dockerignore with:
## node_modules
## .git
## *.md
## 3. Multi-stage builds to reduce image size
FROM builder AS production # Only copy what's needed
## 4. Combine RUN commands to reduce layers
RUN apt-get update && \
apt-get install -y pkg1 pkg2 && \
rm -rf /var/lib/apt/lists/*
## 5. Use --no-cache-dir for pip
RUN pip install --no-cache-dir -r requirements.txt
Health Checks¶
## HTTP health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
## Python health check (no curl)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
## Node.js health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
Common Patterns¶
Using Build Arguments¶
ARG PYTHON_VERSION=3.12
FROM python:${PYTHON_VERSION}-slim
ARG BUILD_DATE
ARG VERSION
ARG REVISION
LABEL org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.revision="${REVISION}"
Using Secrets During Build¶
## Mount secrets without storing in layers
RUN --mount=type=secret,id=pip_config \
pip config set global.index-url $(cat /run/secrets/pip_config) && \
pip install -r requirements.txt
Conditional Stages¶
## Base stage
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
## Development stage
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
## Production stage
FROM base AS production
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]
## Build with: docker build --target production -t myapp:prod .
.dockerignore Template¶
## Version control
.git
.gitignore
.gitattributes
## CI/CD
.github
.gitlab-ci.yml
.travis.yml
## Dependencies
node_modules
venv
.venv
## Build artifacts
dist
build
target
*.pyc
__pycache__
## IDE
.vscode
.idea
*.swp
*.swo
## OS
.DS_Store
Thumbs.db
## Logs
*.log
logs
## Documentation
*.md
docs
## Tests
tests
__tests__
*.test.js
*.spec.js
## Environment files
.env
.env.local
.env.*.local
## Docker
Dockerfile*
docker-compose*.yml
.dockerignore
Advanced Patterns¶
Multi-Architecture Build¶
## Build for multiple architectures
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -o /app/server ./cmd/server
FROM alpine:3.19
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
## Build with: docker buildx build --platform linux/amd64,linux/arm64 -t myapp .
Using Cache Mounts¶
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
## Use cache mount for go modules
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .
## Use cache mount for build cache
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 go build -o /app/server ./cmd/server
References¶
Official Documentation¶
Security¶
Tools¶
- Hadolint - Dockerfile linter
- Dive - Image layer analysis
- Trivy - Vulnerability scanner
- Docker Slim - Image optimizer
Status: Active