Claude-initial-setup dockerfile-best-practices

install
source · Clone the upstream repo
git clone https://github.com/VersoXBT/claude-initial-setup
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/VersoXBT/claude-initial-setup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/docker/dockerfile-best-practices" ~/.claude/skills/versoxbt-claude-initial-setup-dockerfile-best-practices && rm -rf "$T"
manifest: skills/docker/dockerfile-best-practices/SKILL.md
source content

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
    latest
    tag
    : Always pin base image versions (
    node:20.11-alpine
    , not
    node:latest
    ). Unpinned tags cause non-reproducible builds.
  • Running as root: Never omit the
    USER
    instruction. 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
    curl
    or
    wget
    explicitly for remote files.
  • Installing dev dependencies in production: Use
    npm ci --omit=dev
    or
    pip install --no-dev
    in 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/*
    after
    apt-get install
    or use
    --no-cache
    with
    apk add
    .

Quick Reference

PracticeDoDon'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 orderdeps before sourcesource before deps
Secrets
--mount=type=secret
COPY .env .
Health
HEALTHCHECK CMD ...
(no health check)
Cache
rm -rf /var/lib/apt/lists/*
(leave cache)