# Bad
FROM python:latest # Good
FROM python:3.11-slim
# Bad
FROM python:latest # Good
FROM python:3.11-slim
# Bad
FROM python:latest # Good
FROM python:3.11-slim
# Bad — runs everything as root
FROM node:20
COPY . /app
CMD ["node", "server.js"] # Good — creates and uses non-root user
FROM node:20-slim
RUN groupadd -r app && useradd -r -g app app
COPY --chown=app:app . /app
USER app
CMD ["node", "server.js"]
# Bad — runs everything as root
FROM node:20
COPY . /app
CMD ["node", "server.js"] # Good — creates and uses non-root user
FROM node:20-slim
RUN groupadd -r app && useradd -r -g app app
COPY --chown=app:app . /app
USER app
CMD ["node", "server.js"]
# Bad — runs everything as root
FROM node:20
COPY . /app
CMD ["node", "server.js"] # Good — creates and uses non-root user
FROM node:20-slim
RUN groupadd -r app && useradd -r -g app app
COPY --chown=app:app . /app
USER app
CMD ["node", "server.js"]
# Bad — reinstalls dependencies every time code changes
FROM python:3.11-slim
COPY . /app
RUN -weight: 500;">pip -weight: 500;">install -r /app/requirements.txt # Good — dependencies cached unless requirements.txt changes
FROM python:3.11-slim
COPY requirements.txt /app/
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r /app/requirements.txt
COPY . /app
# Bad — reinstalls dependencies every time code changes
FROM python:3.11-slim
COPY . /app
RUN -weight: 500;">pip -weight: 500;">install -r /app/requirements.txt # Good — dependencies cached unless requirements.txt changes
FROM python:3.11-slim
COPY requirements.txt /app/
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r /app/requirements.txt
COPY . /app
# Bad — reinstalls dependencies every time code changes
FROM python:3.11-slim
COPY . /app
RUN -weight: 500;">pip -weight: 500;">install -r /app/requirements.txt # Good — dependencies cached unless requirements.txt changes
FROM python:3.11-slim
COPY requirements.txt /app/
RUN -weight: 500;">pip -weight: 500;">install --no-cache-dir -r /app/requirements.txt
COPY . /app
# Bad — leaves cache in the image
RUN -weight: 500;">apt-get -weight: 500;">update && -weight: 500;">apt-get -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget # Good — clean up in the same layer
RUN -weight: 500;">apt-get -weight: 500;">update && \ -weight: 500;">apt-get -weight: 500;">install -y --no--weight: 500;">install-recommends -weight: 500;">curl -weight: 500;">wget && \ rm -rf /var/lib/-weight: 500;">apt/lists/*
# Bad — leaves cache in the image
RUN -weight: 500;">apt-get -weight: 500;">update && -weight: 500;">apt-get -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget # Good — clean up in the same layer
RUN -weight: 500;">apt-get -weight: 500;">update && \ -weight: 500;">apt-get -weight: 500;">install -y --no--weight: 500;">install-recommends -weight: 500;">curl -weight: 500;">wget && \ rm -rf /var/lib/-weight: 500;">apt/lists/*
# Bad — leaves cache in the image
RUN -weight: 500;">apt-get -weight: 500;">update && -weight: 500;">apt-get -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget # Good — clean up in the same layer
RUN -weight: 500;">apt-get -weight: 500;">update && \ -weight: 500;">apt-get -weight: 500;">install -y --no--weight: 500;">install-recommends -weight: 500;">curl -weight: 500;">wget && \ rm -rf /var/lib/-weight: 500;">apt/lists/*
# Bad — Docker has no idea if your app is actually working
CMD ["python", "server.py"] # Good — Docker can detect and -weight: 500;">restart unhealthy containers
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD -weight: 500;">curl -f http://localhost:8080/health || exit 1
CMD ["python", "server.py"]
# Bad — Docker has no idea if your app is actually working
CMD ["python", "server.py"] # Good — Docker can detect and -weight: 500;">restart unhealthy containers
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD -weight: 500;">curl -f http://localhost:8080/health || exit 1
CMD ["python", "server.py"]
# Bad — Docker has no idea if your app is actually working
CMD ["python", "server.py"] # Good — Docker can detect and -weight: 500;">restart unhealthy containers
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD -weight: 500;">curl -f http://localhost:8080/health || exit 1
CMD ["python", "server.py"]
-weight: 500;">git clone https://github.com/spinov001-art/dockerfile-linter
python linter.py Dockerfile
-weight: 500;">git clone https://github.com/spinov001-art/dockerfile-linter
python linter.py Dockerfile
-weight: 500;">git clone https://github.com/spinov001-art/dockerfile-linter
python linter.py Dockerfile
🔴 HIGH: Line 1 — Using :latest tag Fix: Pin to specific version (e.g., python:3.11-slim) 🔴 HIGH: Running as root (no USER instruction) Fix: Add USER nonroot before CMD 🟡 MEDIUM: Line 8 — -weight: 500;">apt-get without cleanup Fix: Add && rm -rf /var/lib/-weight: 500;">apt/lists/* Score: 62/100
🔴 HIGH: Line 1 — Using :latest tag Fix: Pin to specific version (e.g., python:3.11-slim) 🔴 HIGH: Running as root (no USER instruction) Fix: Add USER nonroot before CMD 🟡 MEDIUM: Line 8 — -weight: 500;">apt-get without cleanup Fix: Add && rm -rf /var/lib/-weight: 500;">apt/lists/* Score: 62/100
🔴 HIGH: Line 1 — Using :latest tag Fix: Pin to specific version (e.g., python:3.11-slim) 🔴 HIGH: Running as root (no USER instruction) Fix: Add USER nonroot before CMD 🟡 MEDIUM: Line 8 — -weight: 500;">apt-get without cleanup Fix: Add && rm -rf /var/lib/-weight: 500;">apt/lists/* Score: 62/100
- name: Lint Dockerfile run: python linter.py Dockerfile --fail-on high
- name: Lint Dockerfile run: python linter.py Dockerfile --fail-on high
- name: Lint Dockerfile run: python linter.py Dockerfile --fail-on high