Why Your Dockerfile Needs a “Health Check”
When I first started with Docker, I thought it was simple: as long as docker build created an image and docker run launched the app, I was done. Honestly, I used to “cook up” Dockerfiles by copy-pasting from StackOverflow or outdated blog posts. The result? A simple Node.js app with a 1.2GB image, a 5-minute build time, and security holes I didn’t even know existed.
The turning point came when I upgraded our systems from Docker Compose v1 to v2. This process inadvertently “exposed” those old, messy Dockerfiles. I discovered many images were using outdated base images (Debian Buster instead of Bullseye/Bookworm), installing packages without clearing caches, or worst of all, running containers as root unnecessarily. That’s when I turned to Hadolint.
Hadolint isn’t just a simple syntax checker. It scrutinizes every line of code to ensure you’re following Docker Best Practices. Its biggest selling point is the built-in Shellcheck integration, which catches errors in the shell commands within RUN instructions. Writing a Dockerfile without Hadolint is like writing JavaScript without ESLint—you’re just asking for trouble in production.
4 Immediate Benefits of Using Hadolint
- Image Weight Loss: It reminds you to clear
/var/lib/apt/lists/*or use Multi-stage builds. I once shrunk a Python image from 850MB to 120MB just by following Hadolint’s suggestions. - Security Patching: Get alerted immediately when using root privileges or installing unnecessary packages that create openings for hackers.
- Style Consistency: The whole team will write Dockerfiles to a single standard, no more individual “flavoring.”
- Blocking Silly Mistakes: Detect usage of the
latesttag—a fatal error that makes apps run fine in staging but crash in production.
How to Install Hadolint
Installing Hadolint is so fast you won’t even finish your cup of coffee.
1. Run Quickly with Docker
Don’t want to install it on your local machine? Use Docker itself to check your Dockerfile:
docker run --rm -i hadolint/hadolint < Dockerfile
2. Direct Installation for MacOS/Linux
Mac users can just use brew:
brew install hadolint
For Linux, download the binary directly from GitHub Releases with just two commands:
sudo wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
sudo chmod +x /usr/local/bin/hadolint
3. The Ultimate “Weapon” for VS Code
I highly recommend Junior developers install the Hadolint extension. It underlines non-standard sections in red while you type. It feels like having a seasoned Senior dev sitting next to you, helping you level up quickly without reading through heaps of dry documentation.
Configuration and Practical Usage
Hadolint returns error codes starting with DL (Docker Lint) or SC (ShellCheck). Don’t panic if you see a long list; just tackle them one by one.
Checking Your First Dockerfile
Try running this command on the file you just wrote:
hadolint Dockerfile
If you see DL3008 warning: Pin versions in apt-get install, it means you need to specify package versions (e.g., python3=3.10.6-1) instead of letting it pull the latest.
Customizing to Your Needs
Sometimes, certain rules are too strict for small projects. You can create a .hadolint.yaml file in the root directory to disable them:
# .hadolint.yaml
ignored:
- DL3008 # Ignore package version requirements for speed
- DL3015 # No more reminders for --no-install-recommends
override:
error:
- DL3001 # Upgrade this warning to a mandatory error
3 “Golden” Rules to Remember
Based on my experience, these are the things Hadolint will save you from:
- Pin Your Versions: Absolutely avoid
FROM node:latest. UseFROM node:18.16.0-alpine. - Clean Up the Scene: Always combine
apt-get installand cache clearing in the sameRUNcommand to prevent layer bloat. - Prioritize WORKDIR: Don’t use
RUN cd /app; useWORKDIR /app. This ensures subsequent instructions are in the correct place.
Automation: Integrating Hadolint into CI/CD
Don’t wait for a build failure to fix things. Turn Hadolint into an automated gatekeeper.
GitHub Actions Integration
Just add these lines to your workflow, and if anyone on the team pushes a “bad” Dockerfile, the system will reject it immediately.
name: Lint Dockerfile
on: [push, pull_request]
jobs:
hadolint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Hadolint
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
Since adopting Hadolint, I no longer worry about unexpected image bloat or minor bugs caused by shell scripts. Clean images mean fast builds and smooth deployments, and it shows my boss a level of professionalism. Go grab your old Dockerfiles and run a test—I guarantee you’ll be surprised by how “red” they are!

