We use cookies to understand how people use Depot.
🚀 Introducing Ultra Runners — Up to 3x faster GitHub Actions jobs
← All Posts

Lint a Dockerfile on every build

Written by
kyle
Kyle Galbraith
Published on
31 May 2023
We're excited to announce that you can now lint your Dockerfile on every build with Depot! No more configuring a separate Dockerfile linter outside or before your build in CI.
Lint a Dockerfile on every build banner

Now available in depot v.2.17.0 is the ability to lint a Dockerfile on any Docker image build you do with Depot. This means you can now run a Dockerfile lint on every build with Depot! No more configuring a Dockerfile linter outside of your actual image build.

How it works

The new Dockerfile linter uses the popular open-source project, hadolint, which helps you build best practice Docker images. It's a fast, lightweight, and easy-to-use linter already used in the Docker community. We're also very excited to be sponsoring the project.

Dockerfile linting with Depot

To use the new Dockerfile linter, you can add the --lint flag to your depot build or depot bake commands:

depot build --lint .
[+] Building 3.9s (16/16) FINISHED
 => [depot] launching arm64 builder                                                                                                                                                          0.5s
 => [depot] connecting to arm64 builder                                                                                                                                                      0.4s
 => [lint]                                                                                                                                                                                   1.3s
 => => INFO Dockerfile:7 DL3059: Multiple consecutive `RUN` instructions. Consider consolidation.                                                                                            1.3s
 => [internal] load build definition from Dockerfile                                                                                                                                         0.3s
 => => transferring dockerfile: 436B                                                                                                                                                         0.3s
 => [internal] load .dockerignore                                                                                                                                                            0.3s
 => => transferring context: 116B                                                                                                                                                            0.3s
 => [internal] load metadata for docker.io/library/node:16-alpine                                                                                                                            0.4s
 => [build 1/6] FROM docker.io/library/node:16-alpine@sha256:f1657204d3463bce763cefa5b25e48c28af6fe0cdb0f68b354f0f8225ef61be7                                                                0.0s
 => => resolve docker.io/library/node:16-alpine@sha256:f1657204d3463bce763cefa5b25e48c28af6fe0cdb0f68b354f0f8225ef61be7                                                                      0.0s
 => [internal] load build context                                                                                                                                                            0.2s
 => => transferring context: 421B                                                                                                                                                            0.2s
 => CACHED [build 2/6] WORKDIR /app                                                                                                                                                          0.0s
 => CACHED [build 3/6] COPY package.json yarn.lock tsconfig.json ./                                                                                                                          0.0s
 => CACHED [build 4/6] COPY src/ ./src/                                                                                                                                                      0.0s
 => CACHED [build 5/6] RUN yarn install --immutable                                                                                                                                          0.0s
 => CACHED [build 6/6] RUN yarn build                                                                                                                                                        0.0s
 => CACHED [stage-1 3/5] COPY --from=build /app/node_modules /app/node_modules                                                                                                               0.0s
 => CACHED [stage-1 4/5] COPY --from=build /app/dist /app/dist                                                                                                                               0.0s
 => CACHED [stage-1 5/5] COPY gifs-to-upload/ dist/gifs-to-upload/                                                                                                                           0.0s
WARN[0001] No output specified with depot driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
 
 1 linter issue found:
INFO Dockerfile:7 Multiple consecutive `RUN` instructions. Consider consolidation.
  More info: https://github.com/hadolint/hadolint/wiki/DL3059
 
  --------------------
     5 |     COPY src/ ./src/
     6 |     RUN yarn install --immutable
     7 | >>> RUN yarn build
     8 |
     9 |     FROM node:16-alpine
  --------------------

The linter runs before the build starts. It outputs any lint errors, warnings, or info messages at the end of the Docker image build. For example, we see here it's complaining about my Docker image build having back-to-back run instructions.

The Dockerfile linting doesn't fail the build by default. But, you can enable this behavior via the --lint-fail-on flag:

depot build --lint --lint-fail-on info .
[+] Building 2.6s (3/3) FINISHED
 => [depot] launching arm64 builder                                                                                                                                                          0.6s
 => [depot] connecting to arm64 builder                                                                                                                                                      0.4s
 => [lint]                                                                                                                                                                                   1.3s
 => => INFO Dockerfile:7 DL3059: Multiple consecutive `RUN` instructions. Consider consolidation.                                                                                            1.3s
WARN[0001] No output specified with depot driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
 
 1 linter issue found:
INFO Dockerfile:7 Multiple consecutive `RUN` instructions. Consider consolidation.
  More info: https://github.com/hadolint/hadolint/wiki/DL3059
 
  --------------------
     5 |     COPY src/ ./src/
     6 |     RUN yarn install --immutable
     7 | >>> RUN yarn build
     8 |
     9 |     FROM node:16-alpine
  --------------------

Here we can see that the build failed because we enabled the --lint-fail-on flag with the info level. This value can be info, warning, or error depending on your goal.

Linting a Dockerfile in CI

If you're not using GitHub Action for continuous integration, you can use the new flags as shown above.

For GitHub Actions, we've updated our depot/build-push-action and depot/bake-action actions to support these new flags. You can enable the Dockerfile linter by setting the lint input to true:

Here is an example of linting a Dockerfile using our build-push-action. It does a git clone, sets up our CLI, and builds the docker image with Dockerfile linting enabled:

jobs:
  build:
    name: Build
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout repo
        uses: actions/checkout@v3
 
      - uses: depot/setup-action@v1
 
      - name: Bake Docker images
        uses: depot/build-push-action@v1
        with:
          lint: true
          lint-fail-on: error

Dockerfile lint functionality works with our bake-action as well. This allows you to build best practice docker images for all images in a build definition file. See our post on building many images via bake for more details.

jobs:
  build:
    name: Build
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout repo
        uses: actions/checkout@v3
 
      - uses: depot/setup-action@v1
 
      - name: Bake Docker images
        uses: depot/bake-action@v1
        with:
          lint: true
          lint-fail-on: error

Why we built this

Linting a Dockerfile is a great way to catch errors during static analysis. It improves the quality of your images and helps you build best practice Docker images. It's one of those best practices that improve code quality and catches mistakes during a code review. But it's an extra hop away today. You have to run hadolint dockerfile outside your actual build process.

With Depot, we are already providing you with a faster experience for building your Docker container, so adding the ability to lint your Dockerfile on every build was a natural next step for us. We want to make it as easy as possible for you to build high-quality Docker images as fast as possible, and this is an excellent step in that direction.

Your builds have never been this quick.
Start building