Optimize Quarkus JVM Container Image Size
After creating a new Quarkus project from the default template, the new project already has four Dockerfile
s to build container images using Docker. These Dockerfile
s are in the directory src/main/docker
. The file Dockerfile.jvm
is used to build container images for Quarkus apps running in JVM mode.
Dockerfile.jvm
uses RedHat's UBI as the base image. The actual base image is ubi8/ubi-minimal
. Based on the base image, java-11-openjdk-headless
is installed using microdnf
.
The result container image is quite big. The sample app used for testing is a simple app generated from code.quarkus.io with extensions resteasy
and resteasy-jackson
.
Size of the built image is 520MB
. We can use dive to check the layers in the image.
From the screen-shot below, we can see that the installation of java-11-openjdk-headless
contributes a layer with size 402MB
. That's too big.
A better choice is to use OpenJDK Alpine base image. The code below is the Dockerfile
that uses Eclipse Temurin JRE 11 alpine image as the base image.
FROM curlimages/curl AS downloader
ARG RUN_JAVA_VERSION=1.3.8
RUN curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /tmp/run-java.sh
# FROM eclipse-temurin:11-jdk-alpine
FROM eclipse-temurin:11-jre-alpine
RUN mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments
COPY /tmp/run-java.sh /deployments/run-java.sh
RUN chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY target/quarkus-app/lib/ /deployments/lib/
COPY target/quarkus-app/*.jar /deployments/
COPY target/quarkus-app/app/ /deployments/app/
COPY target/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]
This Dockerfile
is named Dockerfile.jvm-alpine
. We can update the property quarkus.docker.dockerfile-jvm-path
with path of this new Dockerfile
.
$ quarkus build -Dquarkus.docker.dockerfile-jvm-path=src/main/docker/Dockerfile.jvm-alpine
Below is the screen-shot of the new image. As you can see, the result image size is only 149MB
.
We can also use JDK base image eclipse-temurin:11-jdk-alpine
instead of JRE. The result image size is 342MB
.
The table below shows the differences of container image sizes.
Base Image | Size (MB) | Difference |
---|---|---|
UBI minimal | 520 | N/A |
Alpine JRE | 149 | 28.65% |
Alpine JDK | 342 | 65.77% |
As you can see, using Alpine JRE/JDK base image can save a lot of spaces.