Awesome-omni-skill dockerfile-optimization

Optimize Dockerfiles for smaller images, faster builds, better caching, and security. Use this skill when writing, reviewing, or debugging Dockerfiles.

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/devops/dockerfile-optimization" ~/.claude/skills/diegosouzapw-awesome-omni-skill-dockerfile-optimization && rm -rf "$T"
manifest: skills/devops/dockerfile-optimization/SKILL.md
source content

Dockerfile Optimization

You are a Docker expert. When writing or reviewing Dockerfiles, apply these best practices for size, speed, caching, and security.

Multi-Stage Build Pattern

Always use multi-stage builds for compiled languages:

# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

Go example (even smaller — scratch base):

FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

Layer Caching Rules

Docker caches each layer. When a layer changes, all layers after it are rebuilt.

Maximize Cache Hits

# BAD — any source change invalidates npm install cache
COPY . .
RUN npm ci

# GOOD — only re-install if package.json changes
COPY package*.json ./
RUN npm ci
COPY . .

Order: Least-changing → Most-changing

  1. Base image (rarely changes)
  2. System packages (rarely changes)
  3. Dependency manifests (changes occasionally)
  4. Dependency install (changes with manifests)
  5. Source code (changes frequently)
  6. Build step (changes with source)

Image Size Optimization

Use Alpine or Distroless Base Images

BaseSizeUse When
scratch
0 MBStatic Go binaries
distroless
~2 MBJava, Go, Python without shell
alpine
~7 MBNeed a shell, package manager
slim
~80 MBNeed Debian packages
full
~200+ MBDevelopment only, never production

Reduce Layer Count

# BAD — 3 layers
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# GOOD — 1 layer, cleaned up
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

Use .dockerignore

Create a

.dockerignore
to exclude unnecessary files:

.git
node_modules
dist
*.md
.env*
.vscode
.idea
__pycache__
*.pyc
coverage
.next

Security Best Practices

Run as Non-Root

# Create a non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Or use built-in user
USER node          # Node.js images
USER nobody:nobody # Generic

Pin Versions

# BAD — unpredictable
FROM node:latest
RUN apt-get install python3

# GOOD — reproducible
FROM node:22.12-alpine3.19
RUN apk add --no-cache python3=3.11.6-r0

Don't Store Secrets in Images

# BAD — secret baked into image
ENV API_KEY=sk-secret123
COPY .env .

# GOOD — pass at runtime
# docker run -e API_KEY=sk-secret123 myapp
# or use Docker secrets / mount

Scan for Vulnerabilities

# Docker Scout
docker scout cves myimage:latest

# Trivy
trivy image myimage:latest

# Grype
grype myimage:latest

Common Anti-Patterns

Anti-PatternProblemFix
FROM ubuntu:latest
Large, unpredictableUse
alpine
or
distroless
, pin version
COPY . .
before
npm install
Breaks cachingCopy manifests first, install, then copy source
RUN apt-get update
alone
Stale package list cachedCombine with
install
in one
RUN
USER root
in production
Security riskCreate and switch to non-root user
No
.dockerignore
Bloated context, slow buildsAdd
.dockerignore
with exclusions
ENTRYPOINT
without
exec
form
Signals not forwardedUse
["executable", "arg"]
form
Not cleaning up in same layerLarger imageCombine install + cleanup in one
RUN

Health Checks

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

Debugging Dockerfiles

# Build with progress output
docker build --progress=plain .

# Build up to a specific stage
docker build --target builder .

# Inspect image layers
docker history myimage:latest

# Run shell in failed build
docker run -it --entrypoint /bin/sh myimage:latest

# Check image size breakdown
docker image inspect myimage:latest --format='{{.Size}}'
dive myimage:latest  # Interactive layer explorer