Fix an issue on my Dockerfile: ARG Scope in Multi-Stage Docker Build
Manuel Artero Anguita 🟨
Posted on October 31, 2024
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"]
Now, check this layer:
RUN echo "{\"version\": \"$VERSION\"}" > /usr/share/nginx/html/_v.json
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" }
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
...and running the container:
docker run -it --rm our-project:2.11.0-ea34fd5 /bin/sh
/ # cat /usr/share/nginx/html/_v.json
# {"version": ""}
😭😭 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:
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"]
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"]
This time i went for moving the ARG
declaration but both solutions work.
--
Thanks for reading.
Posted on October 31, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.