All skills
akin-ozer avatar

/dockerfile-generator

@1b2342a

Create, generate, or write Dockerfiles and multi-stage Docker images. Containerize apps.

Use this Skill: https://skilld.dev/gh/akin-ozer/cc-devops-skills/dockerfile-generator

This session only. Nothing lands on disk.

referencesoptimization_patterns.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Dockerfile Optimization Patterns

Overview

This guide provides comprehensive optimization techniques for reducing Docker image size, improving build times, and enhancing runtime performance.

Image Size Optimization

Use Multi-Stage Builds

Impact: 50-85% size reduction

# Before: Single stage (500MB)
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/server.js"]

# After: Multi-stage (150MB)
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

Choose Minimal Base Images

Size Comparison:

ubuntu:22.04          → 77MB
node:20               → 996MB
node:20-slim          → 239MB
node:20-alpine        → 132MB
alpine:3.21           → 7.8MB
distroless/static     → 2MB
scratch               → 0MB

Selection Guide:

  • Full OS (ubuntu, debian): When you need many system tools
  • Slim variants: Good balance of size and compatibility
  • Alpine: Minimal size, may have glibc compatibility issues
  • Distroless: Highest security, minimal attack surface
  • Scratch: For static binaries only (Go, Rust)

Remove Unnecessary Files

# Clean up in same RUN layer
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

# Remove build artifacts
RUN npm run build && \
    rm -rf src/ tests/ docs/

# Use .dockerignore
# (prevents files from being sent to build context)

Build Time Optimization

Layer Caching Strategy

Order instructions from least to most frequently changing:

# Bad - Invalidates cache on any code change
COPY . /app
RUN npm install

# Good - Cache dependencies separately
COPY package*.json /app/
RUN npm install
COPY . /app

Optimal Layer Order:

  1. Base image (FROM)
  2. System packages (RUN apt-get)
  3. Application dependencies (package.json, requirements.txt)
  4. Application code (COPY . .)
  5. Build commands (RUN build)
  6. Runtime configuration (CMD, ENTRYPOINT)

BuildKit Cache Mounts

Mount external caches to persist across builds:

# syntax=docker/dockerfile:1

# NPM cache mount
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci

# Go module cache
FROM golang:1.21-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
    go mod download

# Pip cache
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

Enable BuildKit:

export DOCKER_BUILDKIT=1
docker build .

Parallel Stage Execution

# syntax=docker/dockerfile:1

FROM alpine AS fetch-1
RUN wget https://example.com/file1

FROM alpine AS fetch-2
RUN wget https://example.com/file2

FROM alpine AS final
COPY --from=fetch-1 /file1 .
COPY --from=fetch-2 /file2 .

BuildKit automatically parallelizes independent stages.

Layer Optimization

Combine RUN Commands

# Bad - Creates 5 layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*

