hadolint: Dockerfile Linter JavaScript API

[ development, gpl, library, program ] [ Propose Tags ]

A smarter Dockerfile linter that helps you build best practice Docker images.

[Skip to Readme]
Versions [faq] 1.2.5, 1.2.6, 1.3.0, 1.4.0, 1.5.0, 1.6.0, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.7.0, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.8.0, 1.9.0, 1.10.1, 1.10.2, 1.10.3, 1.10.4, 1.11.0, 1.11.1, 1.11.2, 1.12.0, 1.13.0, 1.14.0, 1.15.0, 1.16.1, 1.16.2, 1.16.3, 1.17.0, 1.17.1, 1.17.2, 1.17.3, 1.17.6, 1.17.7, 1.18.0, 1.18.1, 1.18.2
Dependencies aeson, base (>=4.8 && <5), bytestring, containers, directory (>=1.3.0), filepath, gitrev (>=1.3.1), hadolint, language-docker (>=6.0.1 && <7), megaparsec (>=6.4), mtl, optparse-applicative (>=0.14.0), ShellCheck (>=0.5.0), split (>=0.2), text, void, yaml [details]
License GPL-3.0-only
Copyright Lukas Martinelli
Author Lukas Martinelli
Maintainer me@lukasmartinelli.ch
Category Development
Home page https://github.com/hadolint/hadolint
Source repo head: git clone git@github.com:hadolint/hadolint.git
Uploaded by lorenzo at 2018-06-18T15:44:58Z
Distributions Fedora:1.18.0, NixOS:1.18.0
Executables hadolint
Downloads 17413 total (1097 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2018-06-18 [all 1 reports]




Maintainer's Corner

For package maintainers and hackage trustees

Readme for hadolint-1.7.5

[back to package description]

[![Linux/OSX Build Status][travis-img]][travis] [![Windows Build status][appveyor-img]][appveyor] [![GPL-3 licensed][license-img]][license] [![GitHub release][release-img]][release] ![Github downloads][downloads-img] <img align="right" alt="pipecat" width="150" src="http://hadolint.lukasmartinelli.ch/img/cat_container.png" />

Haskell Dockerfile Linter

A smarter Dockerfile linter that helps you build [best practice][] Docker images. The linter is parsing the Dockerfile into an AST and performs rules on top of the AST. It is standing on the shoulders of [ShellCheck][] to lint the Bash code inside RUN instructions.

<!-- [:globe_with_meridians: **Check the online version on hadolint.lukasmartinelli.ch**](http://hadolint.lukasmartinelli.ch/.) [![Screenshot](screenshot.png)](http://hadolint.lukasmartinelli.ch/) -->

How to use

You can run hadolint locally to lint your Dockerfile.

hadolint <Dockerfile>
hadolint --ignore DL3003 --ignore DL3006 <Dockerfile> # exclude specific rules

Docker comes to the rescue to provide an easy way how to run hadolint on most platforms. Just pipe your Dockerfile to docker run:

docker run --rm -i hadolint/hadolint < Dockerfile


You can download prebuilt binaries for OSX, Windows and Linux from the latest [release page][]. However, if it doesn't work for you, please fall back to Docker, brew or source installation.

If you are on OSX you can use brew to install hadolint.

brew install hadolint

As shwon before, hadolint ia available as a docker container:

docker pull hadolint/hadolint

If you need a docker container with shell access, use the debian variant of the docker image:

docker pull hadolint/hadolint:latest-debian

You can also build hadolint locally. You need [Haskell][] and the [stack][] build tool to build the binary.

git clone https://github.com/hadolint/hadolint
cd hadolint
stack install


hadolint supports specifying the ignored rules using a configuration file. The configuration file should be in yaml format. This is one valid configuration file as an example:

  - DL3000
  - SC1010

Configuration files can be used globally or per project. By default, hadolint will look for a configuration file in the current directory with the name .hadolint.yaml

The global configuration file should be placed in the folder specified by XDG_CONFIG_HOME, with the name hadolint.yaml. In summary, the following locations are valid for the configuration file, in order or preference:

  • $PWD/.hadolint.yaml
  • $XDG_CONFIG_HOME/hadolint.yaml
  • ~/.config/hadolint.yaml

In windows, the %LOCALAPPDATA% environment variable is used instead of XDG_CONFIG_HOME

Inline ignores

It is also possible to ignore rules by using a special comment directly above the Dockerfile instruction you want to make an exception for. Ignore rule comments look like # hadolint ignore=DL3001,SC1081. For example:

# hadolint ignore=DL3006
FROM ubuntu

# hadolint ignore=DL3003,SC1035
RUN cd /tmp && echo "hello!"

Inline ignores will only work if place directly above the instruction.


To get most of hadolint it is useful to integrate it as a check to your CI or to your editor to lint your Dockerfile as you write it. See our [Integration][] docs.


An incomplete list of implemented rules. Click on the error code to get more detailed information.

  • Rules with the prefix DL originate from hadolint. Take a look at Rules.hs to find the implementation of the rules.

  • Rules with the SC prefix originate from ShellCheck (Only the most common rules are listed, there are dozens more)

Please [create an issue][] if you have an idea for a good rule.

| Rule | Description | |:-------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------| | DL3000 | Use absolute WORKDIR. | | DL3001 | For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig. | | DL3002 | Last user should not be root. | | DL3003 | Use WORKDIR to switch to a directory. | | DL3004 | Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root. | | DL3005 | Do not use apt-get upgrade or dist-upgrade. | | DL3007 | Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag. | | DL3006 | Always tag the version of an image explicitly. | | DL3008 | Pin versions in apt-get install. | | DL3009 | Delete the apt-get lists after installing something. | | DL3010 | Use ADD for extracting archives into an image. | | DL3011 | Valid UNIX ports range from 0 to 65535. | | DL3012 | Provide an email address or URL as maintainer. | | DL3013 | Pin versions in pip. | | DL3014 | Use the -y switch. | | DL3015 | Avoid additional packages by specifying --no-install-recommends. | | DL3016 | Pin versions in npm. | | DL3017 | Do not use apk upgrade. | | DL3018 | Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>. | | DL3019 | Use the --no-cache switch to avoid the need to use --update and remove /var/cache/apk/* when done installing packages. | | DL3020 | Use COPY instead of ADD for files and folders. | | DL3021 | COPY with more than 2 arguments requires the last argument to end with / | | DL3022 | COPY --from should reference a previously defined FROM alias | | DL3023 | COPY --from cannot reference its own FROM alias | | DL3024 | FROM aliases (stage names) must be unique | | DL4000 | MAINTAINER is deprecated. | | DL4001 | Either use Wget or Curl but not both. | | DL4003 | Multiple CMD instructions found. | | DL4004 | Multiple ENTRYPOINT instructions found. | | DL4005 | Use SHELL to change the default shell. | | SC1000 | $ is not used specially and should therefore be escaped. | | SC1001 | This \c will be a regular 'c' in this context. | | SC1007 | Remove space after = if trying to assign a value (or for empty string, use var='' ...). | | SC1010 | Use semicolon or linefeed before done (or quote to make it literal). | | SC1018 | This is a unicode non-breaking space. Delete it and retype as space. | | SC1035 | You need a space here | | SC1045 | It's not foo &; bar, just foo & bar. | | SC1065 | Trying to declare parameters? Don't. Use () and refer to params as $1, $2 etc. | | SC1066 | Don't use $ on the left side of assignments. | | SC1068 | Don't put spaces around the = in assignments. | | SC1077 | For command expansion, the tick should slant left (` vs ยด). | | SC1078 | Did you forget to close this double-quoted string? | | SC1079 | This is actually an end quote, but due to next char, it looks suspect. | | SC1081 | Scripts are case sensitive. Use if, not If. | | SC1083 | This {/} is literal. Check expression (missing ;/\n?) or quote it. | | SC1086 | Don't use $ on the iterator name in for loops. | | SC1087 | Braces are required when expanding arrays, as in ${array[idx]}. | | SC1095 | You need a space or linefeed between the function name and body. | | SC1097 | Unexpected ==. For assignment, use =. For comparison, use [/[[. | | SC1098 | Quote/escape special characters when using eval, e.g. eval "a=(b)". | | SC1099 | You need a space before the #. | | SC2002 | Useless cat. Consider <code>cmd < file | ..</code> or <code>cmd file | ..</code> instead. | | SC2015 | Note that <code>A && B || C</code> is not if-then-else. C may run when A is true. | | SC2026 | This word is outside of quotes. Did you intend to 'nest '"'single quotes'"' instead'? | | SC2028 | echo won't expand escape sequences. Consider printf. | | SC2035 | Use ./*glob* or -- *glob* so names with dashes won't become options. | | SC2046 | Quote this to prevent word splitting | | SC2086 | Double quote to prevent globbing and word splitting. | | SC2140 | Word is in the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"? | | SC2154 | var is referenced but not assigned. | | SC2164 | Use <code>cd ... || exit</code> in case cd fails. |


If you are an experienced Haskeller we would be really thankful if you would tear our code apart in a review.


  1. Clone repository

    git clone --recursive git@github.com:hadolint/hadolint.git
  2. Install the dependencies

    stack install


The easiest way to try out the parser is using the REPL.

# start the repl
stack repl
# parse instruction and look at AST representation
parseString "FROM debian:jessie"


Run unit tests.

stack test

Run integration tests.



Dockerfile syntax is fully described in the [Dockerfile reference][]. Just take a look at [Syntax.hs][] in the language-docker project to see the AST definition.


<!-- References --> [travis-img]: https://travis-ci.org/hadolint/hadolint.svg?branch=master [travis]: https://travis-ci.org/hadolint/hadolint [appveyor-img]: https://ci.appveyor.com/api/projects/status//github/hadolint/hadolint?svg=true&branch=master [appveyor]: https://ci.appveyor.com/project/hadolint/hadolint/branch/master [license-img]: https://img.shields.io/badge/license-GPL--3-blue.svg [license]: https://tldrlegal.com/l/gpl-3.0 [release-img]: https://img.shields.io/github/release/hadolint/hadolint.svg [release]: https://github.com/hadolint/hadolint/releases/latest [downloads-img]: https://img.shields.io/github/downloads/hadolint/hadolint/total.svg [best practice]: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices [shellcheck]: https://github.com/koalaman/shellcheck [release page]: https://github.com/hadolint/hadolint/releases/latest [haskell]: https://www.haskell.org/platform/ [stack]: http://docs.haskellstack.org/en/stable/install_and_upgrade.html [integration]: docs/INTEGRATION.md [create an issue]: https://github.com/hadolint/hadolint/issues/new [dockerfile reference]: http://docs.docker.com/engine/reference/builder/ [syntax.hs]: https://www.stackage.org/haddock/nightly-2018-01-07/language-docker-2.0.1/Language-Docker-Syntax.html