- Custom Runtime Images with `jlink`
This guide describes how to build a custom runtime image for your Helidon application using Helidon’s support for the JDK’s
jlinktool.
Introduction
JDK 9 introduced the jlink command that supports assembling a set of modules and their dependencies into a custom runtime image. The helidon-maven-plugin has support for easily creating a custom runtime image for your Helidon application resulting in a smaller, better performing runtime.
In this guide you will learn how to build a custom runtime image locally on your machine, as well as how to build it in a Docker image.
What You Need
For this 10 minute tutorial, you will need the following:
| Java SE 21 (Open JDK 21) | Helidon requires Java 21+ (25+ recommended). |
| Maven 3.8+ | Helidon requires Maven 3.8+. |
| Docker 18.09+ | If you want to build and run Docker containers. |
| Kubectl 1.16.5+ | If you want to deploy to Kubernetes, you need kubectl and a Kubernetes cluster (you can install one on your desktop). |
java -version
mvn --version
docker --version
kubectl version# On Mac
export JAVA_HOME=`/usr/libexec/java_home -v 21`
# On Linux
# Use the appropriate path to your JDK
export JAVA_HOME=/usr/lib/jvm/jdk-21Verify JDK
As noted in the prerequisites above, Java 21 or newer is required (Java 25 or newer is recommended).
$JAVA_HOME/bin/java --versionCreating a custom runtime image requires that the JDK modules are present as *.jmod files, and some distributions do not provide them by default. Check the jmods directory to ensure they are present:
ls $JAVA_HOME/jmodsOpenJDK on Linux
RPM based distributions provide *.jmod files in separate java-*-openjdk-jmods packages. Debian based distributions provide *.jmod files only in the openjdk-*-jdk-headless packages.
Generate the Project
Generate the project using the Helidon SE Quickstart Maven archetype.
mvn -U archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=4.4.1 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.seThe archetype generates a Maven project in your current directory (for example, helidon-quickstart-se). Change into this directory and build.
cd helidon-quickstart-se
mvn packageAt this point you can run the application using the JVM:
java -jar target/helidon-quickstart-se.jarIn another shell test an endpoint:
curl -X GET http://localhost:8080/greetThe application should respond with {"message":"Hello World!"}
Now stop the running application (by pressing Ctrl+C).
For more information about the Quickstart application and other endpoints it supports see the Helidon SE quickstart Guide.
Building a Custom Runtime Image
You can build a custom runtime image in 2 different ways:
Locally, on your desktop
Using Docker
Local Build
Build the custom runtime image using the jlink image profile:
mvn package -Pjlink-imageTip
This uses the helidon-maven-plugin to perform the custom image generation.
After the build completes it will report some statistics about the build including the reduction in image size.
The target/helidon-quickstart-se-jri directory is a self contained custom image of your application. It contains your application, its runtime dependencies and the JDK modules it depends on. You can start your application using the provide start script:
./target/helidon-quickstart-se-jri/bin/startClass Data Sharing (CDS) Archive and AOT Cache
If you are building with Java 24 or earlier a Class Data Sharing (CDS) archive is also included in your custom image by default. The CDS archive improves your application’s startup performance and in-memory footprint. You can learn more about Class Data Sharing in the JDK documentation.
If you are building with Java 25 or later an AOT Cache is created instead of a CDS archive. The AOT Cache is more advanced than the CDS archive and over time will contain more optimizations for improving application startup performance. You can learn more about the AOT Cache in the following JEPS: JEP 483, JEP 515, JEP 514
An on-disk cache (CDS Archive or AOT Cache) increases the size of the custom image to get these performance optimizations. It can be of significant size (tens of MB). The size of the on-disk cache is reported at the end of the build output.
If you want to skip the creation of the on-disk cache you can do so by executing your build like this:
For Java 24 or earlier
mvn package -Pjlink-image -Djlink.image.addClassDataSharingArchive=falseFor Java 25 or later
mvn package -Pjlink-image -Djlink.image.aotCache=falseFor more information on available configuration options see the helidon-maven-plugin documentation.
Multi-Stage Docker Build
To build a Docker image with a custom Java runtime image use the jlink Dockerfile included with the quickstart.
docker build -t helidon-quickstart-se-jri -f Dockerfile.jlink .Tip
This does a full build inside the Docker container. The first time you run it, it will take a while because it is downloading all of the Maven dependencies and caching them in a Docker layer. Subsequent builds will be much faster as long as you don’t change the pom.xml file. If the pom is modified then the dependencies will be re-downloaded.
Start the application:
docker run --rm -p 8080:8080 helidon-quickstart-se-jri:latestYou can exercise the application’s endpoints as before.
Using Custom Runtime Images
Custom runtime images are ideal for use when you want all of the runtime performance of the JDK JVM in a reasonably compact form.
For cases where absolute minimal startup time and image size are required, then consider using GraalVM Native Images.