Introducing Autopilot, an AI coding assistant
Using Hadolint, a dockerfile linter, to enforce best practices

Using Hadolint, a dockerfile linter, to enforce best practices

Mar 14, 2022
5 min read

Misconfigured Docker containers can be slow, heavy and difficult to maintain. This leads to sluggish releases, high storage costs and frustrated developers. Moreover, inadequate Docker containers can be a major security liability. A recent report by Aqua Security found that 50% of new Docker instances are attacked within 56 minutes of being deployed. Docker problems can be devastating for any business and software teams need to put in the time and energy to alleviate these risks.

Hadolint is a Dockerfile linter that helps you build best practice Docker images. I use it in all of my projects to ensure I’m creating small, secure, efficient and maintainable images.

Introduction to hadolint

Hadolint comes with a robust and easy to use CLI. You can install it on a variety of platforms, including macOS using brew install hadolint.

Confirm the installation was successful with the following command:


We’ll use the following Dockerfile as an example, which can be used to run a Python Django web server. On the surface, it looks fine but we’ll see it has a lot of problems.


Let’s run it through Hadolint:


Every violation takes on the following structure:


Let’s dive into these parameters in more detail.

Rule code

A rule code is prefixed with either DL or SC. The DL prefix means the rule comes from Hadolint directly. The SC prefix means the rule comes from SpellCheck which is a static analysis tool for shell scripts that comes with Hadolint out of the box. You can find the combined list of rules here.

Every rule has a dedicated documentation page that lists code examples, rationale and other important details. See the dedicated page for DL3006 here.

You can ignore one or more rules using the --ignore RULECODE option:


You can also ignore rules within the Dockerfile inline. I prefer this approach because you can exclude rule codes on a per-line basis and it’s more clear where the violation is actually happening.


Hadolint has an active open-source community. New rule codes are added on a regular basis so be sure to check you’re running the latest version of Hadolint every so often.

Severity level

The severity level indicates how critical a violation is. There are six levels: error, warning, info, style, ignore, and none.

The CLI includes a --failure-threshold (abbreviated as -t) to exclude certain severity levels from causing a failure. For example, if you only want Hadolint to fail on error violations.


Note, violations from other severity levels will still be reported but they won’t cause a failure.

If you don’t agree with a rule code’s severity level, you can easily change it using the --<SEVERITY_LEVEL> RULECODE option. For example, the following command upgrades DL3006 to error and downgrades DL3045 to info (both codes are warning by default):


Label linting

Dockerfile labels are an excellent tool for annotating your Docker images. Hadolint comes with some validation options for ensuring your labels are set correctly.

The --require-label LABELSCHEMA option verifies that your labels follow a specific format. You can see all acceptable format values here.


The --strict-labels option verifies there are no extra labels outside of the ones defined in your schema.


Configuration file

Manually passing options into every Hadolint run can be annoying and error-prone. Hadolint conveniently comes with configuration file support for storing all of your options in a single place. This file can live in a variety of locations but I generally just put it in the repository’s root as .hadolint.yaml.


Fix the dockerfile

Working through each error one-by-one is a fantastic exercise for learning about Dockerfile best practices. As mentioned above, every rule has a very clear and detailed documentation page. Give it a shot and revisit this post when you’re done.

At this point, Hadolint should report no errors. Your file should look similar to this:


A few changes that need further explanation:

  • We’re tagging the python base image with the latest available Python minor version, which is currently 3.10. We’re not including the patch version (3.10.2) because Python patch versions are backwards compatible and generally include useful bug fixes.
  • I generally like to use the /app working directory to keep my Docker images consistent but you can use any new or existing directory you want.
  • We’re ignoring DL3013 because we want to download the latest version of pip. There’s no need to pin it to a specific version.


Hadolint includes many convenient integrations for automatically running the linter throughout the development process. My favorites are:

Integrations are crucial, especially in larger teams because some developers will forget to run the linter manually. I set them up immediately when I start a new Docker project.

Final thoughts

Hadolint is a terrific tool for building best practice Docker images. It gives you the peace of mind that your containers running in the cloud are small, fast and free of any major security vulnerabilities. Hook it into your development workflow and see what improvements you can make to your Dockerfiles.

If you're looking for an easier way to run your Docker-image-based tasks, consider using Airplane. With Airplane, you can build and run any image and run Docker commands against that image. This makes it easy to take your existing Docker images and deploy them onto Airplane's serverless platform that anyone on your team can use.

To try this out yourself, sign up for a free account or book a demo.

Share this article:
Johnny Metz
Johnny is a full-stack software engineer who specializes in web development and cloud technologies. He enjoys writing blog posts about Docker, Kubernetes, Python and JavaScript.

Subscribe to new blog posts from Airplane.