Back to skills
SKILL.md
Dockerfile Best Practices
CSecurityCreate and optimize Dockerfiles with BuildKit, multi-stage builds, advanced caching, and security. Use when: (1) Creating new Dockerfile, (2) Optimizing existing Dockerfile, (3) Reducing image size, (4) Improving security, (5) Using Python with uv, (6) Resolving cache or slow build issues, (7) Setting up CI/CD builds
- 666 stars
- 0 votes
- 0 copies
- 0 views
- Added February 8, 2026
Works with
Security analysis
71/100- Accesses sensitive system or user directories
- Sends environment variables or credentials to an external URL
- Installs packages at runtime which could introduce malicious dependencies
npx -y skills add majiayu000/claude-skill-registry --skill dockerfile-best-practices --agent claude-codeAre you the author of Dockerfile Best Practices?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/majiayu000-dockerfile-best-practices)---
name: dockerfile-best-practices
description: "Create and optimize Dockerfiles with BuildKit, multi-stage builds, advanced caching, and security. Use when: (1) Creating new Dockerfile, (2) Optimizing existing Dockerfile, (3) Reducing image size, (4) Improving security, (5) Using Python with uv, (6) Resolving cache or slow build issues, (7) Setting up CI/CD builds"
license: MIT
compatibility: "Claude Code, Claude.ai"
allowed-tools: "Read Grep Glob Bash Write Edit"
metadata:
version: "1.0.1"
category: development
tags:
- docker
- dockerfile
- containers
- buildkit
- optimization
- security
- devops
- ci-cd
triggers:
- "create dockerfile"
- "optimize dockerfile"
- "docker best practices"
- "reduce image size"
- "docker security"
- "multi-stage build"
- "buildkit"
surfaces:
- claude-code
- claude-ai
author:
name: obeone
url: https://github.com/obeone
---
# Dockerfile Best Practices
Comprehensive guide for creating optimized, secure, and fast Docker images using modern BuildKit features.
## Quick Start Workflow
When working with Dockerfiles, follow this decision tree:
1. **Choose your language/framework** → Use appropriate template below
2. **Apply security hardening** → Non-root user, pin versions, secrets management
3. **Optimize for cache** → Separate deps from code, use cache mounts
4. **Multi-stage if needed** → Separate build from runtime for compiled languages
5. **Review with analyzer** → Run `scripts/analyze_dockerfile.py`
## Essential Rules (Always Apply)
1. **Start with BuildKit syntax:**
```dockerfile
# syntax=docker/dockerfile:1
```
2. **Pin runtime versions, not OS versions:**
```dockerfile
# ✅ GOOD - Pin runtime, let OS update
FROM python:3.12-slim
# ❌ BAD - Pins OS version (bookworm), prevents security updates
FROM python:3.12-slim-bookworm
```
**Why?** OS versions update with security patches. Pin runtime (python:3.12) for reproducible behavior.
3. **Create `.dockerignore`:**
Use template from `assets/dockerignore-template`
4. **Use cache mounts for package managers:**
```dockerfile
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
```
5. **APT cache setup (before any apt operations):**
```dockerfile
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
```
6. **Never use ARG/ENV for secrets:**
```dockerfile
RUN --mount=type=secret,id=api_key curl -H "Authorization: $(cat /run/secrets/api_key)" https://api.example.com
```
7. **Use non-root user with safe UID/GID:**
```dockerfile
# Let system auto-assign (simple)
RUN groupadd -r app && useradd -r -g app app
# Or use UID/GID >10000 for consistency across environments
RUN groupadd -r -g 10001 app && useradd -r -u 10001 -g app app
```
## Language-Specific Templates
### Python (with uv - Recommended)
For detailed Python/uv integration, see `references/uv_integration.md`.
```dockerfile
# syntax=docker/dockerfile:1
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
# Install dependencies (separate layer for caching)
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
# Copy and install project
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked
# Security: non-root user with UID/GID >10000
RUN groupadd -r -g 10001 app && \
useradd -r -u 10001 -g app app && \
chown -R app:app /app
USER app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "myapp"]
```
### Node.js
```dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
# Install dependencies
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn \
yarn install --frozen-lockfile --production
# Copy source
COPY . .
# Security: non-root user with UID/GID >10000
RUN addgroup -g 10001 app && \
adduser -u 10001 -G app -S app && \
chown -R app:app /app
USER app
EXPOSE 3000
CMD ["node", "index.js"]
```
### Go (Multi-stage)
```dockerfile
# syntax=docker/dockerfile:1
# Build stage
FROM golang:1-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="-w -s" -o main
# Runtime stage
FROM alpine:3
RUN addgroup -g 10001 app && adduser -u 10001 -G app -S app
USER app
COPY --from=builder /app/main /main
ENTRYPOINT ["/main"]
```
### Debian-based (with APT cache)
```dockerfile
# syntax=docker/dockerfile:1
FROM debian:stable-slim
# Configure APT for cache reuse
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
WORKDIR /app
# Install dependencies with cache mount
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates
# Copy application
COPY . .
# Security: non-root user with UID/GID >10000
RUN groupadd -r -g 10001 app && \
useradd -r -u 10001 -g app app && \
chown -R app:app /app
USER app
CMD ["./app"]
```
### PHP (with Composer)
```dockerfile
# syntax=docker/dockerfile:1
FROM php:8-fpm-alpine
WORKDIR /app
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Install dependencies
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/tmp/cache \
composer install --no-dev --optimize-autoloader --no-scripts
# Copy source
COPY . .
RUN composer dump-autoload --optimize
# Security: non-root user with UID/GID >10000
RUN addgroup -g 10001 app && \
adduser -u 10001 -G app -S app && \
chown -R app:app /app
USER app
EXPOSE 9000
CMD ["php-fpm"]
```
## Security Checklist
- [ ] Use specific version tags for base images
- [ ] Use minimal base image (alpine, slim, distroless)
- [ ] Create and use non-root user
- [ ] Never expose secrets via ARG/ENV
- [ ] Use `RUN --mount=type=secret` for sensitive data
- [ ] Add HEALTHCHECK instruction
- [ ] Scan image for vulnerabilities (`docker scan`)
## Performance Checklist
- [ ] Add BuildKit syntax directive
- [ ] Create comprehensive `.dockerignore`
- [ ] Order instructions: manifests → deps → code
- [ ] Use `--mount=type=cache` for all package managers
- [ ] Implement multi-stage builds for compiled languages
- [ ] Chain RUN commands with `&&`
- [ ] Clean up in same RUN instruction
## Size Optimization
**Quick wins:**
1. Use alpine or slim variants
2. Multi-stage builds
3. Remove unnecessary files in same layer
4. Use `--no-install-recommends` with apt
5. Consider distroless for runtime
## Common Patterns
### Intermediate Layers (Faster Rebuilds)
Separate dependency installation from code copy:
```dockerfile
# Install deps first (rarely changes)
COPY package.json package-lock.json ./
RUN npm ci --production
# Copy code (changes frequently)
COPY . .
```
### Remote Cache (CI/CD)
```bash
docker buildx build \
--cache-from=type=registry,ref=myregistry.com/app:cache \
--cache-to=type=registry,ref=myregistry.com/app:cache,mode=max \
--push \
-t myregistry.com/app:latest .
```
### Secret Management
```bash
# Build with secret
docker buildx build --secret id=api_key,src=./key.txt .
# Or from environment
docker buildx build --secret id=api_key,env=API_KEY .
```
## Tools and Scripts
### Analyze Dockerfile
```bash
python scripts/analyze_dockerfile.py ./Dockerfile
```
Detects common anti-patterns:
- Missing BuildKit syntax
- Using :latest tags
- ADD instead of COPY
- Missing cache mounts
- Secrets in ARG/ENV
- Missing non-root USER
- apt-get without cleanup
### Generate .dockerignore
```bash
cp assets/dockerignore-template .dockerignore
```
## Docker Compose Best Practices
For multi-container applications, follow modern Compose practices:
### Key Rules
1. **Don't use `version:` field** - Deprecated since Compose V2
```yaml
# ✅ GOOD - No version field
services:
app:
image: myapp:1.0.0
```
2. **Never use `container_name:`** - Prevents scaling and parallel environments
```yaml
# ✅ GOOD - Let Compose generate names
services:
app:
image: myapp:1.0.0
# No container_name - allows scaling with --scale
# Use project names for environment isolation:
# docker compose -p myapp-dev up
# docker compose -p myapp-test up
```
3. **Use specific image tags** - Not `:latest`
4. **Define health checks** - For service dependencies
5. **Set resource limits** - Prevent resource exhaustion
For complete Compose guide, see `references/compose_best_practices.md`.
## Reference Documentation
For detailed information, consult these references:
1. **`references/optimization_guide.md`** - Complete optimization guide with BuildKit, caching, multi-stage builds
2. **`references/best_practices.md`** - Checklist of all best practices with impact levels
3. **`references/examples.md`** - Real-world before/after optimization examples
4. **`references/uv_integration.md`** - Python with uv package manager (recommended for Python)
5. **`references/compose_best_practices.md`** - Docker Compose modern practices (no version:, no container_name:)
## Common Issues and Solutions
### Slow builds
- Add cache mounts: `RUN --mount=type=cache,target=...`
- Optimize layer ordering: deps before code
- Use remote cache in CI/CD
### Large images
- Use multi-stage builds
- Switch to alpine/slim base images
- Clean up in same RUN instruction
- Remove dev dependencies
### Security concerns
- Pin versions with SHA256
- Use non-root USER
- Never use ARG/ENV for secrets
- Use BuildKit secret mounts
- Scan with `docker scan`
### Cache invalidation
- Separate dependency installation from code
- Use `--mount=type=bind` for manifests
- Order instructions correctly
## Examples by Use Case
### CLI Tool
```dockerfile
# syntax=docker/dockerfile:1
FROM python:3.12-alpine
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv uv sync --locked
ENV PATH="/app/.venv/bin:$PATH"
ENTRYPOINT ["mytool"]
```
### Web API
```dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn yarn install --frozen-lockfile --production
COPY . .
RUN addgroup -g 10001 app && \
adduser -u 10001 -G app -S app && \
chown -R app:app /app
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
```
### Static Site
```dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn yarn install --frozen-lockfile
COPY . .
RUN yarn build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
```
## Build Commands Reference
```bash
# Basic build
docker buildx build -t myapp:latest .
# With cache from registry
docker buildx build \
--cache-from=type=registry,ref=registry.com/myapp:cache \
--cache-to=type=registry,ref=registry.com/myapp:cache,mode=max \
-t myapp:latest .
# With secrets
docker buildx build \
--secret id=api_key,src=./key.txt \
-t myapp:latest .
# Multi-platform
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myapp:latest --push .
# Target specific stage
docker buildx build --target builder -t myapp:builder .
```
## Next Steps
After creating your Dockerfile:
1. Run analyzer: `python scripts/analyze_dockerfile.py Dockerfile`
2. Test locally: `docker buildx build -t test .`
3. Check size: `docker images test`
4. Scan for vulnerabilities: `docker scout cves test`
5. Profile build: `docker buildx build --progress=plain . 2>&1 | less`
Attribution
Comments
Loading comments…