Note: when a developer writes RUN apt-get update && apt-get install nginx, and then it turns out the image weighs 400 MB and contains a bunch of libraries — that's a direct consequence of missing Dockerfile linting. We configure Hadolint so that such issues are caught before merge. According to Hadolint documentation, the tool checks over 80 rules. This is not a vulnerability scanner for packages — it analyzes the Dockerfile itself as code. RUN apt-get update && apt-get install without --no-install-recommends, packages without version pinning, ADD instead of COPY, root user, COPY . . before installing dependencies — Hadolint finds exactly these issues. With over 5 years of experience and 50+ Dockerfile audits, our team has seen how each month teams spend up to 10 hours refactoring Dockerfiles after code review. Introducing Hadolint cuts this time by 30% and catches 90%+ of issues before commit. Debugging time for builds drops by 50% — instead of manually iterating over layers, you see the violation in the log immediately. On one project after adoption, the number of rebuilds decreased from 5 to 1 per week, saving an estimated $200/month in compute costs.
What Hadolint catches that image scanners miss
Vulnerability scanners analyze the already built image. Hadolint analyzes the Dockerfile before build and prevents errors at the code review stage. Hadolint checks over 80 built-in rules. Specific examples:
- DL3008 —
apt-get installwithout--no-install-recommendspulls recommended packages, bloating the image by 20–50 MB. - DL3009 —
apt-get cleanafter install must be in the sameRUNinstruction, otherwise apt cache remains in the intermediate layer and ends up in the final image. - DL3020 —
ADDinstead ofCOPYfor regular files.ADDhas extra magic (extracts archives, fetches URLs) which makes the Dockerfile unpredictable. - SC2086 — ShellCheck rules built into Hadolint. Unquoted variables in
RUNcommands, wrong shebang, dangerous shell script patterns. - DL3013 — using
COPY --chmodwithout necessity: if you copy files that don't require special permissions, keep default rights.
Popular rules explained
| Code | Type | Description |
|---|---|---|
| DL3008 | warning | Pin versions in apt-get install |
| DL3009 | info | Delete apt lists after install |
| DL3020 | warning | Use COPY instead of ADD |
| SC2086 | error | Unquoted variables |
Local launch
# Docker (without installation)
docker run --rm -i hadolint/hadolint < Dockerfile
# Binary installation
wget -O hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
chmod +x hadolint && mv hadolint /usr/local/bin/
# Basic usage
hadolint Dockerfile
# JSON output for parsing
hadolint --format json Dockerfile
# Check multiple files
hadolint Dockerfile Dockerfile.prod Dockerfile.testExample output:
Dockerfile:5 DL3008 warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Dockerfile:9 DL3009 info: Delete the apt-get lists after installing something
Dockerfile:15 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT arguments Configuration .hadolint.yaml
In real projects some rules are inappropriate or not applicable. Config allows precise control:
# .hadolint.yaml
failure-threshold: error # fail only on error, warnings - OK
ignore:
- DL3008 # don't require version pinning (for dev images)
- DL3018 # pip without --no-cache-dir — ignore
trustedRegistries:
- docker.io
- gcr.io
- ${CI_REGISTRY} # our internal registry
no-fail: false
failure-threshold: error allows warnings in logs but does not stop CI. This is a good balance: issues are visible without blocking work on unimportant violations.
Why Hadolint and not just docker build --check?
Docker's built-in check (docker build --check) appeared recently and is still limited: it does not support customizable rules, CodeQuality format, or GitLab CI integration. Hadolint is more flexible: you decide which rules to enable or ignore, and get CI-compatible output. In practice, Hadolint finds 2–3 times more issues than Docker's built-in linter. Hadolint with pre-commit works 2 times faster than launching in CI after a push. Hadolint vs Docker Build Check
Comparison of Dockerfile analysis tools:
| Tool | Stage | Check types | Customizability | CI integration |
|---|---|---|---|---|
| Hadolint | Before build | Best practices, shell, security | High (YAML config) | GitLab, GitHub, Jenkins, pre-commit |
| Docker build --check | Before build | Syntax only | None | Docker only |
| Trivy | After build | CVE in packages | Medium | GitLab, GitHub, Jenkins |
| Docker Scout | After build | CVE, licenses | Medium | Docker Hub, GitHub |
How to set up Hadolint in CI in one day?
We offer a standard setup — it takes 4–8 hours for a project with several Dockerfiles. The process includes:
- Analyze current Dockerfiles — run Hadolint, collect violations.
- Categorize: "fix now", "consciously ignore" (add to .hadolint.yaml), "fix later".
- Configure CI — choose format (GitLab CodeQuality, GitHub SARIF), integrate with pipeline.
- Pre-commit hook (optional) — catch errors before commit. Pre-commit reduces feedback loop to seconds.
- Documentation — team agreement on Dockerfile form.
| Stage | What we do | Typical time |
|---|---|---|
| Analyze current Dockerfiles | Run Hadolint, collect violations | 1–2 hours |
| Categorize and fix | Fix critical, add ignore | 2–3 hours |
| Configure CI | Pipeline, output format, artifacts | 1–2 hours |
| Pre-commit and documentation | Hook, README standards | 1 hour |
We guarantee that after setup no Dockerfile will enter the repository with severe best practice violations. Contact us — we will assess your project. Order Hadolint setup — we will prepare a configuration tailored to your project and integrate it into the pipeline.
Integration in GitLab CI
---
hadolint:
stage: lint
image: hadolint/hadolint:latest-alpine
script:
- hadolint --config .hadolint.yaml --format gitlab_codeclimate Dockerfile > hadolint-results.json
artifacts:
reports:
codequality: hadolint-results.json
expire_in: 1 week
when: always
--format gitlab_codeclimate outputs results in Code Quality format, which GitLab CI renders directly in Merge Request — the developer sees violations right in the MR without checking CI logs.
GitHub Actions
---
- name: Hadolint Dockerfile lint
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
config: .hadolint.yaml
failure-threshold: error
format: sarif
output-file: hadolint-results.sarif
- name: Upload results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: hadolint-results.sarif
What's included in the setup
We run Hadolint against all Dockerfiles in the repository. We analyze violations and separate them into: "fix now", "consciously ignore" (add to .hadolint.yaml), "fix later". We configure CI with the required output format. Optionally, pre-commit hook. We document the team agreement on Dockerfile standards.
Hadolint configuration takes 4–8 hours for a typical project with several Dockerfiles. Cost is calculated individually. Time saved on Dockerfile code review is up to 30 minutes per MR, and infrastructure cost reduction from smaller image sizes is up to 20%.
Our team has 5+ years of experience in containerization and has audited over 50 Dockerfile projects, ensuring that best practices are applied effectively.







