Why gradle does not use cache in docker

My Dockerfile:

FROM gradle:jdk8 as gradle
WORKDIR /app

COPY settings.gradle build.gradle ./
COPY module-core/build.gradle ./module-core/
COPY gradle ./gradle
RUN gradle build --no-daemon --info --stacktrace  #make gradle download dependencies 
COPY . .
RUN gradle :module-core:bootJar --no-daemon --info --stacktrace #should reuse the dependencies

As shown I copy the gradle configuration files without the source codes to make gradle download the dependencies, and I can make sure that gradle download a lot of dependencies through the log.

Then I copy the source codes and run the build again, I expect that gradle will use the cached depedencies , however I found it download them again.

What’s going on?

Because the Gradle cache lives in the ~/.gradle on the host. When running in a Docker container that folder is going to be empty. You could mount that Gradle user home directory into the Docker container, but there are complications with that and it isn’t fully supported. However, there is preliminary work which will be available in Gradle 6.1 to make this possible.

When running in a Docker container that folder is going to be empty

At the first build time it is ok if the folder is empty, but after the first build. Docker should reuse the cached layer where the ~/.gradle should contain the jars.

This is only the case if you run multiple builds in the same container, or use persistent volumes between containers, otherwise, everything I spin up a new container that folder will be empty.

But why the following work:

FROM node:10-alpine as node
WORKDIR /app
COPY package*.json ./
RUN npm install 
ADD . .
RUN npm run build

The last line npm run build will reuse the node dependencies downloaded in the step npm install.

The only difference is that the node dependencies will be saved in the current directory while gradle dependencies will be put outside the current directory.

Ok, I think I understand what you are referring to now. You are indeed running multiple builds in the same container, and the later builds still download dependencies? Could you perhaps run those builds with --scan and share the build scan URLs?

Sorry but I can not paste the build scan because of company restriction.

But I tried to stop the build after the gradle build complete.

FROM gradle:5.6.4-jdk8 as gradle
WORKDIR /app

COPY settings.gradle build.gradle ./
COPY module-core/build.gradle ./module-core/
COPY gradle ./gradle
RUN gradle build --no-daemon --info --stacktrace --scan
# RUN ....

Then use docker run to go inside the container where I expect to find a lot of jars in /root/.gradle, however the directory is empty.

Then I guess the following build steps have to down the files again and again.

It’s possible there are different semantics regarding user home directories when build a Docker image vs running a container.

One thing you might try is using an explicit Gradle user home directory by passing a location via -g. Try using the same location for both build invocations and see if that helps.

The image has VOLUME /home/gradle/.gradle. This was done because CI containers tend to be ephemoral and this gives you the option to mount them in a volume so it can be re-used between containers (and generally child images are meant to be a non-project specific image that is used for many projects, and thus avoids having dependencies for any particular project in the image).

However, it is not currently possible to unset this decision in child images, so if you want the downloaded dependencies included in the image, the fix would be to choose another home directory as @mark_vieira has suggested . mrhaki describes some methods for doing that here: https://mrhaki.blogspot.com/2010/09/gradle-goodness-changing-gradle-user.html.