- Notifications
You must be signed in to change notification settings - Fork437
Dockerfile linter, validate inline bash, written in Haskell
License
hadolint/hadolint
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A smarter Dockerfile linter that helps you buildbest practice Dockerimages. The linter parses the Dockerfile into an AST and performs rules ontop of the AST. It stands on the shoulders ofShellCheck to lintthe Bash code insideRUN
instructions.
🌐Check the online version onhadolint.github.io/hadolint
- How to use
- Install
- CLI
- Configure
- Non-Posix Shells
- Ignoring Rules
- Linting Labels
- Integrations
- Rules
- Develop
- Alternatives
You can runhadolint
locally to lint your Dockerfile.
hadolint<Dockerfile>hadolint --ignore DL3003 --ignore DL3006<Dockerfile># exclude specific ruleshadolint --trusted-registry my-company.com:500<Dockerfile># Warn when using untrusted FROM images
Docker comes to the rescue, providing an easy way how to runhadolint
on mostplatforms.Just pipe yourDockerfile
todocker run
:
docker run --rm -i hadolint/hadolint< Dockerfile# ORdocker run --rm -i ghcr.io/hadolint/hadolint< Dockerfile
or usingPodman:
podman run --rm -i docker.io/hadolint/hadolint< Dockerfile# ORpodman 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 usebrew to installhadolint
.
brew install hadolint
On Windows, you can usescoop toinstallhadolint
.
scoop install hadolint
On distributions that havenix
installed, you can use thehadolint
package to run ad-hoc shells or permanently installhadolint
intoyour environment.
As mentioned earlier,hadolint
is available as a container image:
docker pull hadolint/hadolint# ORdocker pull ghcr.io/hadolint/hadolint
If you need a container with shell access, use the Debian or Alpinevariants:
docker pull hadolint/hadolint:latest-debian# ORdocker pull hadolint/hadolint:latest-alpine# ORdocker pull ghcr.io/hadolint/hadolint:latest-debian# ORdocker pull ghcr.io/hadolint/hadolint:latest-alpine
You can also buildhadolint
locally. You needHaskell and thecabalbuild tool to build the binary.
git clone https://github.com/hadolint/hadolint \&&cd hadolint \&& cabal configure \&& cabal build \&& cabal install
If you want theVS Code Hadolintextension to use Hadolint in a container, you can use the followingwrapper script:
#!/bin/bashdockerfile="$1"shiftdocker run --rm -i hadolint/hadolint hadolint"$@" -<"$dockerfile"
hadolint --help
hadolint - Dockerfile Linter written in HaskellUsage: hadolint [-v|--version] [-c|--config FILENAME] [DOCKERFILE...] [--file-path-in-report FILEPATHINREPORT] [--no-fail] [--no-color] [-V|--verbose] [-f|--format ARG] [--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] [--disable-ignore-pragma] [-t|--failure-threshold THRESHOLD] Lint Dockerfile for errors and best practicesAvailable options: -h,--help Show this help text -v,--version Show version -c,--config FILENAME Path to the configuration file --file-path-in-report FILEPATHINREPORT The file path referenced in the generated report. This only applies for the 'checkstyle' format and is useful when running Hadolint with Docker to set the correct file path. --no-fail Don't exit with a failure status code when any rule is violated --no-color Don't colorize output -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 | gnu | codacy | sonarqube | sarif] (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` --disable-ignore-pragma Disable inline ignore pragmas `# hadolint ignore=DLxxxx` -t,--failure-threshold THRESHOLD Exit with failure code only when rules with a severity equal to or above THRESHOLD are violated. Accepted values: [error | warning | info | style | ignore | none] (default: info)
Configuration files can be used globally or per project.Hadolint looks for configuration files in the following locations or theirplatform specific equivalents in this order and uses the first one exclusively:
$PWD/.hadolint.yaml
$XDG_CONFIG_HOME/hadolint.yaml
$HOME/.config/hadolint.yaml
$HOME/.hadolint/hadolint.yaml or $HOME/hadolint/config.yaml
$HOME/.hadolint.yaml
In windows, the%LOCALAPPDATA%
environment variable is used instead ofXDG_CONFIG_HOME
. Config files can have eitheryaml
oryml
extensions.
hadolint
fullyaml
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 | gnu | codacy)ignored:[string]# list of ruleslabel-schema:# See Linting Labels below for specific label-schema detailsauthor:string# Your namecontact:string# email addresscreated:timestamp# rfc3339 datetimeversion:string# semverdocumentation:string# urlgit-revision:string# hashlicense:string# spdxno-color:boolean# true | falseno-fail:boolean# true | falseoverride:error:[string]# list of ruleswarning:[string]# list of rulesinfo:[string]# list of rulesstyle:[string]# list of rulesstrict-labels:boolean# true | falsedisable-ignore-pragma:boolean# true | falsetrustedRegistries:string | [string]# registry or list of registries
hadolint
supports specifying the ignored rules using a configurationfile. The configuration file should be inyaml
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 -SC1010trustedRegistries: -docker.io -my-company.com:5000 -"*.gcr.io"
If you want to override the severity of specific rules, you can do that too:
override:error: -DL3001 -DL3002warning: -DL3042 -DL3033info: -DL3032style: -DL3015
failure-threshold
Exit with failure code only when rules with aseverity above THRESHOLD are violated (Available in v2.6.0+)
failure-threshold:infooverride:warning: -DL3042 -DL3033info: -DL3032
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# ORdocker run --rm -i -v /your/path/to/hadolint.yaml:/.config/hadolint.yaml ghcr.io/hadolint/hadolint< Dockerfile
In addition to config files, Hadolint can be configured with environmentvariables.
NO_COLOR=1# Set or unset. See https://no-color.orgHADOLINT_NOFAIL=1# Truthy value e.g. 1, true or yesHADOLINT_VERBOSE=1# Truthy value e.g. 1, true or yesHADOLINT_FORMAT=json# Output format (tty | json | checkstyle | codeclimate | gitlab_codeclimate | gnu | codacy | sarif )HADOLINT_FAILURE_THRESHOLD=info# threshold level (error | warning | info | style | ignore | none)HADOLINT_OVERRIDE_ERROR=DL3010,DL3020# comma separated list of rule codesHADOLINT_OVERRIDE_WARNING=DL3010,DL3020# comma separated list of rule codesHADOLINT_OVERRIDE_INFO=DL3010,DL3020# comma separated list of rule codesHADOLINT_OVERRIDE_STYLE=DL3010,DL3020# comma separated list of rule codesHADOLINT_IGNORE=DL3010,DL3020# comma separated list of rule codesHADOLINT_STRICT_LABELS=1# Truthy value e.g. 1, true or yesHADOLINT_DISABLE_IGNORE_PRAGMA=1# Truthy value e.g. 1, true or yesHADOLINT_TRUSTED_REGISTRIES=docker.io# comma separated list of registry urlsHADOLINT_REQUIRE_LABELS=maintainer:text# comma separated list of label schema items
When using base images with non-posix shells as default (e.g. Windows basedimages) a special pragmahadolint shell
can specify which shell the base imageuses, so that Hadolint can automatically ignore all shell-specific rules.
FROM mcr.microsoft.com/windows/servercore:ltsc2022# hadolint shell=powershellRUN Get-Process notepad | Stop-Process
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=DL3006FROM ubuntu# hadolint ignore=DL3003,SC1035RUN cd /tmp && echo"hello!"
The comment "inline ignores" applies only to the statement following it.
Rules can also be ignored on a per-file basis using the global ignore pragma.It works just like inline ignores, except that it applies to the whole fileinstead of just the next line.
# hadolint global ignore=DL3003,DL3006,SC1035FROM ubuntuRUN cd /tmp && echo"foo"
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:textcontact:emailcreated:rfc3339version:semverdocumentation:urlgit-revision:hashlicense:spdx
The value of a label can be either oftext
,url
,semver
,hash
orrfc3339
:
Schema | Description |
---|---|
text | Anything |
rfc3339 | A time, formatted according toRFC 3339 |
semver | Asemantic version |
url | A URI as described inRFC 3986 |
hash | Either a short or a longGit hash |
spdx | AnSPDX license identifier |
An email address conforming toRFC 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:busterARG VERSION="du-jour"LABEL version="${VERSION}"
To allow this, the label schema must specifytext
as value for that label:
label-schema:version:text
To get most ofhadolint
, it is useful to integrate it as a check in your CIor into your editor, or as a pre-commit hook, to lint yourDockerfile
as youwrite it. See ourIntegration docs.
- Code Review Platform Integrations
- Continuous Integrations
- Editor Integrations
- Version Control Integrations
An incomplete list of implemented rules. Click on the error code to get moredetailed information.
Rules with the prefix
DL
are fromhadolint
. Have a look atRules.hs
to find the implementation of the rules.Rules with the
SC
prefix are fromShellCheck (only the mostcommon rules are listed, there are dozens more).
Pleasecreate an issue if you have an idea for a good rule.
Rule | Default Severity | Description |
---|---|---|
DL1001 | Ignore | Please refrain from using inline ignore pragmas# hadolint ignore=DLxxxx . |
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. |
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 inapt-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 | MultipleHEALTHCHECK 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 innpm . |
DL3018 | Warning | Pin versions inapk add . Instead ofapk add <package> useapk 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 | UseCOPY instead ofADD 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 definedFROM alias |
DL3023 | Error | COPY --from cannot reference its ownFROM 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 theFROM image |
DL3027 | Warning | Do not useapt as it is meant to be an end-user tool, useapt-get orapt-cache instead |
DL3028 | Warning | Pin versions in gem install. Instead ofgem install <gem> usegem install <gem>:<version> |
DL3029 | Warning | Do not use --platform flag with FROM. |
DL3030 | Warning | Use the-y switch to avoid manual inputyum install -y <package> |
DL3032 | Warning | yum clean all missing after yum command. |
DL3033 | Warning | Specify version withyum install -y <package>-<version> |
DL3034 | Warning | Non-interactive switch missing fromzypper command:zypper install -y |
DL3035 | Warning | Do not usezypper dist-upgrade . |
DL3036 | Warning | zypper clean missing after zypper use. |
DL3037 | Warning | Specify version withzypper install -y <package>[=]<version> . |
DL3038 | Warning | Use the-y switch to avoid manual inputdnf install -y <package> |
DL3040 | Warning | dnf clean all missing after dnf command. |
DL3041 | Warning | Specify version withdnf install -y <package>-<version> |
DL3042 | Warning | Avoid cache directory withpip install --no-cache-dir <package> . |
DL3043 | Error | ONBUILD ,FROM orMAINTAINER triggered from withinONBUILD instruction. |
DL3044 | Error | Do not refer to an environment variable within the sameENV statement where it is defined. |
DL3045 | Warning | COPY to a relative destination withoutWORKDIR 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 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 | Ignore | HEALTHCHECK instruction missing. |
DL3058 | Warning | Label<label> is not a valid email format - must conform to RFC5322. |
DL3059 | Info | Multiple consecutiveRUN instructions. Consider consolidation. |
DL3060 | Info | yarn cache clean missing afteryarn install was run. |
DL3061 | Error | Invalid instruction order. Dockerfile must begin withFROM ,ARG or comment. |
DL4000 | Error | MAINTAINER is deprecated. |
DL4001 | Warning | Either use Wget or Curl but not both. |
DL4003 | Warning | MultipleCMD instructions found. |
DL4004 | Error | MultipleENTRYPOINT instructions found. |
DL4005 | Warning | UseSHELL to change the default shell. |
DL4006 | Warning | Set theSHELL option -o pipefail beforeRUN 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, usevar='' ... ). | |
SC1010 | Use semicolon or linefeed beforedone (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 notfoo &; bar , justfoo & 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. Useif , notIf . | |
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 usingeval , e.g.eval "a=(b)" . | |
SC1099 | You need a space before the# . | |
SC2002 | Useless cat. Considercmd < file | .. orcmd file | .. instead. | |
SC2015 | Note thatA && 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. Considerprintf . | |
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 | Usecd ... || exit in casecd fails. |
If you are an experienced Haskeller, we would be very grateful if you wouldtear our code apart in a review.
To compile, you will need a recent Haskell environment andcabal-install
.
Clone repository
git clone --recursive git@github.com:hadolint/hadolint.git
Install dependencies and compile source
cabal configurecabal build
(Optional) Install Hadolint on your system
cabal install
The easiest way to try out the parser is using the REPL.
# start the replcabal repl# overload strings to be able to use Text:set -XOverloadedStrings# import parser libraryimport Language.Docker# parse instruction and look at AST representationparseText"FROM debian:jessie"
Compile with unit tests and run them:
cabal configure --enable-testscabal build --enable-testscabaltest
Run integration tests:
./integration_test.sh
Dockerfile syntax is fully described in theDockerfile reference.Just take a look atSyntax.hs in thelanguage-docker
project to seethe AST definition.
Hadolint uses many libraries to do the dirty work. In particular,language-docker is used to parse Dockerfiles and produce an AST which then canbe analyzed. To build Hadolint against a custom version of such libraries, dothe following. This example uses language-docker, but it would work with anyother library as well.
- In the same directory (e.g.
/home/user/repos
) clone Hadolint andlanguage-docker git repositories
cd /home/user/reposgit clone https://github.com/hadolint/hadolint.gitgit clone https://github.com/hadolint/language-docker.git
Make your modifications to language-docker
In the Hadolint repo, edit the
cabal.project
file, such that thepackages
property points to the other repo too
[...]packages:.../language-docker[...]
- Recompile Hadolint and run the tests
cd /home/user/repos/hadolintcabal configure --enable-testscabal build --enable-testscabaltest
replicatedhq/dockerfilelint,the other linter used by thesuper-linter
RedCoolBeans/dockerlint
projectatomic/dockerfile_lint
About
Dockerfile linter, validate inline bash, written in Haskell