Cleaning Up Your Dockerfile with Hadolint: Stop Bloated and Insecure Images

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

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 latest tag—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:

  1. Pin Your Versions: Absolutely avoid FROM node:latest. Use FROM node:18.16.0-alpine.
  2. Clean Up the Scene: Always combine apt-get install and cache clearing in the same RUN command to prevent layer bloat.
  3. Prioritize WORKDIR: Don’t use RUN cd /app; use WORKDIR /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!

Share: