# Create Dockerfile Command You are helping the user create an optimized Dockerfile for containerizing their application following Sngular's DevOps best practices. ## Instructions 1. **Detect application type**: - Node.js (Express, Fastify, NestJS, Next.js) - Python (FastAPI, Flask, Django) - Go application - Java/Spring Boot - Static site (React, Vue, etc.) - Multi-service application 2. **Determine build requirements**: - Package manager (npm, yarn, pnpm, pip, go mod, maven, gradle) - Build steps needed - Dependencies to install - Environment variables required - Port to expose 3. **Ask for optimization preferences**: - Multi-stage build (recommended) - Base image preference (alpine, slim, distroless) - Development vs production - Build caching strategy ## Dockerfile Templates ### Node.js Application (Multi-stage) ```dockerfile # syntax=docker/dockerfile:1 # Build stage FROM node:20-alpine AS builder WORKDIR /app # Install dependencies first (better caching) COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force # Copy application code COPY . . # Build application (if needed) RUN npm run build # Production stage FROM node:20-alpine AS production # Security: Create non-root user RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 WORKDIR /app # Copy only necessary files from builder COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist 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))" # Start application CMD ["node", "dist/main.js"] ``` ### Next.js Application ```dockerfile FROM node:20-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app COPY package*.json ./ RUN npm ci FROM node:20-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED 1 RUN npm run build FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV production ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 ENV HOSTNAME "0.0.0.0" CMD ["node", "server.js"] ``` ### Python FastAPI Application ```dockerfile # Build stage FROM python:3.11-slim AS builder WORKDIR /app # Install system dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ && rm -rf /var/lib/apt/lists/* # Install Python dependencies COPY requirements.txt . RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt # Production stage FROM python:3.11-slim WORKDIR /app # Install runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ && rm -rf /var/lib/apt/lists/* # Copy wheels and install COPY --from=builder /app/wheels /wheels COPY requirements.txt . RUN pip install --no-cache /wheels/* # Create non-root user RUN useradd -m -u 1001 appuser # Copy application COPY --chown=appuser:appuser . . USER appuser EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] ``` ### Go Application ```dockerfile # Build stage FROM golang:1.21-alpine AS builder WORKDIR /app # Install build dependencies RUN apk add --no-cache git # Copy go mod files COPY go.mod go.sum ./ RUN go mod download # Copy source code COPY . . # Build binary with optimizations RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-w -s" -o main . # Production stage (distroless for minimal size) FROM gcr.io/distroless/static-debian11 WORKDIR /app # Copy binary from builder COPY --from=builder /app/main . # Use numeric user ID (distroless doesn't have /etc/passwd) USER 65532:65532 EXPOSE 8080 ENTRYPOINT ["/app/main"] ``` ### Static Site (Nginx) ```dockerfile # Build stage FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM nginx:alpine # Copy custom nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf # Copy built files COPY --from=builder /app/dist /usr/share/nginx/html # Add non-root user RUN chown -R nginx:nginx /usr/share/nginx/html && \ chmod -R 755 /usr/share/nginx/html && \ chown -R nginx:nginx /var/cache/nginx && \ chown -R nginx:nginx /var/log/nginx && \ touch /var/run/nginx.pid && \ chown -R nginx:nginx /var/run/nginx.pid USER nginx EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=3s CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1 CMD ["nginx", "-g", "daemon off;"] ``` ## Nginx Configuration for Static Sites ```nginx # nginx.conf server { listen 8080; server_name _; root /usr/share/nginx/html; index index.html; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Gzip compression gzip on; gzip_vary on; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } # SPA routing location / { try_files $uri $uri/ /index.html; } # Health check location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } ``` ## .dockerignore File ``` # .dockerignore node_modules npm-debug.log dist build .git .gitignore .env .env.local .env.*.local README.md .vscode .idea *.log coverage .next .cache __pycache__ *.pyc *.pyo .pytest_cache .mypy_cache target bin obj ``` ## Docker Compose for Development ```yaml # docker-compose.yml version: '3.8' services: app: build: context: . dockerfile: Dockerfile target: development ports: - "3000:3000" volumes: - .:/app - /app/node_modules environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/myapp depends_on: - db - redis db: image: postgres:16-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: postgres POSTGRES_PASSWORD: password volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" redis: image: redis:7-alpine ports: - "6379:6379" volumes: postgres_data: ``` ## Best Practices ### Security - Use specific version tags, not `latest` - Run as non-root user - Use minimal base images (alpine, slim, distroless) - Scan images for vulnerabilities - Don't include secrets in images ### Performance - Use multi-stage builds to reduce image size - Leverage build cache (COPY dependencies first) - Combine RUN commands to reduce layers - Use .dockerignore to exclude unnecessary files ### Optimization ```dockerfile # Bad: Creates multiple layers RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y git # Good: Single layer with cleanup RUN apt-get update && apt-get install -y \ curl \ git \ && rm -rf /var/lib/apt/lists/* ``` ### Health Checks ```dockerfile # Application health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 ``` ### Build Arguments ```dockerfile ARG NODE_VERSION=20 FROM node:${NODE_VERSION}-alpine ARG BUILD_DATE ARG VCS_REF LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.vcs-ref=$VCS_REF ``` ## Building and Running ```bash # Build image docker build -t myapp:latest . # Build with build args docker build --build-arg NODE_VERSION=20 -t myapp:latest . # Run container docker run -p 3000:3000 -e NODE_ENV=production myapp:latest # Run with docker-compose docker-compose up -d # View logs docker logs -f myapp # Execute command in container docker exec -it myapp sh ``` ## Image Size Optimization ```dockerfile # Use smaller base images FROM node:20-alpine # ~110MB # vs FROM node:20 # ~900MB # Use distroless for Go/static binaries FROM gcr.io/distroless/static-debian11 # ~2MB # Multi-stage builds FROM node:20 AS builder # ... build steps FROM node:20-alpine AS production COPY --from=builder /app/dist ./dist ``` Ask the user: "What type of application would you like to containerize?"