Reviews Dockerfiles for build performance, image size, and security issues. Use when optimizing, validating, or improving Dockerfiles.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
STARTER_CHARACTER = 🐳
When starting, announce: "🐳 Using DOCKERFILE-REVIEW skill".
droast (dockerfile-roast) is available by running command -v droast
droast --no-roast --min-severity warning <Dockerfile> and use the output as a starting pointDependency files (package.json, requirements.txt, go.mod) must be copied and installed BEFORE application source code. Violations cause full dependency reinstalls on every code change.
Anti-pattern: COPY . . followed by RUN pip install or RUN npm ci. The entire dependency layer rebuilds on any source change.
Impact: builds that should take seconds take minutes. With frequent builds, this compounds to hours per week.
Three tiers exist: full (includes compilers, tools), slim (minimal OS, basic tooling), distroless (application only, no shell).
Anti-patterns:
Alpine warning: Alpine uses musl libc instead of glibc. For Python, Node, and other runtimes with native extensions, this means rebuilding native deps from source (slower builds) or silent performance regressions. Prefer -slim (Debian-based) for these workloads — similar size savings, zero compatibility issues. Alpine remains a good choice for Go binaries and static builds where libc doesn't matter.
Without .dockerignore, COPY . . sends everything to the build context: node_modules (50-500MB), .git (100MB+), coverage reports, IDE configs, .env files.
COPY . . is fine — if the .dockerignore is correct. Most pain comes from a missing or incomplete ignore file, not from the COPY instruction itself.
Anti-patterns:
Build tools, compilers, and dev dependencies should not exist in the final image. Multi-stage builds separate construction from execution. The builder stage is the bloat zone; the final stage must be lean.
Anti-patterns:
Tags like latest or unpinned package versions create non-reproducible builds.
Anti-patterns:
FROM ubuntu:latest or FROM node:ltsapt-get install -y curl without version pinningFor production images, pin base images by digest, not just tag. node:20 today is not node:20 in six months — tags are mutable. Example: FROM node:20-slim@sha256:abc123.... This guarantees reproducible builds. Use tag+digest together so the tag documents intent while the digest locks the exact image.
Each RUN, COPY, ADD creates a layer. Deletions in later layers don't reduce image size — the previous layer still contains the data.
Anti-pattern: separate RUN instructions for install and cleanup. RUN apt-get update then RUN rm -rf /var/lib/apt/lists/* preserves the cache in the first layer.
Fix direction: combine install and cleanup in a single RUN with &&.
BuildKit's --mount=type=cache keeps package manager caches (pip, apt, cargo, npm) between builds without them ending up in the final layer. This dramatically speeds up rebuilds.
Example:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txtRUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt/lists \
apt-get update && apt-get install -y curlAnti-patterns:
Requires DOCKER_BUILDKIT=1 or Docker Buildx (default in modern Docker). Check the project's Docker version expectations before recommending.
Credentials, API keys, and tokens embedded in layers are permanently accessible via docker history or registry inspection.
Anti-patterns:
ENV API_KEY=... or ARG with secrets passed at build timeFix direction: use --mount=type=secret with BuildKit for build-time secrets. Never bake secrets into layers.
Running as root means a container escape or application vulnerability grants full system access.
Anti-patterns:
Fix direction: create a dedicated user with useradd, chown application files, then switch with USER — placed after all file operations that need root.
One process per container is a good default, not an absolute law. If the app needs nginx + app server and is not running at orchestrator scale, using supervisord or a process manager is a valid choice that avoids premature infrastructure complexity.
When to flag: when a Dockerfile runs multiple unrelated services that should clearly be separate (e.g., app + database). When NOT to flag: when a Dockerfile uses a process manager for tightly coupled processes in a simple deployment context.
saski/arnesto
752f690
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.