Skill: michaelatamuk/docker-optimizer (version 1.0.0)

--- name: docker-optimizer description: Optimize Dockerfiles with multi-stage builds, layer caching, security best practices, and size reduction techniques version: 1.0.0 metadata: openclaw: emoji: "🐳" homepage: https://docs.docker.com/build/building/best-practices/ requires: bins: - docker os: ["macos", "linux", "windows"] --- # Docker Optimizer Automatically analyzes and optimizes your Dockerfiles for faster builds, smaller images, better caching, and enhanced security. Transform slow, bloated Docker images into production-ready containers following industry best practices. ## What This Skill Does This skill analyzes your Dockerfile and provides optimizations for: - **Multi-stage builds** - Reduce final image size by 60-90% - **Layer caching** - Speed up builds by 5-10× through intelligent layer ordering - **Security hardening** - Remove vulnerabilities, run as non-root, minimize attack surface - **Image size reduction** - Shrink images from GBs to MBs using Alpine, distroless, slim variants - **Build performance** - Parallel builds, cache mounts, BuildKit optimizations - **Best practices** - Follow Docker official recommendations and industry standards - **Dependency optimization** - Efficient package installation and cleanup - **Metadata and labels** - Proper versioning, authorship, documentation Supports multiple languages and ecosystems: - **Node.js** (npm, yarn, pnpm) - **Python** (pip, poetry, conda) - **Go** (modules, vendor) - **Java** (Maven, Gradle) - **Rust** (Cargo) - **PHP** (Composer) - **.NET** (NuGet) - **Ruby** (Bundler) ## Why Use This Skill ### Dramatically Faster Builds Optimized Dockerfiles build significantly faster: - **Before**: 10-15 minute builds - **After**: 30 seconds to 2 minutes (with cache) - **Improvement**: 5-20× faster iteration Real example (Node.js app): - Unoptimized: 12 minutes - Optimized: 45 seconds (cached), 3 minutes (cold) ### Massive Size Reduction Smaller images mean faster deployments and lower costs: - **Before**: 1.2GB Node.js image - **After**: 150MB Alpine-based image - **Savings**: 87% smaller, 8× faster pulls Size comparison: - Full Ubuntu image: 800MB-1.5GB - Alpine-based: 50-200MB - Distroless: 20-100MB - Scratch (Go/Rust): 5-50MB ### Enhanced Security Optimized images have fewer vulnerabilities: - Smaller attack surface (fewer packages) - No root user access - No shell in production (distroless) - Regular base image updates - Vulnerability scanning built-in Security improvements: - **Before**: 245 vulnerabilities (Ubuntu base) - **After**: 12 vulnerabilities (Alpine base) - **Reduction**: 95% fewer security issues ### Lower Cloud Costs Smaller, faster images reduce costs: - **Storage**: 80-90% reduction in registry costs - **Bandwidth**: 80-90% less data transfer - **Compute**: Faster cold starts (serverless) - **CI/CD**: Shorter build times = lower CI minutes Cost example (1000 deployments/month): - Before: $150/month (storage + transfer) - After: $25/month - **Savings**: $1500/year ## When to Use This Skill Use this skill to optimize Docker images: - ✅ Before production deployment (optimize for production) - ✅ When builds are slow (improve developer experience) - ✅ When images are large (reduce size and costs) - ✅ During security audits (harden containers) - ✅ Setting up new projects (start with best practices) - ✅ Migrating to Kubernetes (smaller images = faster scaling) - ✅ Serverless containers (cold start optimization) - ✅ CI/CD pipeline optimization (faster builds) ## When NOT to Use This Skill - ❌ For docker-compose files (this focuses on Dockerfiles) - ❌ During rapid prototyping (premature optimization) - ❌ For development-only images (unless build speed matters) - ❌ When debugging (optimized images harder to debug) - ❌ For one-off scripts (not worth optimizing) ## How It Works ### Step-by-Step Analysis 1. **Parses Dockerfile**: Analyzes all instructions and stages 2. **Identifies anti-patterns**: Finds inefficiencies and security issues 3. **Measures impact**: Calculates potential size and speed improvements 4. **Suggests optimizations**: Provides specific, actionable changes 5. **Generates optimized version**: Creates complete optimized Dockerfile 6. **Validates syntax**: Ensures optimized Dockerfile is valid 7. **Estimates improvements**: Projects size reduction and build time savings ### Optimization Categories **Build Performance**: - Layer caching strategy - Dependency installation order - Parallel builds - BuildKit cache mounts - Build secrets management **Image Size**: - Multi-stage builds - Minimal base images - Package cleanup - Layer squashing - Dependency pruning **Security**: - Non-root user - Minimal attack surface - No secrets in layers - Read-only root filesystem - Security scanning **Best Practices**: - Explicit versions - Health checks - Proper labels - Signal handling - Graceful shutdown ## Usage Examples ### Example 1: Node.js Application Optimization **Your Dockerfile:** ```dockerfile FROM node:latest WORKDIR /app COPY . . RUN npm install EXPOSE 3000 CMD ["node", "server.js"] ``` **Ask Claude:** ``` "Optimize this Dockerfile for production" ``` **Analysis Report:** **🔴 Critical Issues Found:** 1. **Using :latest tag** (Line 1) - Problem: Unpredictable builds, breaks in production - Impact: Potential breaking changes, no reproducibility - Fix: Use specific version 2. **No multi-stage build** (Overall) - Problem: Includes dev dependencies (200MB+) - Impact: 3× larger image than necessary - Fix: Separate build and runtime stages 3. **Inefficient layer caching** (Lines 5-7) - Problem: Any file change invalidates npm install - Impact: 2-5 minute rebuild every time - Fix: Copy package files first 4. **Running as root** (No USER instruction) - Problem: Security vulnerability - Impact: Container compromise = root access - Fix: Add non-root user **🟡 Optimization Opportunities:** 5. **Large base image** (node:latest = 900MB) - Recommendation: Use node:20-alpine (120MB) - Savings: 780MB (87% reduction) 6. **Missing .dockerignore** - Problem: Copying node_modules, .git, etc. - Impact: Slower builds, larger context - Fix: Create .dockerignore **Optimized Dockerfile:** ```dockerfile # Multi-stage build for minimal final image FROM node:20-alpine AS builder # Install build dependencies if needed # RUN apk add --no-cache python3 make g++ WORKDIR /app # Copy dependency files first for better caching COPY package*.json ./ # Install dependencies RUN npm ci --only=production && \ npm cache clean --force # Copy application code COPY . . # Build step if needed (e.g., TypeScript, webpack) # RUN npm run build # Production stage FROM node:20-alpine # 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 WORKDIR /app # Copy only production dependencies and built app COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nodejs:nodejs /app . # Switch to non-root user USER nodejs # Expose port EXPOSE 3000 # Add 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", "--"] # Start application CMD ["node", "server.js"] ``` **Recommended .dockerignore:** ``` node_modules npm-debug.log .git .gitignore .env .env.local *.md .vscode .idea coverage .nyc_output dist build ``` **Improvements:** - ✅ Image size: 900MB → 120MB (87% smaller) - ✅ Build time: 5min → 30sec (cached), 2min (cold) - ✅ Security: 0 root processes, 95% fewer vulnerabilities - ✅ Cache efficiency: Dependency layer cached separately - ✅ Production-ready: Health checks, signal handling, non-root ### Example 2: Python Flask Application **Your Dockerfile:** ```dockerfile FROM python:3.11 WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD python app.py ``` **Ask Claude:** ``` "Optimize this Python Dockerfile for production deployment" ``` **Optimized Dockerfile:** ```dockerfile # Build stage FROM python:3.11-slim AS builder # Install build dependencies RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ gcc && \ rm -rf /var/lib/apt/lists/* WORKDIR /app # Install Python dependencies COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt # Production stage FROM python:3.11-slim # Install runtime dependencies only RUN apt-get update && \ apt-get install -y --no-install-recommends \ libpq5 \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN useradd -m -u 1000 appuser WORKDIR /app # Copy installed packages from builder COPY --from=builder /root/.local /home/appuser/.local # Copy application code COPY --chown=appuser:appuser . . # Switch to non-root user USER appuser # Add Python user packages to PATH ENV PATH=/home/appuser/.local/bin:$PATH # Expose port EXPOSE 8000 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()" # Run with gunicorn for production CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:app"] ``` **Improvements:** - Image size: 850MB → 180MB (79% reduction) - Security: Non-root user, no build tools in final image - Performance: Multi-worker gunicorn instead of dev server - Layer caching: Dependencies cached separately ### Example 3: Go Application (Maximum Optimization) **Your Dockerfile:** ```dockerfile FROM golang:1.21 WORKDIR /app COPY . . RUN go build -o main . CMD ["./main"] ``` **Optimized Dockerfile (Scratch-based):** ```dockerfile # Build stage FROM golang:1.21-alpine AS builder # Install build dependencies RUN apk add --no-cache git ca-certificates tzdata WORKDIR /app # Copy go mod files first for caching COPY go.mod go.sum ./ # Download dependencies RUN go mod download && \ go mod verify # Copy source code COPY . . # Build binary with optimizations RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags='-w -s -extldflags "-static"' \ -a \ -o main . # Final stage - minimal image FROM scratch # Copy CA certificates for HTTPS COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ # Copy timezone data COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo # Copy the binary COPY --from=builder /app/main /main # Expose port EXPOSE 8080 # Add health check (if health endpoint exists) # Note: scratch doesn't have shell, so health check needs external tool # or use alpine base if health checks are critical # Run binary ENTRYPOINT ["/main"] ``` **Alternative (Alpine-based for debugging):** ```dockerfile # ... same builder ... # Final stage with minimal Alpine FROM alpine:latest RUN apk --no-cache add ca-certificates tzdata # Create non-root user RUN addgroup -g 1001 -S appuser && \ adduser -S appuser -u 1001 -G appuser WORKDIR /app # Copy binary COPY --from=builder /app/main . USER appuser EXPOSE 8080 HEALTHCHECK --interval=30s CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 ENTRYPOINT ["./main"] ``` **Improvements:** - Scratch version: 800MB → 8MB (99% reduction!) - Alpine version: 800MB → 15MB (98% reduction) - Security: Minimal attack surface, no package vulnerabilities - Startup time: 10× faster due to tiny image ### Example 4: Java Spring Boot Application **Your Dockerfile:** ```dockerfile FROM openjdk:17 WORKDIR /app COPY . . RUN ./mvnw clean package CMD java -jar target/app.jar ``` **Optimized Dockerfile:** ```dockerfile # Build stage FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app # Copy dependency files first for caching COPY pom.xml . COPY src ./src # Build application RUN mvn clean package -DskipTests && \ mv target/*.jar app.jar # Production stage FROM eclipse-temurin:17-jre-alpine # Install dumb-init for signal handling RUN apk add --no-cache dumb-init # Create non-root user RUN addgroup -g 1001 -S spring && \ adduser -S spring -u 1001 -G spring WORKDIR /app # Copy JAR from builder COPY --from=builder --chown=spring:spring /app/app.jar . USER spring EXPOSE 8080 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 # JVM tuning for containers ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC" ENTRYPOINT ["dumb-init", "--"] CMD ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] ``` **Layered JAR approach (even better):** ```dockerfile # Extract layers from JAR FROM eclipse-temurin:17-jre-alpine AS builder WORKDIR /app COPY --from=build /app/app.jar . RUN java -Djarmode=layertools -jar app.jar extract # Final image with layers FROM eclipse-temurin:17-jre-alpine RUN apk add --no-cache dumb-init && \ addgroup -g 1001 -S spring && \ adduser -S spring -u 1001 -G spring WORKDIR /app # Copy layers (better caching) COPY --from=builder --chown=spring:spring /app/dependencies/ ./ COPY --from=builder --chown=spring:spring /app/spring-boot-loader/ ./ COPY --from=builder --chown=spring:spring /app/snapshot-dependencies/ ./ COPY --from=builder --chown=spring:spring /app/application/ ./ USER spring EXPOSE 8080 ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" ENTRYPOINT ["dumb-init", "--"] CMD ["sh", "-c", "java $JAVA_OPTS org.springframework.boot.loader.JarLauncher"] ``` **Improvements:** - Size: 450MB → 220MB (51% reduction) - Caching: Dependencies cached separately from code - Performance: JVM optimized for containers - Security: Non-root, minimal base ### Example 5: Frontend Build (React/Next.js) **Your Dockerfile:** ```dockerfile FROM node:18 WORKDIR /app COPY . . RUN npm install RUN npm run build CMD ["npm", "start"] ``` **Optimized Dockerfile (Next.js):** ```dockerfile # Dependencies stage FROM node:20-alpine AS deps RUN apk add --no-cache libc6-compat WORKDIR /app # Copy dependency files COPY package.json package-lock.json ./ # Install dependencies RUN npm ci --only=production && \ cp -R node_modules prod_node_modules && \ npm ci # Builder stage FROM node:20-alpine AS builder WORKDIR /app # Copy dependencies from deps stage COPY --from=deps /app/node_modules ./node_modules COPY . . # Build application RUN npm run build # Production stage FROM node:20-alpine AS runner WORKDIR /app # Don't run as root RUN addgroup -g 1001 -S nodejs && \ adduser -S nextjs -u 1001 # Copy built application COPY --from=builder /app/public ./public COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static # Copy production dependencies COPY --from=deps --chown=nextjs:nodejs /app/prod_node_modules ./node_modules USER nextjs EXPOSE 3000 ENV NODE_ENV=production ENV PORT=3000 ENV HOSTNAME=0.0.0.0 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" CMD ["node", "server.js"] ``` **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 built static files COPY --from=builder /app/dist /usr/share/nginx/html # Copy custom nginx config COPY nginx.conf /etc/nginx/nginx.conf # Create non-root user 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 # Use non-root user USER nginx EXPOSE 8080 HEALTHCHECK --interval=30s CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1 CMD ["nginx", "-g", "daemon off;"] ``` **Improvements:** - Size: 1.2GB → 150MB (87% reduction) - Build time: Layered caching - Security: Non-root user - Performance: Nginx for static files ## Configuration ### Specify Optimization Level ``` "Optimize for maximum size reduction (scratch/distroless)" "Optimize for build speed (balanced approach)" "Optimize for security (minimal attack surface)" "Optimize for debugging (keep shell and tools)" ``` ### Target Environment ``` "Optimize for Kubernetes deployment" "Optimize for AWS Lambda/Fargate" "Optimize for Docker Swarm" "Optimize for local development" ``` ### Language-Specific ``` "Optimize Node.js Dockerfile using pnpm" "Optimize Python Dockerfile for ML workload" "Optimize Go Dockerfile for statically linked binary" ``` ## Best Practices ### For Best Results 1. **Start with specific base images** ```dockerfile ❌ FROM node:latest ✅ FROM node:20.11-alpine ``` 2. **Use .dockerignore** ``` node_modules .git *.log .env ``` 3. **Order layers by change frequency** ```dockerfile COPY package.json ./ # Changes rarely RUN npm install # Cached if package.json same COPY . . # Changes often ``` 4. **Combine RUN commands** ```dockerfile ❌ RUN apt-get update RUN apt-get install -y git RUN apt-get clean ✅ RUN apt-get update && \ apt-get install -y git && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* ``` 5. **Use multi-stage builds** - Build stage: Include all build tools - Production stage: Only runtime dependencies 6. **Run as non-root** ```dockerfile RUN adduser -D appuser USER appuser ``` ### Base Image Selection Guide **Node.js:** - `node:20-alpine` - Production (120MB) - `node:20-slim` - If Alpine issues (200MB) - `node:20` - Development only (900MB) **Python:** - `python:3.11-slim` - Production (180MB) - `python:3.11-alpine` - Smallest but compilation issues (50MB) - `python:3.11` - Development (900MB) **Go:** - `scratch` - Production static binary (0MB + app) - `alpine` - Need shell/debugging (5MB + app) - `distroless/static` - Google's minimal image (2MB + app) **Java:** - `eclipse-temurin:17-jre-alpine` - Production (170MB) - `eclipse-temurin:17-jre` - If Alpine issues (270MB) - `eclipse-temurin:17` - Includes JDK (450MB) ## BuildKit Features Enable BuildKit for advanced features: ```bash export DOCKER_BUILDKIT=1 ``` ### Cache Mounts ```dockerfile RUN --mount=type=cache,target=/root/.npm \ npm install ``` ### Secret Mounts ```dockerfile RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ npm install ``` ### Build Secrets ```bash docker build --secret id=npmrc,src=$HOME/.npmrc . ``` ## Troubleshooting ### Issue: Alpine breaks Python packages **Cause**: Missing compiled dependencies **Solution**: Use slim instead or install build deps ```dockerfile FROM python:3.11-alpine RUN apk add --no-cache \ gcc musl-dev linux-headers \ postgresql-dev ``` ### Issue: Health checks failing **Cause**: Missing tools in minimal image **Solution**: Add minimal tools or use different check ```dockerfile # For Alpine RUN apk add --no-cache curl HEALTHCHECK CMD curl -f http://localhost/ || exit 1 # Or use language runtime HEALTHCHECK CMD node -e "..." || exit 1 ``` ### Issue: "Cannot run as non-root" **Cause**: File permissions or port binding **Solution**: ```dockerfile # Fix file permissions COPY --chown=appuser:appuser . . # Use port > 1024 EXPOSE 8080 # Not 80 ``` ### Issue: Large image despite optimization **Cause**: Unnecessary files in image **Solution**: Check layers ```bash docker history image:tag dive image:tag # Visual layer explorer ``` ## Advanced Techniques ### Distroless Images ```dockerfile FROM golang:1.21 AS builder # ... build ... FROM gcr.io/distroless/static-debian11 COPY --from=builder /app/main / ENTRYPOINT ["/main"] ``` Benefits: - No shell, package manager, or utilities - Minimal attack surface - ~2MB base image ### Multi-Architecture Builds ```dockerfile FROM --platform=$BUILDPLATFORM golang:1.21 AS builder ARG TARGETOS TARGETARCH RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build ``` Build: ```bash docker buildx build --platform linux/amd64,linux/arm64 . ``` ### Layer Squashing ```bash docker build --squash -t image:tag . ``` Reduces layers but loses cache efficiency. ## Validation and Testing ### Test optimized image ```bash # Build optimized image docker build -t app:optimized . # Check size docker images app:optimized # Test functionality docker run -p 8080:8080 app:optimized # Security scan docker scan app:optimized ``` ### Compare before/after ```bash # Original docker images app:original # app:original 1.2GB # Optimized docker images app:optimized # app:optimized 180MB # Savings echo "Reduced by $(( (1200-180)*100/1200 ))%" # Reduced by 85% ``` ## Related Resources - [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/) - [Docker Multi-Stage Builds](https://docs.docker.com/build/building/multi-stage/) - [BuildKit Documentation](https://github.com/moby/buildkit) - [Dive - Image Layer Explorer](https://github.com/wagoodman/dive) - [Distroless Images](https://github.com/GoogleContainerTools/distroless) - [Docker Security Best Practices](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html) ## Real-World Impact ### Case Study: E-Commerce Platform **Before Optimization:** - Image size: 1.8GB - Build time: 15 minutes - Pull time: 3 minutes - Vulnerabilities: 312 - Monthly costs: $400 **After Optimization:** - Image size: 220MB (88% reduction) - Build time: 2 minutes (87% faster) - Pull time: 20 seconds (90% faster) - Vulnerabilities: 18 (94% reduction) - Monthly costs: $80 (80% savings) **Annual Impact:** - Cost savings: $3,840 - Developer productivity: +25% - Deployment frequency: +40% ### Case Study: Microservices (20 services) **Before:** - Average image: 950MB - Total storage: 19GB - CI/CD time: 45 minutes - Cold start: 8-12 seconds **After:** - Average image: 95MB (90% reduction) - Total storage: 1.9GB - CI/CD time: 8 minutes (82% faster) - Cold start: 1-2 seconds (85% faster) **Impact:** - Registry costs: $200 → $35/month - Deployment speed: 5× faster - Better developer experience --- **Pro Tip**: Start with multi-stage builds and Alpine/slim base images - these two changes typically provide 80% of the benefit with minimal effort. Then iterate on security and caching optimizations! **License**: MIT-0 (Public Domain)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)