Docker Community Forums

Share and learn in the Docker community.

Access ARG between stages in Dockerfile

How can I make an ARG available between to different stages in the Dockerfile?

I want the JAVA_HOME variable from the jdk11 to use within the base.

FROM adoptopenjdk:11-jdk-hotspot AS jdk11
ARG JDK11=${JAVA_HOME}

FROM opensuse/leap:15.1 AS base
ARG JDK11
COPY --from=jdk11 ${JDK11} /usr/java/jdk-11
ENV JAVA_HOME=/usr/java/jdk-11

I need to access the JAVA_HOME from the jdk11 stage within the base stage.

According to the documentation the ARG must be defined at both stages, but it does not work.

ARG are also known as build-time variables. They are only available from the moment they are ‘announced’ in the Dockerfile with an ARG instruction up to the moment when the image is built. Running containers can’t access values of ARG variables.

I am not interested in keeping the JDK11 beyond building the image.
I need the JAVA_HOME from the jdk11 image as an argument within the build process.

It is needed for the path of the Java installation I am using in the COPY statement.

I found an explanation on Docker GitHub that might explain what I want is not possible.

Basically, after the FROM instruction all the build arguments are reset and thus aren’t available in the Dockerfile.

Anyone have an idea how I can work around this?

I need the JAVA_HOME environment variable taken in FROM jdk11 into the FROM base.

Would ENV work instead for what I am trying to do.

I must admit, I didn’t sport the problem in the first place. I thought you are passing in JAVA_HOME as --build-arg to your build. Though, what you want is to reuse the ENV from the the stage jdk11 in your base stage.

Why don’t you just move ${JAVA_HOME} to a fixed folder like /java in the first stage and use this folder in the second stage?

Basicly you want to copy the jdk from the first stage into the second. I am not sure if this realy is a good idea, since depending libraries might not be 100% compatible with those from opensuse/leap.

The problem is on Windows they use the full JDK version in the JAVA_HOME, and for JDK-11 that might change depending on the build. Though AdoptOpenJDK has adopted an easier approach for Linux by using /opt/java/openjdk for JAVA_HOME.

I could copy JAVA_HOME to temporary directory in the first stage. That could work. I did not think of that. Thanks for the suggestion.

JDK does not have any depending libraries on the distribution. The AdoptOpenJDK are using Ubuntu, but the JDK is usable among all distributions. If you download the OpenJDK it is an archive you can unpack. Most distributions has the OpenJDK in its distribution, OpenSUSE sure does, but we have settled for using AdoptOpenJDK across all systems (good to be consisten among Linux, Mac and Windows builds).

I could though download the OpenJDK archive directly from AdoptOpenJDK through their API, but it looked easier to use their docker image to copy from.
Perhaps additional logic in the Dockerfile to copy OpenJDK to a temporary directory, using an archive from AdoptOpenJDK API would now be just as simple, less logic.

If AdoptOpenJDK only privides a generic archive, then there should be no problem with this approch. It is different, but still valid :slight_smile:

You mean, you want to move the JDK in the first stage to a temporary folder and use this folder in the second stage like I suggest?

A temporary directory might be the solution, if I still want to use the OpenJDK docker image.

So, It is not possible to deliver information from one stage to another in the Dockerfile during build?

FROM stage1

FROM stage2

This is what I ended up doing:

ARG JAVA_TMP=/tmp/java
FROM adoptopenjdk:11-jdk-hotspot AS jdk11
ARG JAVA_TMP
RUN cp -R ${JAVA_HOME} ${JAVA_TMP}

FROM opensuse/leap:15.1 AS opensuse
ENV JAVA_HOME=/usr/java/jdk-11
COPY --from=jdk11 ${JAVA_TMP} ${JAVA_HOME}

After I got this working, I was thinking it was pretty close to what I wanted.

ARG JDK11_PATH
ARG JDK14_PATH

FROM adoptopenjdk:11-jdk-hotspot AS java11
ARG JDK11_PATH=${JAVA_HOME}

FROM adoptopenjdk:14-jdk-hotspot AS java14
ARG JDK14_PATH=${JAVA_HOME}

FROM opensuse/leap:15.1 AS opensuse
ENV JAVA_HOME=/usr/java/jdk-11
COPY --from=java11 ${JDK11_PATH} ${JAVA_HOME}
COPY --from=java11 ${JDK14_PATH} /usr/java/jdk-14

Which works. I thought previosly this didn’t work, because RUN echo $JDK11_PATH prints out empty under the opensuse stage.

It absolutely does work, I use it often. In your Dockerfiles above though, you are passing a value in JAVA_HOME for the first stage but not for the second, therefore it is empty. The build args for each stage are independent, they don’t get copied in the way that I suspect you think they do, but they can be assigned the same value. So you need to re-declare args for each stage.

The reference in the docs to declaring args before the FROM is really talking about the first FROM in your multi-stage build. Many people, including me declare things like the image repo and tag and then reference those args in the actual FROM statement itself. That’s the primary use case AFAIK.

My Dockerfile did not work. I did not inspect the image properly.
=> [opensuse 2/3] COPY --from=java11 /usr/java/jdk-11
=> [opensuse 3/3] COPY --from=java14 /usr/java/jdk-14
The source was actually empty.

I need the value of the JAVA_HOME from both the first stages into the target stage.

I used to hard code it since it was /opt/java/openjdk. If this would change later in AdoptOpenJDK my Dockerfile will no longer build.

The larger problem is Windows where JAVA_HOME does not have such a simple directory path.
C:\Program Files\AdoptOpenJDK\jdk-11.0.7.10-hotspot
The JDK 11 Docker image could then have different JAVA_HOME when they build a new JDK 11 release.

I could have use wildcard in copy, but that does not work on Windows.

COPY "/opt/java/*jdk" /usr/java/jdk-11
COPY [ "C:\Program Files\AdoptOpenJDK\jdk*" "C:\Program Files\Java\jdk-11" ]

If I cannot find a solution to this, I would instead of using the AdoptOpenJDK docker images, download the AdoptOpenJDK archive and unpack it.