I have a spring boot application that I'm running in a docker image via the extracted layered jar feature, with the following Dockerfile:

FROM openjdk:11.0.12-jre-slim as layers

WORKDIR layers
COPY build/libs/*.jar application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:11.0.12-slim

RUN mkdir /opt/spring
WORKDIR /opt/spring

COPY --from=layers layers/dependencies/ ./
COPY --from=layers layers/spring-boot-loader/ ./
COPY --from=layers layers/snapshot-dependencies/ ./
COPY --from=layers layers/application/ ./

ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

I also have an application.properties file in the classpath root, which looks something like this:

greeting=Hello from application.properties on the classpath
name=Alice

When I run a docker container with the docker image, I want to override one of these properties, so I create another application.properties file and mount it in the docker container in /opt/spring, the same location that the layered jar was extracted to:

name=Bob

However, when I do this and run the code that uses these properties, I get an error that says the greeting property has not been defined. After some trial and error, I discovered that the application.properties in the classpath root is being completely ignored if another application.properties is placed in the same directory that the layered jar has been extracted to.

This error does not occur if running the application as an unextracted fat jar, like so:

FROM openjdk:11-jdk-slim

RUN mkdir /opt/spring
WORKDIR /opt/spring
COPY ./build/libs/*.jar application.jar

ENTRYPOINT ["java", "-jar", "application.jar"]

While putting together a test case to submit, I did discover an easy workaround. Putting the properties file in a config directory instead of the current directory will fix the problem, but the documentation does state that placing an application.properties in the current directory is supported and should work.

I'm using spring boot version 2.5.4. Here's a link to a github repo that should allow anyone to easily reproduce the issue:

https://github.com/ericparton/spring-boot-layered-jar-properties-bug

Comment From: wilkinsona

The root cause of your problem is that /opt/spring is both the current working directory and the root of the classpath. This means that the config locations file:./ and classpath:/ both contain the same location, /opt/spring. classpath:/ also contains opt/spring/BOOT-INF/classes but this appears on the classpath after /opt/spring so, honouring this ordering, /opt/spring/application.properties is preferred to /opt/spring/BOOT-INF/classes/application.properties. The end result is that /opt/spring/BOOT-INF/classes/application.properties is ignored as both file:/ and classpath:/ resolve to opt/spring/application.properties instead.

Comment From: Two4

I appreciate the root cause analysis, which allows me to work around this issue, thank you @wilkinsona. This is unexpected behaviour though, and a bit of a gotcha. I would argue that this is a bug, or at least an wanting for enhancement. Would you consider reopening this issue?

Comment From: wilkinsona

I'm afraid that's unlikely to happen. This behaviour isn't a bug as it is working as per the documented locations from which external configuration is loaded:

Spring Boot will automatically find and load application.properties and application.yaml files from the following locations when your application starts: 1. From the classpath a. The classpath root b. The classpath /config package 2. From the current directory a. The current directory b. The config/ subdirectory in the current directory c. Immediate child directories of the config/ subdirectory

I don't think we can consider an enhancement to change this either as there are likely to be users relying on the current behavior. Changing it would require a configuration option to opt into or out of the new behavior and, given that the configuration option would config how configuration is loaded, there's no particularly easy way to do that.