# Good - Creates 1 layer
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        git \
        vim && \
    rm -rf /var/lib/apt/lists/*

Minimize Layer Count

Each instruction creates a layer:

  • FROM, RUN, COPY, ADD create layers
  • ENV, WORKDIR, EXPOSE, USER do not create significant layers
  • Combine related operations
# Bad - Many layers
COPY package.json .
COPY package-lock.json .
COPY tsconfig.json .
COPY src/ ./src/
COPY tests/ ./tests/

# Good - Fewer layers (use .dockerignore)
COPY . .

Layer Size Analysis

# Inspect layer sizes
docker history myimage:latest

# Find large layers
docker history --no-trunc --format "table {{.Size}}\t{{.CreatedBy}}" myimage:latest | sort -hr | head -10

Dependency Optimization

Install Only Production Dependencies

Node.js:

# Development dependencies excluded
RUN npm ci --only=production

# Or use package.json scripts
RUN npm ci --omit=dev

Python:

# Create separate requirements files
# requirements.txt (production)
# requirements-dev.txt (development)
RUN pip install --no-cache-dir -r requirements.txt

Java (Maven):

# Skip tests during build
RUN ./mvnw clean package -DskipTests

Remove Development Tools

# Multi-stage: Keep build tools in builder stage
FROM golang:1.21 AS builder
RUN apk add --no-cache git make
RUN make build

FROM alpine:3.21
# No build tools in final image
COPY --from=builder /app/binary /app/

.dockerignore Optimization

Reduces build context size and build time:

# Version control
.git
.gitignore

# Dependencies (installed during build)
node_modules
vendor
__pycache__

# IDE files
.vscode
.idea
*.swp

# Build artifacts
dist
build
target

# Documentation
*.md
docs/

# CI/CD
.github
.gitlab-ci.yml

# Environment files
.env
.env.*

# Logs
*.log
logs/

# Tests
tests/
*.test
coverage/

Impact:

  • Smaller build context → Faster upload to Docker daemon
  • Fewer files → Faster COPY operations
  • No accidental secret leaks

Runtime Performance

Use Exec Form for CMD/ENTRYPOINT

# Bad - Shell form (spawns extra sh process)
CMD node server.js

# Good - Exec form (direct process execution)
CMD ["node", "server.js"]

# Benefits:
# - Proper signal handling (SIGTERM, SIGINT)
# - Faster startup
# - Lower memory usage

Optimize Application

# Node.js: Use production mode
ENV NODE_ENV=production

# Python: Use optimized bytecode
ENV PYTHONOPTIMIZE=1

# Java: Set heap size
ENV JAVA_OPTS="-Xms512m -Xmx2048m"

# Go: Build with optimizations
RUN go build -ldflags="-s -w" -o app

Health Checks

# Efficient health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

# Avoid heavy health checks
# Bad: HEALTHCHECK CMD curl http://localhost/full-db-check

Build Optimization Checklist

  • Use multi-stage builds
  • Choose minimal base images (Alpine, distroless)
  • Order layers from least to most frequently changing
  • Combine RUN commands where logical
  • Use BuildKit cache mounts
  • Create comprehensive .dockerignore
  • Install only production dependencies
  • Clean package manager caches in same layer
  • Remove development tools from final image
  • Use exec form for CMD/ENTRYPOINT
  • Optimize application for production
  • Add efficient health checks

Advanced Techniques

Squash Layers (Use with Caution)

# Squash all layers into one (loses layer caching)
docker build --squash -t myapp:latest .

Use Cases:

  • Final production images
  • When layer caching isn't important
  • To hide sensitive information in layers (better: use multi-stage)

Drawbacks:

  • Loses layer caching benefits
  • Larger initial download
  • Less transparent image history

BuildKit Secrets (Zero-Copy Secrets)

# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=aws_credentials \
    aws configure set credentials $(cat /run/secrets/aws_credentials)
docker build --secret id=aws_credentials,src=$HOME/.aws/credentials .

Cross-Platform Builds

# Build for multiple platforms
docker buildx build \
    --platform linux/amd64,linux/arm64 \
    -t myapp:latest \
    .

Measuring Optimization Impact

Before and After Comparison

# Build original
docker build -t myapp:before .
docker images myapp:before

# Build optimized
docker build -t myapp:after .
docker images myapp:after

# Compare sizes
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep myapp

Build Time Measurement

# Measure build time
time docker build -t myapp:latest .

# Measure with cache
docker build -t myapp:cached .

# Measure without cache
docker build --no-cache -t myapp:no-cache .

Dive Tool (Layer Analysis)

# Install dive
brew install dive  # macOS
# or download from https://github.com/wagoodman/dive

# Analyze image
dive myapp:latest

Real-World Examples

Node.js Optimization

Before (996MB):

FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

After (50MB, 95% reduction):

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/server.js ./
CMD ["node", "server.js"]

Go Optimization

Before (800MB):

FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o app
CMD ["./app"]

After (8MB, 99% reduction):

FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o app

FROM scratch
COPY --from=builder /app/app /app
ENTRYPOINT ["/app"]

References

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The Dockerfile Generator skill provides a robust and secure workflow for containerizing applications. It implements industry best practices such as multi-stage builds, non-root users, and automated security validation using tools like hadolint and checkov. The analysis found no malicious patterns, and the skill includes proactive measures to prevent common security pitfalls in Docker configurations.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    10/17 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 1b2342a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 7 months ago

README badge

README badge for akin-ozer/cc-devops-skills/dockerfile-generator