Custom JRE for spring-boot app (Java 20)

Ziga GZiga G
3 min read

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!

0
Subscribe to my newsletter

Read articles from Ziga G directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ziga G
Ziga G