Comprehensive toolkit for validating, linting, and securing Dockerfiles. Use this skill when validating Dockerfile syntax, checking security best practices, optimizing image builds. Applies to all Dockerfile variants (Dockerfile, Dockerfile.prod, Dockerfile.dev, etc.).
Overall
score
93%
Does it follow best practices?
Validation for skill structure
This document summarizes official Docker best practices based on current recommendations from Docker documentation and industry standards.
.dockerignore to exclude unnecessary filesUse specific tags, not :latest
# Bad
FROM node:latest
# Good
FROM node:21-alpine
# Better
FROM node:21-alpine@sha256:abc123...Choose minimal base images
Prefer official images
Chain commands to reduce layers
# Bad - creates 4 layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN curl -sL https://example.com/script.sh | bash
# Good - creates 1 layer
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
vim \
&& rm -rf /var/lib/apt/lists/* \
&& curl -sL https://example.com/script.sh | bashClean up in same layer
# Package manager cache must be removed in same RUN
RUN apt-get update && apt-get install -y \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*
# For Alpine
RUN apk add --no-cache package1 package2Use --no-install-recommends for apt
RUN apt-get install -y --no-install-recommends packagePin package versions
# For apt
RUN apt-get install -y package=1.2.3-1
# For apk
RUN apk add package=1.2.3-r0
# For pip
RUN pip install package==1.2.3Sort multi-line arguments
RUN apt-get update && apt-get install -y \
curl \
git \
vim \
wget \
&& rm -rf /var/lib/apt/lists/*Use pipefail for pipes
RUN set -o pipefail && wget -O - https://example.com | wc -l > /numberPrefer COPY over ADD
# Use COPY for files and directories
COPY app.py /app/
# Only use ADD for auto-extraction or remote URLs
ADD https://example.com/file.tar.gz /tmp/Use COPY --chown to avoid extra layer
# Bad - creates extra layer
COPY app.py /app/
RUN chown user:user /app/app.py
# Good - single layer
COPY --chown=user:user app.py /app/Use absolute paths
# Bad
WORKDIR app
# Good
WORKDIR /appDon't use RUN cd
# Bad
RUN cd /app && npm install
# Good
WORKDIR /app
RUN npm installDon't run as root
# Create user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Or for Alpine
RUN addgroup -g 1001 -S appuser && adduser -S appuser -u 1001
# Switch to user
USER appuserUse high UID (>10000) for better security
RUN useradd -u 10001 -m appuser
USER appuserUse exec form for proper signal handling
# Bad - shell form (doesn't handle signals)
CMD python app.py
# Good - exec form
CMD ["python", "app.py"]Combine ENTRYPOINT and CMD
# ENTRYPOINT defines the executable
ENTRYPOINT ["python"]
# CMD provides default arguments (can be overridden)
CMD ["app.py"]Document ports even though it doesn't publish
EXPOSE 8080
EXPOSE 443Add health checks for services
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1Add metadata
LABEL org.opencontainers.image.authors="team@example.com"
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.description="Application description"Order instructions from least to most frequently changing
# 1. Base image (rarely changes)
FROM node:21-alpine
# 2. System packages (rarely change)
RUN apk add --no-cache curl
# 3. Dependencies (change occasionally)
COPY package*.json ./
RUN npm ci
# 4. Source code (changes frequently)
COPY . .Separate build and runtime
# Build stage
FROM node:21 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Runtime stage
FROM node:21-alpine
COPY --from=builder /app/dist /app
CMD ["node", "/app/index.js"]Enable modern features
# syntax=docker/dockerfile:1
# Use cache mounts
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# Use secret mounts (secrets not in final image)
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://bucket/file .docker scan myimage:tag
# or
trivy image myimage:tag# Bad
ENV DATABASE_PASSWORD=secret123
# Good - use runtime config or secrets
# Pass at runtime: docker run -e DATABASE_PASSWORD=...USER appuserdocker run --read-only myimagedocker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myimage# Missing cleanup increases image by hundreds of MB
RUN apt-get update && apt-get install -y package
# Missing: && rm -rf /var/lib/apt/lists/*# Bloated image
RUN apt-get install -y vim nano emacs curl wgetInstall with Tessl CLI
npx tessl i pantheon-ai/dockerfile-validator@0.1.0