Fix an issue on my Dockerfile: ARG Scope in Multi-Stage Docker Build

manuartero

Manuel Artero Anguita 🟨

Posted on October 31, 2024

Fix an issue on my Dockerfile: ARG Scope in Multi-Stage Docker Build

OK, so this one had me stuck until I hit the Eureka moment,
the "Oooooohhhh, gotcha!" moment.

Context

Specifically this was for a web-app, we use a node:alpine as builder, install the dependencies and build the app; then use an nginx image and copy the static build to /usr/share/nginx/html.

I bet you have done similar.

For reference, the Dockerfile 🐳:

FROM node:20-alpine AS builder

ARG VERSION
WORKDIR /app

COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/bin /app/bin
COPY --from=builder /app/build /usr/share/nginx/html
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf

RUN echo "{\"version\": \"$VERSION\"}" > /usr/share/nginx/html/_v.json

CMD ["/app/bin/serve-prod"]
Enter fullscreen mode Exit fullscreen mode

Now, check this layer:

RUN echo "{\"version\": \"$VERSION\"}" > /usr/share/nginx/html/_v.json
Enter fullscreen mode Exit fullscreen mode

this is an specific use case, our infra rely on our services to expose this _v.json which is returning something like

{ "version": "2.11.0-ea34fd5" }
Enter fullscreen mode Exit fullscreen mode

this wasn't working,

The problem

Building the image like:

npm run build:image
# docker build --build-arg VERSION=$VERSION-$SHA -f ./Dockerfile -t our-project:$VERSION-$SHA ."
#...
#=> naming to docker.io/library/our-project:2.11.0-ea34fd5
Enter fullscreen mode Exit fullscreen mode

...and running the container:

docker run -it --rm our-project:2.11.0-ea34fd5 /bin/sh
/ # cat /usr/share/nginx/html/_v.json
# {"version": ""}
Enter fullscreen mode Exit fullscreen mode

😭😭 Where is my $VERSION 😭😭


Explanation

Each FROM instruction defines a new stage.

Any ARG variable set in each stage is only accessible within that stage. Visually:

ARG are defined in their own stage

So, when I tried using an ARG for a version string defined in the build stage, it wasn’t available in the nginx stage.


Two possible fixes

In this case, since the VERSION argument is only used in the nginx step, this just works:

FROM node:20-alpine AS builder

-ARG VERSION
WORKDIR /app

COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
+ARG VERSION
+
COPY --from=builder /app/bin /app/bin
COPY --from=builder /app/build /usr/share/nginx/html
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf

RUN echo "{\"version\": \"$VERSION\"}" > /usr/share/nginx/html/_v.json

CMD ["/app/bin/serve-prod"]
Enter fullscreen mode Exit fullscreen mode

An alternative: using ENV would promote the VERSION argument to env var:

FROM node:20-alpine AS builder

ARG VERSION
ENV VERSION=$VERSION
WORKDIR /app

COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/bin /app/bin
COPY --from=builder /app/build /usr/share/nginx/html
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf

RUN echo "{\"version\": \"$VERSION\"}" > /usr/share/nginx/html/_v.json

CMD ["/app/bin/serve-prod"]
Enter fullscreen mode Exit fullscreen mode

This time i went for moving the ARG declaration but both solutions work.

--
Thanks for reading.

💖 💪 🙅 🚩
manuartero
Manuel Artero Anguita 🟨

Posted on October 31, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related