writing-dockerfiles — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited writing-dockerfiles (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Create production-grade Dockerfiles with multi-stage builds, security hardening, and language-specific optimizations.
Invoke when:
Ask three questions to determine the approach:
1. What language?
references/python-dockerfiles.mdreferences/nodejs-dockerfiles.mdreferences/go-dockerfiles.mdreferences/rust-dockerfiles.mdreferences/java-dockerfiles.md2. Is security critical?
references/security-hardening.md)3. Is image size critical?
Separate build environment from runtime environment to minimize final image size.
Pattern:
# Stage 1: Build
FROM build-image AS builder
RUN compile application
# Stage 2: Runtime
FROM minimal-runtime-image
COPY --from=builder /app/binary /app/
CMD ["/app/binary"]Benefits:
Decision matrix:
| Language | Build Stage | Runtime Stage | Final Size |
|---|---|---|---|
| Go (static) | golang:1.22-alpine | gcr.io/distroless/static-debian12 | 10-30MB |
| Rust (static) | rust:1.75-alpine | scratch | 5-15MB |
| Python | python:3.12-slim | python:3.12-slim | 200-400MB |
| Node.js | node:20-alpine | node:20-alpine | 150-300MB |
| Java | maven:3.9-eclipse-temurin-21 | eclipse-temurin:21-jre-alpine | 200-350MB |
Distroless images (Google-maintained):
gcr.io/distroless/static-debian12 → Static binaries (2MB)gcr.io/distroless/base-debian12 → Dynamic binaries with libc (20MB)gcr.io/distroless/python3-debian12 → Python runtime (60MB)gcr.io/distroless/nodejs20-debian12 → Node.js runtime (150MB)See references/base-image-selection.md for complete comparison.
Enable BuildKit for advanced caching and security:
export DOCKER_BUILDKIT=1
docker build .
# OR
docker buildx build .Key features:
--mount=type=cache → Persistent package manager caches--mount=type=secret → Inject secrets without storing in layers--mount=type=ssh → SSH agent forwarding for private reposSee references/buildkit-features.md for detailed patterns.
Order Dockerfile instructions from least to most frequently changing:
# 1. Base image (rarely changes)
FROM python:3.12-slim
# 2. System packages (rarely changes)
RUN apt-get update && apt-get install -y build-essential
# 3. Dependencies manifest (changes occasionally)
COPY requirements.txt .
RUN pip install -r requirements.txt
# 4. Application code (changes frequently)
COPY . .
# 5. Runtime configuration (rarely changes)
CMD ["python", "app.py"]BuildKit cache mounts:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txtCache persists across builds, eliminating redundant downloads.
Essential security practices:
1. Non-root users
# Debian/Ubuntu
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Alpine
RUN adduser -D -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Distroless (built-in)
USER nonroot:nonroot2. Secret management
# ❌ NEVER: Secret in layer history
RUN git clone https://${GITHUB_TOKEN}@github.com/private/repo.git
# ✅ ALWAYS: BuildKit secret mount
RUN --mount=type=secret,id=github_token \
TOKEN=$(cat /run/secrets/github_token) && \
git clone https://${TOKEN}@github.com/private/repo.gitBuild with:
docker buildx build --secret id=github_token,src=./token.txt .3. Vulnerability scanning
# Trivy (recommended)
trivy image myimage:latest
# Docker Scout
docker scout cves myimage:latest4. Health checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1See references/security-hardening.md for comprehensive hardening patterns.
Create .dockerignore to exclude unnecessary files:
# Version control
.git
.gitignore
# CI/CD
.github
.gitlab-ci.yml
# IDE
.vscode
.idea
# Testing
tests/
coverage/
**/*_test.go
**/*.test.js
# Build artifacts
node_modules/
dist/
build/
target/
__pycache__/
# Environment
.env
.env.local
*.logReduces build context size and prevents leaking secrets.
Three approaches:
Example: Poetry multi-stage
FROM python:3.12-slim AS builder
RUN --mount=type=cache,target=/root/.cache/pip \
pip install poetry==1.7.1
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
python -m venv /opt/venv && \
/opt/venv/bin/pip install -r requirements.txt
FROM python:3.12-slim
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
USER 1000:1000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]See references/python-dockerfiles.md for complete patterns and examples/python-fastapi.Dockerfile.
Key patterns:
npm ci (not npm install) for reproducible buildsnode user (UID 1000)Example: Express multi-stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
RUN npm prune --omit=dev
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/index.js"]See references/nodejs-dockerfiles.md for npm/pnpm/yarn patterns and examples/nodejs-express.Dockerfile.
Smallest possible images:
-ldflags="-s -w"/go/pkg/mod and build cacheExample: Distroless static
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/main /app/main
USER nonroot:nonroot
ENTRYPOINT ["/app/main"]See references/go-dockerfiles.md and examples/go-microservice.Dockerfile.
Ultra-small static binaries:
Example: Scratch base
FROM rust:1.75-alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /app
# Cache dependencies
COPY Cargo.toml Cargo.lock ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
mkdir src && echo "fn main() {}" > src/main.rs && \
cargo build --release --target x86_64-unknown-linux-musl && \
rm -rf src
# Build application
COPY src ./src
RUN --mount=type=cache,target=/usr/local/cargo/registry \
cargo build --release --target x86_64-unknown-linux-musl
FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/app /app
USER 1000:1000
ENTRYPOINT ["/app"]See references/rust-dockerfiles.md and examples/rust-actix.Dockerfile.
BuildKit cache mount locations:
| Language | Package Manager | Cache Mount Target |
|---|---|---|
| Python | pip | --mount=type=cache,target=/root/.cache/pip |
| Python | poetry | --mount=type=cache,target=/root/.cache/pypoetry |
| Python | uv | --mount=type=cache,target=/root/.cache/uv |
| Node.js | npm | --mount=type=cache,target=/root/.npm |
| Node.js | pnpm | --mount=type=cache,target=/root/.local/share/pnpm/store |
| Go | go mod | --mount=type=cache,target=/go/pkg/mod |
| Rust | cargo | --mount=type=cache,target=/usr/local/cargo/registry |
Persistent caches eliminate redundant package downloads across builds.
Validate Dockerfile quality:
# Lint Dockerfile
python scripts/validate_dockerfile.py Dockerfile
# Scan for vulnerabilities
trivy image myimage:latest
# Analyze image size
docker images myimage:latest
docker history myimage:latestCompare optimization results:
# Before optimization
docker build -t myapp:before .
# After optimization
docker build -t myapp:after .
# Compare
bash scripts/analyze_image_size.sh myapp:before myapp:afterSee scripts/validate_dockerfile.py for automated Dockerfile linting.
Upstream (provide input):
testing-strategies → Test application before containerizingsecurity-hardening → Application-level security before Docker layerDownstream (consume Dockerfiles):
building-ci-pipelines → Build and push Docker images in CIkubernetes-operations → Deploy containers to K8s clustersinfrastructure-as-code → Deploy containers with Terraform/PulumiParallel (related context):
secret-management → Inject runtime secrets (K8s secrets, vaults)observability → Container logging and metrics collection1. Static binary (Go/Rust) → Smallest image
gcr.io/distroless/static-debian12 or scratch2. Interpreted language (Python/Node.js) → Production-optimized
3. JVM (Java) → Optimized runtime
4. Security-critical → Maximum hardening
5. Development → Fast iteration
❌ Never:
latest tags (unpredictable builds)✅ Always:
python:3.12.1-slim, not python:3)Base image registries:
gcr.io/distroless/*python:*, node:*, golang:*registry.access.redhat.com/ubi9/*Vulnerability scanners:
trivy image myimage:latestdocker scout cves myimage:latestgrype myimage:latestReference documentation:
references/base-image-selection.md → Complete base image comparisonreferences/buildkit-features.md → Advanced BuildKit patternsreferences/security-hardening.md → Comprehensive security guidereferences/ directoryexamples/ directory~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.