Docker ARGs
Today I realised that there is more depth to the ARG
docker command than I had anticipated. Especially its scope in the context of multi-stage builds is non-trivial. Take the following (highly convoluted) Dockerfile for example:
ARG VERSION=latest FROM alpine:$VERSION as build ARG VERSION=3.16 RUN echo $VERSION > image_version FROM alpine:$VERSION COPY --from=build /image_version /other_image_version ARG VERSION RUN echo $VERSION > image_version CMD cat other_image_version && cat image_version
The first stage will use alpine:latest
as base image. VERSION
then gets overwritten to 3.16
and this value is saved to file image_version
.
Which alpine
tag will the second stage use? Because ARG
commands within a stage are local to that stage, the ARG VERSION=3.16
line has no effect on this FROM
statement. Instead, the previous value latest
will be used.
This second stage retrieves the version file from the first stage and also creates its own image_version
file. Which value will be written to this file? We defined an argument VERSION
without value in this stage, but that does not mean that the argument will be empty. Rather, this line allows the ARG VERSION=latest
statement on line 1 to enter the scope of the second stage. As a consequence, the value latest
will be written to the image_version
file. If we had skipped the ARG VERSION
line, $VERSION
would have been undefined and the image_version
file would have been empty.
Let's confirm our theory:
$ docker build -t argtest . [+] Building 1.0s (8/8) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 320B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/alpine:latest 0.0s => [build 1/2] FROM docker.io/library/alpine:latest 0.0s => [build 2/2] RUN echo 3.16 > image_version 0.3s => [stage-1 2/3] COPY --from=build /image_version /other_image_version 0.0s => [stage-1 3/3] RUN echo latest > image_version 0.5s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:667654e8ba78edf07a9d64a3fb7576fdfb6a4be421b... 0.0s => => naming to docker.io/library/argtest 0.0s $ docker run --rm argtest 3.16 latest
When we manually specify a value for the build argument, it is applied everywhere:
$ docker build -t argtest --build-arg VERSION=edge . $ docker run --rm argtest edge edge
Fun fact
ARG
s are not secret. The values passed during docker build
can be retrieved from an image with the docker history
command. Do not use ARG
s to pass sensitive information such as passwords. Use RUN --mount=type=secret
instead.
Summary
-
ARG
s defined before the first stage- can only be used in
FROM
statements - can be imported in the scope of a stage by re-declaring them without value
- can only be used in
-
ARG
s defined within a stage- are scoped to the subsequent lines of that stage
- shadow any outside
ARG
s with the same name
-
ARG
s are unsuited for secrets