Dockerfile best practices
Skill VersoXBT/claude-initial-setup/skills/docker/dockerfile-best-practices
75 skills, 14 agents, 15 commands for Claude Code. Plug-and-play starter kit covering TypeScript, Python, Go, Rust, React, Next.js, FastAPI, Django, Docker, CI/CD, security, testing, and more. Cross-AI support for Cursor, Copilot, and Codex.
npx -y skills add VersoXBT/claude-initial-setup --skill dockerfile-best-practicesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guide for writing production-ready Dockerfiles with multi-stage builds, layer caching, security hardening, and optimized image sizes. Use when the user creates a Dockerfile, asks about Docker image optimization, mentions container security, or needs help with build performance. Trigger whenever Docker or container packaging is discussed.
SKILL.md
4.7 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it
Dockerfile Best Practices
Write secure, efficient, and maintainable Dockerfiles that produce minimal production images with proper caching, non-root users, and health checks.
When to Use
- User creates or modifies a Dockerfile
- User asks about reducing Docker image size
- User mentions container security or hardening
- User has slow Docker builds or cache invalidation issues
- User asks about COPY vs ADD or layer ordering
Core Patterns
Multi-Stage Builds
Separate build dependencies from runtime to minimize final image size.
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY src/ src/
COPY tsconfig.json ./
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Layer Caching Optimization
Order instructions from least to most frequently changed. Copy dependency manifests before source code so dependency installs are cached across builds.
FROM python:3.12-slim
WORKDIR /app
# System deps change rarely -- cache this layer
RUN apt-get update && \
apt-get install -y --no-install-recommends libpq-dev && \
rm -rf /var/lib/apt/lists/*
# Dependencies change occasionally -- cache this layer
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Source code changes frequently -- last layer
COPY . .
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]
Non-Root User
Never run containers as root in production. Create a dedicated user with minimal permissions.
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 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
USER 65534:65534
ENTRYPOINT ["/server"]
.dockerignore
Always include a .dockerignore to prevent sending unnecessary files to the build context.
.git
.github
node_modules
dist
*.md
.env*
.vscode
.idea
docker-compose*.yml
Dockerfile*
coverage
__pycache__
*.pyc
.pytest_cache
Health Checks
Define health checks in the Dockerfile so orchestrators can monitor container health.
# HTTP health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/healthz || exit 1
# TCP health check (when curl is unavailable)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD nc -z localhost 8080 || exit 1
Anti-Patterns
- Using
latesttag: Always pin base image versions (node:20.11-alpine, notnode:latest). Unpinned tags cause non-reproducible builds. - Running as root: Never omit the
USERinstruction. Root in a container is root on the host if the container escapes. - Using ADD instead of COPY: ADD auto-extracts archives and fetches URLs, which is unexpected. Use COPY for local files; use
curlorwgetexplicitly for remote files. - Installing dev dependencies in production: Use
npm ci --omit=devorpip install --no-devin the final stage. - Single-stage builds: Shipping compilers, build tools, and source code in production images wastes space and expands the attack surface.
- Not cleaning up apt/apk cache: Always add
rm -rf /var/lib/apt/lists/*afterapt-get installor use--no-cachewithapk add.
Quick Reference
| Practice | Do | Don't |
|---|---|---|
| Base image | node:20-alpine | node:latest |
| Copy files | COPY . . | ADD . . |
| User | USER 1001 | (run as root) |
| Install deps | RUN npm ci | RUN npm install |
| Layer order | deps before source | source before deps |
| Secrets | --mount=type=secret | COPY .env . |
| Health | HEALTHCHECK CMD ... | (no health check) |
| Cache | rm -rf /var/lib/apt/lists/* | (leave cache) |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most performance cost skills give in ~1.2k tokens
Counted across 803 of the 1,058 authors here whose files we hold, read 2026-08-07
- Keep skill files under 500 lines or tokensin 82 of 803, across 16 files
- Use imperative form in instructionsin 80 of 803, across 9 files
- Draft assertions while test runs are in progressin 75 of 803, across 9 files
- Create two to three realistic test promptsin 74 of 803, across 9 files
- Write skill descriptions to be pushyin 72 of 803, across 7 files
- Save test cases to evals JSONin 72 of 803, across 6 files
- Ask questions about edge cases and input formatsin 72 of 803, across 7 files
- Save timing data immediately when runs completein 70 of 803, across 5 files
- Include all trigger conditions in the skill descriptionin 69 of 803, across 3 files
- Launch all test runs in a single turn or simultaneouslyin 69 of 803, across 3 files
- Capture intent before writing a skillin 67 of 803, across 1 file
- Import directly instead of barrel filesin 52 of 803, across 15 files
Said here and by no other author read
- create a non-root user
- define a health check
- use COPY instead of ADD
- install production dependencies only
- clean package manager caches after installing
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.