DEV Community

Michel Sánchez Montells
Michel Sánchez Montells

Posted on • Edited on

Enhancing Dockerfile Flexibility: The Power of ARG Directives

ARG Directives: Enhancing Flexibility and Reducing Code Duplication

Docker is a powerful tool for containerization and deployment of applications. When creating Docker images, it's common to encounter scenarios where certain values need to be dynamic or configurable.

In this article, we'll explore how to improve the Dockerfile by leveraging ARG directives to make the Alpine version dynamic and avoid code duplication.

Let's start with the initial Dockerfile:

FROM alpine:3.18 as base

RUN echo "Alpine version 3.18"
RUN echo "Alpine version 3.18" > message.txt
CMD cat message.txt

FROM base as production

RUN echo "Alpine version 3.18"
RUN echo "Alpine version 3.18" > message.txt
CMD cat message.txt
Enter fullscreen mode Exit fullscreen mode

build and run

$> docker build -t base --target base .
[base 2/3] RUN echo "Alpine version 3.18"
[base 3/3] RUN echo "Apline version 3.18" > message.txt     
$> docker run base
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

If we do similar build an run with target production then

$> docker build -t production --target production .
[production 1/2] RUN echo "Alpine version 3.18" 
[production 2/2] RUN echo "Apline version 3.18" > message.txt
$> docker run production
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

In the above Dockerfile, the Alpine version is hard-coded as 3.18, which leads to code duplication and lack of flexibility. We can improve this step by step using ARG directives.

Step 1: Dynamic Alpine Version in the FROM Directive

We can use the ARG directive to define an argument that can be expanded during the build process. Let's modify the first two lines as follows:

ARG ALPINE_VERSION
FROM alpine:${ALPINE_VERSION} as base
Enter fullscreen mode Exit fullscreen mode

Now, during the Docker build command, we can pass the value for the ALPINE_VERSION argument using the --build-arg flag:

docker build -t base --target base --build-arg ALPINE_VERSION=3.18 .
Enter fullscreen mode Exit fullscreen mode

This allows us to have a more flexible image where the Alpine version can be configured at build time.

Step 2: Default Value for Dynamic Alpine Version

We can provide a default value for the ALPINE_VERSION argument by modifying the first line as follows:

ARG ALPINE_VERSION='3.18'
Enter fullscreen mode Exit fullscreen mode

Now, if the ALPINE_VERSION argument is not provided during the Docker build command, the default value will be used:

docker build -t base --target base .
docker run base
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

Notice that the value 3.18 is used even when was not provided in the docker build command.

$> docker build -t base --target base --build-arg ALPINE_VERSION=3.17 .
[internal] load metadata for docker.io/library/alpine:3.17
Enter fullscreen mode Exit fullscreen mode

Our build process use the value provided in the docker build command using the --build-arg flag

Step 3: Using the Same Value Inside a Build Stage

In the original Dockerfile, the ARG ALPINE_VERSION was not expanded inside the build stage. To make it accessible, we need to duplicate its definition inside the build stage. Modify the Dockerfile as follows:

ARG ALPINE_VERSION='3.18'
FROM alpine:${ALPINE_VERSION} as base

ARG ALPINE_VERSION
RUN echo "Alpine version ${ALPINE_VERSION}"
RUN echo "Alpine version ${ALPINE_VERSION}" > message.txt
Enter fullscreen mode Exit fullscreen mode

Now, the ARG ALPINE_VERSION is accessible inside the build stage, and the default value is also available:

$> docker build -t base --target base .
[base 2/3] RUN echo "Alpine version 3.18"
[base 3/3] RUN echo "Apline version 3.18" > message.txt 
$> docker run base
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

IMPORTANT: The line ARG ALPINE_VERSION . The ARG must be re-defined(duplicated) inside the build stage

Step 4: Using the Same Value Inside Different Build Stages

When Inherits

If build stages inherit from each other, they can access the arguments defined in their parent stage. Modify the Dockerfile as follows:

ARG ALPINE_VERSION='3.18'

FROM alpine:${ALPINE_VERSION} as base
ARG ALPINE_VERSION
RUN echo "Alpine version ${ALPINE_VERSION}"
RUN echo "Alpine version ${ALPINE_VERSION}" > message.txt
CMD cat message.txt

FROM base as production
RUN echo "Alpine version ${ALPINE_VERSION}"
RUN echo "Alpine version ${ALPINE_VERSION}" > message.txt
CMD cat message.txt
Enter fullscreen mode Exit fullscreen mode

In this case, the build stage "production" inherits from "base," so it can access the arguments defined in the "base" stage without duplicating their definitions. The same value of ALPINE_VERSION will be used in both stages.

$> docker build -t production --target production .
=> CACHED [base 2/3] RUN echo "Alpine version 3.18"
=> CACHED [base 3/3] RUN echo "Apline version 3.18" > message.txt
=> [production 1/2] RUN echo "Alpine version 3.18" 
=> [production 2/2] RUN echo "Apline version 3.18" > message.txt
$> docker run production
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

When no Inherits

If a build stage does not inherit from another stage, the arguments need to be duplicated inside that stage for access.

ARG  ALPINE_VERSION='3.18'

FROM  alpine:${ALPINE_VERSION}  as  base
ARG  ALPINE_VERSION
RUN  echo  "Alpine  version  ${ALPINE_VERSION}"
RUN  echo  "Apline  version  ${ALPINE_VERSION}"  >  message.txt
CMD  cat  message.txt

FROM  alpine:${ALPINE_VERSION}  as  staging
ARG  ALPINE_VERSION
RUN  echo  "Alpine  version  ${ALPINE_VERSION}"
RUN  echo  "Apline  version  ${ALPINE_VERSION}"  >  message.txt
CMD  cat  message.txt
Enter fullscreen mode Exit fullscreen mode

ouput:

$> docker build -t staging --target staging .
=>[staging 3/3] RUN echo "Apline version 3.18"
> message.txt
$> docker run staging
Apline version 3.18
Enter fullscreen mode Exit fullscreen mode

Notice: re-duplicate ARG ALPINE_VERSION

Summary

By using ARG directives, we can avoid code duplication and build more flexible Docker images.

  • ARGs defined outside build stages (before FROM) are accessible only in the lines with the FROM directive.
  • ARGs defined inside a build stage are accessible only within that stage and its child stages.
  • To access the value of an ARG defined outside from inside a build stage, duplicate its definition in the build stage.

Top comments (0)