Validate Dockerfiles With One Command

Docker is an excellent way of deploying software. But, how do you know if your build configurations (your Dockerfiles) are valid without building them? In this short tutorial, I’ll show you how.

Let's say that you've written the following Dockerfile to containerise one of your PHP applications.

# syntax=docker/dockerfile:1
ARG FPM_VERSION=alpine3.17
ARG PHP_VERSION=8.2.3

FROM php:${PHP_VERSION}-fpm-${FPM_VERSION} as base

RUN apk --update-cache \
    add \
    autoconf \
    gcc \
    icu-dev \
    libzip-dev \
    linux-headers \
    make \
    musl-dev \
    sqlite \
    && pecl install xdebug \
    && docker-php-ext-install intl zip \
    && docker-php-ext-enable xdebug intl zip \
    && rm -rf /var/lib/apt/lists/* /var/cache/apk/*

COPY --from=composer:2.2.7 /usr/bin/composer /usr/bin/

WORKDIR /var/www/html

# Set up the log file directory with appropriate permissions
RUN mkdir -p data/log \
    && chmod -R 777 data/log \
    && chown -R www-data.www-data data/log \
    && touch data/log/webhook.log \
    && chown -R www-data.www-data data/log/webhook.log \
    && chmod -R 777 data/log/webhook.log

There’s not a lot to it. It is based on the Alpine Linux, php-fpm version of the official Docker Hub PHP image. The Dockerfile:

  • Installs a number of dependencies, mainly to support running Composer
  • Copies Composer from version 2.2.7 of the official Docker Hub Composer image
  • Sets up the application’s directory structure, including assigning ownership of it to the www-data user and group

You have tests that validate your code, which you run before code is deployed. But, how do you validate a Dockerfile?

Well, available with Dockerfile 1.8 and Buildx version 0.15.0, the docker build command supports the --check option. It checks a Dockerfile against a number of criteria, providing the required validation.

Assuming that we were in the same directory as the Dockerfile above, we’d run the command below to validate it.

docker build --check .

It would produce output pretty similar to the following.

[+] Building 3.6s (6/6) FINISHED                                                              docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                          0.0s
 => => transferring dockerfile: 918B                                                                          0.0s
 => resolve image config for docker-image://docker.io/docker/dockerfile:1                                     1.8s
 => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd5  0.0s
 => => resolve docker.io/docker/dockerfile:1@sha256:fe40cf4e92cd0c467be2cfc30657a680ae2398318afd50b0c8058578  0.0s
 => [internal] load metadata for docker.io/library/composer:2.2.7                                             1.6s
 => [internal] load metadata for docker.io/library/php:8.2.3-fpm-alpine3.17                                   1.6s
 => [internal] load .dockerignore                                                                             0.0s
 => => transferring context: 2B                                                                               0.0s

WARNING: FromAsCasing - https://docs.docker.com/go/dockerfile/rule/from-as-casing/
'as' and 'FROM' keywords' casing do not match
Dockerfile:5
--------------------
   3 |     ARG PHP_VERSION=8.2.3
   4 |
   5 | >>> FROM php:${PHP_VERSION}-fpm-${FPM_VERSION} as base
   6 |
   7 |     RUN docker-php-ext-install opcache
--------------------

Toward the bottom of the output, you can see a warning. This indicates that the keywords in the FROM instruction use a mixture of upper and lower case. There are a number of other build checks that are performed, such as if there are duplicate stage names, undefined variables, and attempts to copy files ignored by .dockerignore.

Now, let’s assume that you updated the Dockerfile, correcting the mixed casing of the FROM instruction and ran the command again. You would then see output similar to the following, showing that no warnings were found.

[+] Building 0.9s (4/4) FINISHED                                                                                                                 docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                             0.0s
 => => transferring dockerfile: 340B                                                                                                                             0.0s
 => [internal] load metadata for docker.io/library/composer:2.3.5                                                                                                0.9s
 => [internal] load metadata for docker.io/library/php:8.3.9-fpm-alpine3.20                                                                                      0.9s
 => [internal] load .dockerignore                                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                                  0.0s
Check complete, no warnings found.

That’s how to validate Dockerfile

While it won’t guarantee that your build will work (though there is another command to do that) docker build --check is a good way to validate your Dockerfiles, a check that is almost trivial to integrate into your CI/CD build pipelines.