Comprehensive toolkit for validating, linting, and securing Dockerfiles. Use this skill when validating Dockerfile syntax, checking security best practices, optimizing image builds. Applies to all Dockerfile variants (Dockerfile, Dockerfile.prod, Dockerfile.dev, etc.).
Overall
score
93%
Does it follow best practices?
Validation for skill structure
Comprehensive guide for optimizing Docker images for size, build time, and runtime performance.
Size Comparison:
ubuntu:22.04 ~80 MB
alpine:3.21 ~5 MB
distroless/base ~20 MB
scratch ~0 MB (empty)When to use each:
Alpine - General purpose minimal Linux
FROM alpine:3.21
RUN apk add --no-cache python3Distroless - Production containers
FROM gcr.io/distroless/python3
COPY --from=builder /app /appScratch - Static binaries only
FROM scratch
COPY --from=builder /app/binary /Problem: Build tools bloat production images
Single-stage (bloated):
FROM golang:1.21
WORKDIR /app
COPY . .
RUN go build -o server
CMD ["./server"]
# Result: ~1 GB (includes Go toolchain)Multi-stage (optimized):
# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o server
# Production stage
FROM alpine:3.21
COPY --from=builder /app/server /server
CMD ["/server"]
# Result: ~10 MB (100x smaller!)Combine RUN commands:
# Bad - 4 layers, poor caching
RUN apt-get update
RUN apt-get install -y curl
RUN curl -O https://example.com/file
RUN rm -f file
# Good - 1 layer, cache cleaned
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& curl -O https://example.com/file \
&& rm -rf /var/lib/apt/lists/*APT (Debian/Ubuntu):
RUN apt-get update && apt-get install -y --no-install-recommends \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*APK (Alpine):
RUN apk add --no-cache package1 package2apk add package && rm -rf /var/cache/apk/*YUM/DNF (RHEL/Fedora):
RUN yum install -y package \
&& yum clean all \
&& rm -rf /var/cache/yumPip (Python):
RUN pip install --no-cache-dir packageNPM (Node.js):
RUN npm ci --only=production
# Or with cache mount:
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=productionProblem: Entire project copied into image
.dockerignore contents:
.git/
node_modules/
*.log
.env
tests/
docs/
README.mdImpact:
Order matters - least to most frequently changing:
# 1. Base image (rarely changes)
FROM node:21-alpine
# 2. System dependencies (rarely change)
RUN apk add --no-cache curl
# 3. Application dependencies (change occasionally)
COPY package*.json ./
RUN npm ci
# 4. Application code (changes frequently)
COPY . .
RUN npm run buildWhy this works:
Enable BuildKit:
export DOCKER_BUILDKIT=1Use cache mounts:
# syntax=docker/dockerfile:1
# Python with pip cache
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# Node.js with npm cache
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Go with module cache
RUN --mount=type=cache,target=/go/pkg/mod \
go build -o appBenefits:
# These stages run in parallel
FROM alpine AS fetch-1
RUN wget https://example.com/file1
FROM alpine AS fetch-2
RUN wget https://example.com/file2
# This stage waits for both
FROM alpine
COPY --from=fetch-1 /file1 .
COPY --from=fetch-2 /file2 .# Bad - shell form (extra shell process)
CMD python app.py
# Good - exec form (direct execution)
CMD ["python", "app.py"]Benefits:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD curl -f http://localhost:8080/health || exit 1Benefits:
# Use all available CPUs
ENV GOMAXPROCS=0
# Or limit to specific count
ENV GOMAXPROCS=4FROM node:21-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:21-alpine
COPY --from=builder /app/node_modules ./node_modules
COPY . .
USER node
CMD ["node", "server.js"]Tips:
npm ci instead of npm installFROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.12-slim
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
USER nobody
CMD ["python", "app.py"]Tips:
FROM golang:1.21-alpine AS builder
WORKDIR /src
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
ENTRYPOINT ["/app"]Tips:
-ldflags="-s -w" to strip debug info (smaller binary)FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package
FROM eclipse-temurin:21-jre-alpine
COPY --from=builder /app/target/*.jar /app.jar
CMD ["java", "-jar", "/app.jar"]Tips:
docker buildx build --platform linux/amd64,linux/arm64 -t myapp .# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm cidocker build --secret id=npmrc,src=$HOME/.npmrc .Benefits:
RUN --mount=type=ssh \
git clone git@github.com:private/repo.gitdocker build --ssh default .docker build --squash -t myapp .Benefits:
Drawbacks:
docker images myapp
# REPOSITORY TAG SIZE
# myapp latest 1.2GBdocker images myapp-optimized
# REPOSITORY TAG SIZE
# myapp-optimized latest 50MBtime docker build -t myapp .
# real 5m30s
time docker build -t myapp-optimized .
# real 0m45s (with cache)dive myapp:latestdocker history myapp:latestdocker scout cves myapp:latestInstall with Tessl CLI
npx tessl i pantheon-ai/dockerfile-validator@0.1.0