Docker Expert
Create containers that are small, secure, and predictable across local and CI environments.
When to Use
- You are creating or hardening a
Dockerfile.
- Builds are slow, non-reproducible, or failing in CI.
- Runtime behavior differs between local machine and container.
Workflow
Base image selection: Prefer alpine for minimal size when musl compatibility is acceptable; use distroless for maximum security (no shell, minimal attack surface); use debian-slim when glibc or common tools are required. Pin major.minor version (e.g. node:20-alpine3.19) and avoid latest.
Multi-stage builds: Use a builder stage for compilation and a minimal runtime stage. Copy only artifacts with COPY --from=builder:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
FROM node:20-alpine
RUN addgroup -g 1000 app && adduser -u 1000 -G app -D app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER app
CMD ["node", "dist/index.js"]
Layer ordering: Copy dependency manifests (package.json, requirements.txt, go.mod) before source code. Install dependencies in a separate RUN. This maximizes cache reuse when only source changes.
BuildKit cache mounts: For package managers, use cache mounts to speed repeated builds:
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev
Configure .dockerignore: Exclude node_modules, .git, .env*, *.log, and build artifacts. Missing entries bloat context and can leak secrets.
Non-root execution: Create a dedicated user, set USER, and use explicit WORKDIR. Prefer CMD with exec form ["executable", "arg1"] over shell form for proper signal handling.
Compose: Define depends_on with condition: service_healthy where applicable. Use env files for overrides, not hardcoded values.
Common Pitfalls
- apt-get without cleanup: Always combine
apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/* in one RUN to avoid bloating layers.
- COPY . before dependency install: Copying source before
npm install or pip install invalidates cache on every code change. Copy manifests first, install, then copy source.
- Missing
.dockerignore: Without it, node_modules, .git, and local .env can be sent as build context, causing slow builds and potential secret leakage.
- ENTRYPOINT without signal handling: Use exec form so PID 1 receives SIGTERM. Wrap scripts with
exec or use tini/dumb-init if a shell script is required.
- ADD vs COPY: Prefer
COPY; ADD has URL and archive-extraction behavior that can surprise. Use COPY for deterministic, explicit behavior.
Security Hardening
- Scan images: Run
trivy image <image> or grype <image> in CI. Fix critical/high CVEs before release.
- Read-only root filesystem: Add
securityContext: { readOnlyRootFilesystem: true } in Kubernetes; ensure writable paths use tmpfs or volumes.
- Drop capabilities: Run with
--cap-drop=ALL and add back only what is needed. Avoid --privileged.
- Minimal base: Prefer distroless or alpine. Fewer packages mean fewer vulnerabilities.
Quality Checklist
Output Format
Return:
- Recommended
Dockerfile/Compose changes with inline examples where helpful
- Build commands:
docker build -t <tag> . and verification: docker run --rm <tag>
- Security scan commands:
trivy image <tag> or equivalent
- Performance and security risks identified
- Follow-up hardening tasks (scan results, capability drops, read-only FS)
Constraints
- Avoid
latest tags in production workflows.
- Prefer deterministic lockfile-based installs (
npm ci, pip install -r requirements.txt with pinned versions).
- Do not rely on host-specific paths or tooling assumptions.
- Do not use
ADD when COPY suffices.
- Ensure entrypoint/CMD use exec form for proper signal propagation.
1---2id: docker-expert3name: docker-expert4description: Use this skill when containerizing services or debugging Docker workflows, including production Dockerfiles, Compose setups, reproducible builds, secure defaults, image size, caching, and runtime behavior.5category: DevOps6---78# Docker Expert910Create containers that are small, secure, and predictable across local and CI environments.1112## When to Use1314- You are creating or hardening a `Dockerfile`.15- Builds are slow, non-reproducible, or failing in CI.16- Runtime behavior differs between local machine and container.1718## Workflow19201. **Base image selection**: Prefer `alpine` for minimal size when musl compatibility is acceptable; use `distroless` for maximum security (no shell, minimal attack surface); use `debian-slim` when glibc or common tools are required. Pin major.minor version (e.g. `node:20-alpine3.19`) and avoid `latest`.21222. **Multi-stage builds**: Use a builder stage for compilation and a minimal runtime stage. Copy only artifacts with `COPY --from=builder`:2324```dockerfile25FROM node:20-alpine AS builder26WORKDIR /app27COPY package*.json ./28RUN npm ci --omit=dev29COPY . .30RUN npm run build3132FROM node:20-alpine33RUN addgroup -g 1000 app && adduser -u 1000 -G app -D app34COPY --from=builder /app/dist ./dist35COPY --from=builder /app/node_modules ./node_modules36USER app37CMD ["node", "dist/index.js"]38```39403. **Layer ordering**: Copy dependency manifests (`package.json`, `requirements.txt`, `go.mod`) before source code. Install dependencies in a separate RUN. This maximizes cache reuse when only source changes.41424. **BuildKit cache mounts**: For package managers, use cache mounts to speed repeated builds:4344```dockerfile45RUN --mount=type=cache,target=/root/.npm \46 npm ci --omit=dev47```48495. **Configure `.dockerignore`**: Exclude `node_modules`, `.git`, `.env*`, `*.log`, and build artifacts. Missing entries bloat context and can leak secrets.50516. **Non-root execution**: Create a dedicated user, set `USER`, and use explicit `WORKDIR`. Prefer `CMD` with exec form `["executable", "arg1"]` over shell form for proper signal handling.52537. **Compose**: Define `depends_on` with `condition: service_healthy` where applicable. Use env files for overrides, not hardcoded values.5455## Common Pitfalls5657- **apt-get without cleanup**: Always combine `apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*` in one RUN to avoid bloating layers.58- **COPY . before dependency install**: Copying source before `npm install` or `pip install` invalidates cache on every code change. Copy manifests first, install, then copy source.59- **Missing `.dockerignore`**: Without it, `node_modules`, `.git`, and local `.env` can be sent as build context, causing slow builds and potential secret leakage.60- **ENTRYPOINT without signal handling**: Use exec form so PID 1 receives SIGTERM. Wrap scripts with `exec` or use `tini`/`dumb-init` if a shell script is required.61- **ADD vs COPY**: Prefer `COPY`; `ADD` has URL and archive-extraction behavior that can surprise. Use `COPY` for deterministic, explicit behavior.6263## Security Hardening6465- **Scan images**: Run `trivy image <image>` or `grype <image>` in CI. Fix critical/high CVEs before release.66- **Read-only root filesystem**: Add `securityContext: { readOnlyRootFilesystem: true }` in Kubernetes; ensure writable paths use tmpfs or volumes.67- **Drop capabilities**: Run with `--cap-drop=ALL` and add back only what is needed. Avoid `--privileged`.68- **Minimal base**: Prefer distroless or alpine. Fewer packages mean fewer vulnerabilities.6970## Quality Checklist7172- [ ] Final image excludes build tools and caches.73- [ ] Secrets are not baked into image layers.74- [ ] Ports, volumes, and env vars are explicitly documented.75- [ ] Image runs with least privilege.7677## Output Format7879Return:8081- Recommended `Dockerfile`/Compose changes with inline examples where helpful82- Build commands: `docker build -t <tag> .` and verification: `docker run --rm <tag>`83- Security scan commands: `trivy image <tag>` or equivalent84- Performance and security risks identified85- Follow-up hardening tasks (scan results, capability drops, read-only FS)8687## Constraints8889- Avoid `latest` tags in production workflows.90- Prefer deterministic lockfile-based installs (`npm ci`, `pip install -r requirements.txt` with pinned versions).91- Do not rely on host-specific paths or tooling assumptions.92- Do not use `ADD` when `COPY` suffices.93- Ensure entrypoint/CMD use exec form for proper signal propagation.