A smarter Dockerfile linter that helps you build best practice Dockerimages. The linter parses the Dockerfile into an AST and performs rules ontop of the AST. It stands on the shoulders of ShellCheck to lintthe Bash code inside RUN
instructions.
You can run hadolint
locally to lint your Dockerfile.
hadolint <Dockerfile>
hadolint --ignore DL3003 --ignore DL3006 <Dockerfile> # exclude specific rules
hadolint --trusted-registry my-company.com:500 <Dockerfile> # Warn when using untrusted FROM images
Docker comes to the rescue, providing an easy way how to run hadolint
on mostplatforms.Just pipe your Dockerfile
to docker run
:
docker run --rm -i hadolint/hadolint < Dockerfile
# OR
docker run --rm -i ghcr.io/hadolint/hadolint < Dockerfile
or using Podman:
podman run --rm -i docker.io/hadolint/hadolint < Dockerfile
# OR
podman run --rm -i ghcr.io/hadolint/hadolint < Dockerfile
or using Windows PowerShell:
cat .\Dockerfile | docker run --rm -i hadolint/hadolint
You can download prebuilt binaries for OSX, Windows and Linux from the latestrelease page. However, if this does not work for you, please fall back tocontainer (Docker), brew
or source installation.
On OSX, you can use brew to install hadolint
.
brew install hadolint
On Windows, you can use scoop toinstall hadolint
.
scoop install hadolint
As mentioned earlier, hadolint
is available as a container image:
docker pull hadolint/hadolint
# OR
docker pull ghcr.io/hadolint/hadolint
If you need a container with shell access, use the Debian or Alpinevariants:
docker pull hadolint/hadolint:latest-debian
# OR
docker pull hadolint/hadolint:latest-alpine
# OR
docker pull ghcr.io/hadolint/hadolint:latest-debian
# OR
docker pull ghcr.io/hadolint/hadolint:latest-alpine
You can also build hadolint
locally. You need Haskell and the stackbuild tool to build the binary.
git clone https://github.com/hadolint/hadolint \
&& cd hadolint \
&& stack install
If you want theVS Code Hadolintextension to use Hadolint in a container, you can use the followingwrapper script:
#!/bin/bash
dockerfile="$1"
shift
docker run --rm -i hadolint/hadolint hadolint "$@" - < "$dockerfile"
hadolint --help
hadolint - Dockerfile Linter written in Haskell
Usage: hadolint [-v|--version] [--no-fail] [--no-color] [-c|--config FILENAME]
[-V|--verbose] [-f|--format ARG] [DOCKERFILE...]
[--error RULECODE] [--warning RULECODE] [--info RULECODE]
[--style RULECODE] [--ignore RULECODE]
[--trusted-registry REGISTRY (e.g. docker.io)]
[--require-label LABELSCHEMA (e.g. maintainer:text)]
[--strict-labels] [-t|--failure-threshold THRESHOLD]
Lint Dockerfile for errors and best practices
Available options:
-h,--help Show this help text
-v,--version Show version
--no-fail Don't exit with a failure status code when any rule
is violated
--no-color Don't colorize output
-c,--config FILENAME Path to the configuration file
-V,--verbose Enables verbose logging of hadolint's output to
stderr
-f,--format ARG The output format for the results [tty | json |
checkstyle | codeclimate | gitlab_codeclimate |
codacy] (default: tty)
--error RULECODE Make the rule `RULECODE` have the level `error`
--warning RULECODE Make the rule `RULECODE` have the level `warning`
--info RULECODE Make the rule `RULECODE` have the level `info`
--style RULECODE Make the rule `RULECODE` have the level `style`
--ignore RULECODE A rule to ignore. If present, the ignore list in the
config file is ignored
--trusted-registry REGISTRY (e.g. docker.io)
A docker registry to allow to appear in FROM
instructions
--require-label LABELSCHEMA (e.g. maintainer:text)
The option --require-label=label:format makes
Hadolint check that the label `label` conforms to
format requirement `format`
--strict-labels Do not permit labels other than specified in
`label-schema`
-t,--failure-threshold THRESHOLD
Exit with failure code only when rules with a
severity above THRESHOLD are violated. Accepted
values: [error | warning | info | style | ignore |
none] (default: info)
Configuration files can be used globally or per project. By default,hadolint
looks for a configuration file named .hadolint.yaml
or.hadolint.yml
in the current directory.
hadolint
full yaml
config file schema
failure-threshold: string # name of threshold level (error | warning | info | style | ignore | none)
format: string # Output format (tty | json | checkstyle | codeclimate | gitlab_codeclimate | codacy)
ignored: [string] # list of rules
label-schema: # See Linting Labels below for specific label-schema details
author: string # Your name
contact: string # email address
created: timestamp # rfc3339 datetime
version: string # semver
documentation: string # url
git-revision: string # hash
license: string # spdx
no-color: boolean # true | false
no-fail: boolean # true | false
override:
error: [string] # list of rules
warning: [string] # list of rules
info: [string] # list of rules
style: [string] # list of rules
strict-labels: boolean # true | false
trustedRegistries: string | [string] # registry or list of registries
hadolint
supports specifying the ignored rules using a configurationfile. The configuration file should be in yaml
format. This is onevalid configuration file as an example:
ignored:
- DL3000
- SC1010
Additionally, hadolint
can warn you when images from untrustedrepositories are being used in Dockerfiles, you can append thetrustedRegistries
keys to the configuration file, as shown below:
ignored:
- DL3000
- SC1010
trustedRegistries:
- docker.io
- my-company.com:5000
If you want to override the severity of specific rules, you can do that too:
override:
error:
- DL3001
- DL3002
warning:
- DL3042
- DL3033
info:
- DL3032
style:
- DL3015
failure-threshold
Exit with failure code only when rules with aseverity above THRESHOLD are violated (Available in v2.6.0+)
failure-threshold: info
warning:
- DL3042
- DL3033
info:
- DL3032
The global configuration file should be placed in the folderspecified by XDG_CONFIG_HOME
,with the name hadolint.yaml
or hadolint.yml
. In summary, thefollowing locations are valid for the configuration file, in orderor 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
Additionally, you can pass a custom configuration file in the command line withthe --config
option
hadolint --config /path/to/config.yaml Dockerfile
To pass a custom configuration file (using relative or absolute path) toa container, use the following command:
docker run --rm -i -v /your/path/to/hadolint.yaml:/.config/hadolint.yaml hadolint/hadolint < Dockerfile
# OR
docker run --rm -i -v /your/path/to/hadolint.yaml:/.config/hadolint.yaml ghcr.io/hadolint/hadolint < Dockerfile
It is also possible to ignore rules by adding a special comment directlyabove the Dockerfile statement for which you want to make an exception for.Such comments look like# hadolint ignore=DL3001,SC1081
. For example:
# hadolint ignore=DL3006
FROM ubuntu
# hadolint ignore=DL3003,SC1035
RUN cd /tmp && echo "hello!"
The comment "inline ignores" applies only to the statement following it.
Hadolint is able to check if specific labels are present and conformto a predefined label schema.First, a label schema must be defined either via the command line:
hadolint --require-label author:text --require-label version:semver Dockerfile
or via the config file:
label-schema:
author: text
contact: email
created: rfc3339
version: semver
documentation: url
git-revision: hash
license: spdx
The value of a label can be either of text
, url
, semver
, hash
orrfc3339
:
Schema | Description |
---|---|
text | Anything |
rfc3339 | A time, formatted according to RFC 3339 |
semver | A semantic version |
url | A URI as described in RFC 3986 |
hash | Either a short or a long Git hash |
spdx | An SPDX license identifier |
An email address conforming to RFC 5322 |
By default, Hadolint ignores any label that is not specified in the label schema. Towarn against such additional labels, turn on strict labels, using the command line:
hadolint --strict-labels --require-label version:semver Dockerfile
or the config file:
strict-labels: true
When strict labels is enabled, but no label schema is specified, hadolint
will warn if any label is present.
It is a common pattern to fill the value of a label not statically, but ratherdynamically at build time by using a variable:
FROM debian:buster
ARG VERSION="du-jour"
LABEL version="${VERSION}"
To allow this, the label schema must specify text
as value for that label:
label-schema:
version: text
To get most of hadolint
, it is useful to integrate it as a check in your CIor into your editor, or as a pre-commit hook, to lint your Dockerfile
as youwrite it. See our Integration docs.
An incomplete list of implemented rules. Click on the error code to get moredetailed information.
Rules with the prefix DL
are from hadolint
. Have a look atRules.hs
to find the implementation of the rules.
Rules with the SC
prefix are from ShellCheck (only the mostcommon rules are listed, there are dozens more).
Please create an issue if you have an idea for a good rule.
Rule | Default Severity | Description |
---|---|---|
DL3000 | Error | Use absolute WORKDIR. |
DL3001 | Info | 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 | Warning | Last user should not be root. |
DL3003 | Warning | Use WORKDIR to switch to a directory. |
DL3004 | Error | Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root. |
DL3005 | Error | Do not use apt-get dist-upgrade. |
DL3006 | Warning | Always tag the version of an image explicitly. |
DL3007 | Warning | Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag. |
DL3008 | Warning | Pin versions in apt-get install. |
DL3009 | Info | Delete the apt-get lists after installing something. |
DL3010 | Info | Use ADD for extracting archives into an image. |
DL3011 | Error | Valid UNIX ports range from 0 to 65535. |
DL3012 | Error | Multiple HEALTHCHECK instructions. |
DL3013 | Warning | Pin versions in pip. |
DL3014 | Warning | Use the -y switch. |
DL3015 | Info | Avoid additional packages by specifying --no-install-recommends. |
DL3016 | Warning | Pin versions in npm . |
DL3018 | Warning | Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version> . |
DL3019 | Info | Use the --no-cache switch to avoid the need to use --update and remove /var/cache/apk/* when done installing packages. |
DL3020 | Error | Use COPY instead of ADD for files and folders. |
DL3021 | Error | COPY with more than 2 arguments requires the last argument to end with / |
DL3022 | Warning | COPY --from should reference a previously defined FROM alias |
DL3023 | Error | COPY --from cannot reference its own FROM alias |
DL3024 | Error | FROM aliases (stage names) must be unique |
DL3025 | Warning | Use arguments JSON notation for CMD and ENTRYPOINT arguments |
DL3026 | Error | Use only an allowed registry in the FROM image |
DL3027 | Warning | Do not use apt as it is meant to be a end-user tool, use apt-get or apt-cache instead |
DL3028 | Warning | Pin versions in gem install. Instead of gem install <gem> use gem install <gem>:<version> |
DL3029 | Warning | Do not use --platform flag with FROM. |
DL3030 | Warning | Use the -y switch to avoid manual input yum install -y <package> |
DL3032 | Warning | yum clean all missing after yum command. |
DL3033 | Warning | Specify version with yum install -y <package>-<version> |
DL3034 | Warning | Non-interactive switch missing from zypper command: zypper install -y |
DL3035 | Warning | Do not use zypper dist-upgrade . |
DL3036 | Warning | zypper clean missing after zypper use. |
DL3037 | Warning | Specify version with zypper install -y <package>[=]<version> . |
DL3038 | Warning | Use the -y switch to avoid manual input dnf install -y <package> |
DL3040 | Warning | dnf clean all missing after dnf command. |
DL3041 | Warning | Specify version with dnf install -y <package>-<version> |
DL3042 | Warning | Avoid cache directory with pip install --no-cache-dir <package> . |
DL3043 | Error | ONBUILD , FROM or MAINTAINER triggered from within ONBUILD instruction. |
DL3044 | Error | Do not refer to an environment variable within the same ENV statement where it is defined. |
DL3045 | Warning | COPY to a relative destination without WORKDIR set. |
DL3046 | Warning | useradd without flag -l and high UID will result in excessively large Image. |
DL3047 | Info | wget without flag --progress will result in excessively bloated build logs when downloading larger files. |
DL3048 | Style | Invalid Label Key |
DL3049 | Info | Label <label> is missing. |
DL3050 | Info | Superfluous label(s) present. |
DL3051 | Warning | Label <label> is empty. |
DL3052 | Warning | Label <label> is not a valid URL. |
DL3053 | Warning | Label <label> is not a valid time format - must be conform to RFC3339. |
DL3054 | Warning | Label <label> is not a valid SPDX license identifier. |
DL3055 | Warning | Label <label> is not a valid git hash. |
DL3056 | Warning | Label <label> does not conform to semantic versioning. |
DL3057 | IgnoreC | HEALTHCHECK instruction missing. |
DL3058 | Warning | Label <label> is not a valid email format - must be conform to RFC5322. |
DL3059 | Info | Multiple consecutive RUN instructions. Consider consolidation. |
DL3060 | Info | yarn cache clean missing after yarn install was run. |
DL4000 | Error | MAINTAINER is deprecated. |
DL4001 | Warning | Either use Wget or Curl but not both. |
DL4003 | Warning | Multiple CMD instructions found. |
DL4004 | Error | Multiple ENTRYPOINT instructions found. |
DL4005 | Warning | Use SHELL to change the default shell. |
DL4006 | Warning | Set the SHELL option -o pipefail before RUN with a pipe in it |
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 [ .. ] or [[ .. ]] . |
|
SC1098 | Quote/escape special characters when using eval , e.g. eval "a=(b)" . |
|
SC1099 | You need a space before the # . |
|
SC2002 | Useless cat. Consider cmd < file | .. or cmd file | .. instead. |
|
SC2015 | Note that A && B || C 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. |
|
SC2039 | In POSIX sh, something is undefined. | |
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. | |
SC2155 | Declare and assign separately to avoid masking return values. | |
SC2164 | Use cd ... || exit in case cd fails. |
If you are an experienced Haskeller, we would be very grateful if you wouldtear our code apart in a review.
Clone repository
git clone --recursive git@github.com:hadolint/hadolint.git
Install the dependencies
stack install
The easiest way to try out the parser is using the REPL.
# start the repl
stack repl
# overload strings to be able to use Text
:set -XOverloadedStrings
# import parser library
import Language.Docker
# parse instruction and look at AST representation
parseText "FROM debian:jessie"
Run unit tests:
stack test
Run integration tests:
./integration_test.sh
Dockerfile syntax is fully described in the Dockerfile reference.Just take a look at Syntax.hs in the language-docker
project to seethe AST definition.
replicatedhq/dockerfilelint,the other linter used by the super-linter
RedCoolBeans/dockerlint
projectatomic/dockerfile_lint
Hadolint 是Haskell 实现的 Dockerfile linter,现依据Docker 官网推荐的 Dockerfile 最佳实践。 安装: alias hadolint='docker run --rm -i hub.baidubce.com/duanshuaixing/hadolint/hadolint:latest-alpine < ' [root@allinone ~]# h