Custom JRE for spring-boot app (Java 20)
I start with a very simple spring-boot project generated by Spring Initializr.
Inpom.xml
I've got spring-boot-starter-parent
as parent and java.version
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/>
</parent>
<groupId>foo.bar</groupId>
<artifactId>demo-custom-jre</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>20</java.version>
</properties>
...
Does it start? Yes!
mvn spring-boot:run
...
[INFO] --- spring-boot-maven-plugin:3.0.5:run (default-cli) @ demo-custom-jre ---
[INFO] Attaching agents: []
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.5)
INFO [ main] c.z.d.d.DemoCustomJreApplication : Starting DemoCustomJreApplication using Java 20 with PID 58559 (/Users/ziga/git/demo-custom-jre/target/classes started by ziga in /Users/ziga/git/demo-custom-jre)
...
INFO [ main] c.z.d.d.DemoCustomJreApplication : Started DemoCustomJreApplication in 0.664 seconds (process running for 0.829)
[CTRL-C]
In Spring Boot guide https://spring.io/guides/topicals/spring-boot-docker/ thet start with a basic Dockerfile
FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
I could use it to build and start the app
./mvnw package
docker build --build-arg JAR_FILE=target/*.jar -t foo .
docker run -p 8080:8080 foo
but since I want to be on Java 20 I need a Docker Image with JDK/JRE version 20. Or I build a Custom JRE for my app.
I want to keep it simple, so everything shall be done in a Dockerfile. Multi-stage Docker build helps me to segregate steps a bit:
compile and build the app
identify the required Java modules
build Custom JRE
pack everything together
# Build the app.jar
FROM azul/zulu-openjdk:20 as app
ENV HOME=/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
ADD . $HOME
RUN ./mvnw package -DskipTests
# Use 'jdeps' to generate list of required modules
FROM azul/zulu-openjdk:20 as deps
WORKDIR /usr/app
COPY --from=app /usr/app/target/*.jar app.jar
RUN set -eux; \
"$JAVA_HOME"/bin/jar -xf app.jar && \
"$JAVA_HOME"/bin/jdeps \
--ignore-missing-deps \
-q \
--multi-release 20 \
--recursive \
--print-module-deps \
--class-path="./BOOT-INF/lib/*" \
--module-path="./BOOT-INF/lib/*" \
./app.jar > jre-deps.info
# Build Custom JRE
FROM azul/zulu-openjdk:20 as builder
WORKDIR /usr/app
COPY --from=deps /usr/app/jre-deps.info jre-deps.info
RUN set -eux; \
"$JAVA_HOME"/bin/jlink --verbose \
--compress 2 \
--strip-java-debug-attributes \
--no-header-files \
--no-man-pages \
--output jre \
--add-modules "$(cat jre-deps.info)"
# Build final image with app.jar and JRE from earlier stages
FROM ubuntu:20.04
ENV JAVA_HOME /opt/java/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
WORKDIR /usr/app
EXPOSE 8080
COPY --from=builder /usr/app/jre $JAVA_HOME
COPY --from=deps /usr/app/app.jar app.jar
ENTRYPOINT ["java","-jar", "app.jar"]
Quick test, does it build?
docker build -t foo .
docker run -p 8080:8080 foo
Yeah! My app starts. Custom JRE for my app is small, just below 60 MB.
docker history 711bd2638b5e
IMAGE CREATED CREATED BY SIZE COMMENT
...
<missing> 37 minutes ago COPY /usr/app/jre /opt/java/openjdk # buildk… 58.5MB buildkit.dockerfile.v0
...
What is the size of JDK/JRE in your Docker image?
Thanks!
Note: The code and configuration shown above are oversimplified and not production-ready!
Subscribe to my newsletter
Read articles from Ziga G directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by