Complete dockerfile toolkit with generation and validation capabilities
74
92%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Risky
Do not use without reviewing
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:latest