Quarkus - Building a Native Executable

    • Compiling the application to a native executable

    • Packaging the native executable in a container

    This guide takes as input the application developed in the Getting Started Guide.

    To complete this guide, you need:

    • less than 15 minutes

    • an IDE

    • JDK 8 installed with configured appropriately

    • A

    • GraalVM version 19.2.1 installed and configured appropriately

    • A working container runtime (Docker, podman)

    • The code of the application developed in the .

    If you cannot install GraalVM, you can use a multi-stage Docker build to run Maven inside a Docker container that embeds GraalVM. There is an explanation of how to do this at the end of this guide.

    Version 19.2.1 is required. Using the community edition is enough.

    • Install GraalVM if you haven’t already. You have a few options for this:
    • Configure the runtime environment. Set GRAALVM_HOME environment variable to the GraalVM installation directory, for example:
    1. export GRAALVM_HOME=$HOME/Development/graalvm/

    On macOS, point the variable to the Home sub-directory:

    1. export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/
    • Install the native-image tool using gu install:
    1. ${GRAALVM_HOME}/bin/gu install native-image
    • (Optional) Set the JAVA_HOME environment variable to the GraalVM installation directory.
    1. export JAVA_HOME=${GRAALVM_HOME}
    • (Optional) Add the GraalVM bin directory to the path
    1. export PATH=${GRAALVM_HOME}/bin:$PATH
    Issues using GraalVM with macOS CatalinaGraalVM binaries are not (yet) notarized for macOS Catalina as reported in this . This means that you may see the following error when using gu:Use the following command to recursively delete the com.apple.quarantine extended attribute on the GraalVM install directory as a workaround:
    1. xattr -r -d com.apple.quarantine $HOME/Development/graalvm/

    Solution

    We recommend that you follow the instructions in the next sections and package the application step by step. However, you can go right to the completed example.

    Clone the Git repository: git clone , or download an archive.

    The solution is located in the getting-started directory.

    The native executable for our application will contain the application code, required libraries, Java APIs, and a reduced version of a VM. The smaller VM base improves the startup time of the application and produces a minimal disk footprint.

    If you have generated the application from the previous tutorial, you can find in the pom.xml the following profile:

    1. <profiles>
    2. <profile>
    3. <id>native</id>
    4. <properties>
    5. <quarkus.package.type>native</quarkus.package.type>
    6. </properties>
    7. </profiles>
    You can provide custom options for the native-image command using the <quarkus.native.additional-build-args> property.Multiple options may be separated by a comma.Another possibility is to include the quarkus.native.additional-build-args configuration property in your application.properties.You can find more information about how to configure the native image building process in the section below.

    We use a profile because, you will see very soon, packaging the native executable takes a few minutes. You couldjust pass -Dquarkus.package.type=native as a property on the command line, however it is better to use a profile asthis allows native image tests to also be run.

    Create a native executable using: ./mvnw package -Pnative.

    In addition to the regular files, the build also produces target/getting-started-1.0-SNAPSHOT-runner.You can run it using: ./target/getting-started-1.0-SNAPSHOT-runner.

    Testing the native executable

    Producing a native executable can lead to a few issues, and so it’s also a good idea to run some tests against the application running in the native file.

    In the pom.xml file, the native profile contains:

    1. <plugin>
    2. <groupId>org.apache.maven.plugins</groupId>
    3. <artifactId>maven-failsafe-plugin</artifactId>
    4. <version>${surefire-plugin.version}</version>
    5. <executions>
    6. <execution>
    7. <goals>
    8. <goal>integration-test</goal>
    9. <goal>verify</goal>
    10. </goals>
    11. <configuration>
    12. <systemProperties>
    13. <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
    14. </systemProperties>
    15. </configuration>
    16. </execution>
    17. </executions>
    18. </plugin>

    This instructs the failsafe-maven-plugin to run integration-test and indicates the location of the produced native executable.

    Then, open the src/test/java/org/acme/quickstart/NativeGreetingResourceIT.java. It contains:

    1. package org.acme.quickstart;
    2. import io.quarkus.test.junit.NativeImageTest;
    3. @NativeImageTest (1)
    4. public class NativeGreetingResourceIT extends GreetingResourceTest { (2)
    5. // Run the same tests
    6. }

    To see the NativeGreetingResourceIT run against the native executable, use ./mvnw verify -Pnative:

    1. ./mvnw verify -Pnative
    2. ...
    3. [getting-started-1.0-SNAPSHOT-runner:18820] universe: 587.26 ms
    4. [getting-started-1.0-SNAPSHOT-runner:18820] (parse): 2,247.59 ms
    5. [getting-started-1.0-SNAPSHOT-runner:18820] (inline): 1,985.70 ms
    6. [getting-started-1.0-SNAPSHOT-runner:18820] (compile): 14,922.77 ms
    7. [getting-started-1.0-SNAPSHOT-runner:18820] compile: 20,361.28 ms
    8. [getting-started-1.0-SNAPSHOT-runner:18820] image: 2,228.30 ms
    9. [getting-started-1.0-SNAPSHOT-runner:18820] write: 364.35 ms
    10. [INFO]
    11. [INFO] --- maven-failsafe-plugin:2.22.1:integration-test (default) @ getting-started ---
    12. [INFO]
    13. [INFO] -------------------------------------------------------
    14. [INFO] T E S T S
    15. [INFO] -------------------------------------------------------
    16. [INFO] Running org.acme.quickstart.NativeGreetingResourceIT
    17. Executing [/data/home/gsmet/git/quarkus-quickstarts/getting-started/target/getting-started-1.0-SNAPSHOT-runner, -Dquarkus.http.port=8081, -Dtest.url=http://localhost:8081, -Dquarkus.log.file.path=build/quarkus.log]
    18. 2019-04-15 11:33:20,348 INFO [io.quarkus] (main) Quarkus 999-SNAPSHOT started in 0.002s. Listening on: http://[::]:8081
    19. 2019-04-15 11:33:20,348 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
    20. [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.387 s - in org.acme.quickstart.NativeGreetingResourceIT
    21. ...
    By default, Quarkus waits for 60 seconds for the native image to start before automatically failing the native tests. Thisduration can be changed using the quarkus.test.native-image-wait-time system property. For example, to increase the durationto 300 seconds, use: ./mvnw verify -Pnative -Dquarkus.test.native-image-wait-time=300.

    By default, native tests runs using the prod profile.This can be overridden using the quarkus.test.native-image-profile property.For example, in your application.properties file, add: quarkus.test.native-image-profile=test.Alternatively, you can run your tests with: ./mvnw verify -Pnative -Dquarkus.test.native-image-profile=test.However, don’t forget that when the native executable is built the prod profile is enabled.So, the profile you enable this way must be compatible with the produced executable.

    Excluding tests when running as a native executable

    If you share your test class between JVM and native executions like we advise above, you can mark certain testswith the @DisabledOnNativeImage annotation in order to only run them on the JVM.

    Before going further, be sure to have a working container runtime (Docker, podman) environment.

    You can run the application in a container using the JAR produced by the Quarkus Maven Plugin.However, in this guide we focus on creating a container image using the produced native executable.

    Containerization Process

    By default, the native executable is tailored for your operating system (Linux, macOS, Windows etc).Because the container may not use the same executable format as the one produced by your operating system,we will instruct the Maven build to produce an executable from inside a container:

    1. ./mvnw package -Pnative -Dquarkus.native.container-build=true
    You can also select the container runtime to use with:These are normal Quarkus config properties, so if you always want to build in a containerit is recommended you add these to your application.properties so you do not need to specify them every time.

    The produced executable will be a 64 bit Linux executable, so depending on your operating system it may no longer be runnable.However, it’s not an issue as we are going to copy it to a container.The project generation has provided a Dockerfile.native in the src/main/docker directory with the following content:

    1. FROM registry.access.redhat.com/ubi8/ubi-minimal
    2. WORKDIR /work/
    3. COPY target/*-runner /work/application
    4. RUN chmod 775 /work
    5. EXPOSE 8080
    6. CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

    Then, if you didn’t delete the generated native executable, you can build the docker image with:

    1. docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/getting-started .

    And finally, run it with:

    1. docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started

    Creating a container with a multi-stage Docker build

    The previous section showed you how to build a native executable using Maven, but implicitly required that the proper GraalVM version be installed on the building machine (be it your local machine or your CI/CD infrastructure).

    In cases where the GraalVM requirement cannot be met, you can use Docker to perform the Maven build by using a multi-stage Docker build. A multi-stage Docker build is like two Dockerfile files combined in one, the first is used to build the artifact used by the second.

    In this guide we will use the first stage to generate the native executable using Maven and the second stage to create our runtime image.

    1. ## Stage 1 : build with maven builder image with native capabilities
    2. FROM quay.io/quarkus/centos-quarkus-maven:19.2.1 AS build
    3. COPY src /usr/src/app/src
    4. COPY pom.xml /usr/src/app
    5. USER root
    6. RUN chown -R quarkus /usr/src/app
    7. USER quarkus
    8. RUN mvn -f /usr/src/app/pom.xml -Pnative clean package
    9. ## Stage 2 : create the docker final image
    10. FROM registry.access.redhat.com/ubi8/ubi-minimal
    11. WORKDIR /work/
    12. COPY --from=build /usr/src/app/target/*-runner /work/application
    13. EXPOSE 8080
    14. CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

    Save this file in src/main/docker/Dockerfile.multistage as it is not included in the getting started quickstart.

    Before launching our Docker build, we need to update the default .dockerignore file as it filters everything except the target directory and as we plan to build inside a container we need to be able to copy the src directory. So edit your .dockerignore and remove or comment its content.
    1. docker build -f src/main/docker/Dockerfile.multistage -t quarkus-quickstart/getting-started .

    And finally, run it with:

    1. docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started
    If you need SSL support in your native executable, you can easily include the necessary libraries in your Docker image.Please see for more information.

    There are a lot of different configuration options that can affect how the native image is generated.These are provided in application.properties the same as any other config property.

    The properties are shown below:

    Configuration property fixed at build time - ️ Configuration property overridable at runtime

    TypeDefault
    Additional arguments to pass to the build processlist of stringrequired
    If the HTTP url handler should be enabled, allowing you to do URL.openConnection() for HTTP URLsbooleantrue
    If the HTTPS url handler should be enabled, allowing you to do URL.openConnection() for HTTPS URLsbooleanfalse
    If all security services should be added to the native imagebooleanfalse
    If JNI should be enabledbooleanfalse
    If all character sets should be added to the native image. This increases image sizebooleanfalse
    The location of the Graal distributionstring${GRAALVM_HOME:}
    The location of the JDKFile${java.home}
    quarkus.native.native-image-xmxThe default maximum old generation size of the native imagestring
    quarkus.native.debug-symbolsIf debug symbols should be includedbooleanfalse
    quarkus.native.debug-build-processIf the native image build should wait for a debugger to be attached before running. This is an advanced option and is generally only intended for those familiar with Substrate internalsbooleanfalse
    quarkus.native.publish-debug-build-process-portIf the debug port should be published when building with docker and debug-build-process is truebooleantrue
    quarkus.native.cleanup-serverIf the native image server should be restartedbooleanfalse
    quarkus.native.enable-retained-heap-reportingThis will report on the size of the retained heap after image buildbooleanfalse
    quarkus.native.enable-code-size-reportingThis enables reporting of the code size of the native imagebooleanfalse
    quarkus.native.enable-isolatesIf isolates should be enabledbooleantrue
    quarkus.native.enable-fallback-imagesIf a JVM based 'fallback image' should be created if native image fails. This is not recommended, as this is functionally the same as just running the application in a JVMbooleanfalse
    quarkus.native.enable-serverIf the native image server should be used. This can speed up compilation but can result in changes not always being picked up due to cache invalidation not working 100%booleanfalse
    quarkus.native.auto-service-loader-registrationIf all META-INF/services entries should be automatically registeredbooleanfalse
    quarkus.native.dump-proxiesIf the bytecode of all proxies should be dumped for inspectionbooleanfalse
    quarkus.native.container-buildIf this build should be done using a container runtime. If this is set docker will be used by default, unless container-runtime is also set.booleanfalse
    quarkus.native.builder-imageThe docker image to use to do the image buildstringquay.io/quarkus/ubi-quarkus-native-image:19.2.1
    quarkus.native.container-runtimeThe container runtime (e.g. docker) that is used to do an image based build. If this is set then a container build is always done.stringrequired
    quarkus.native.container-runtime-optionsOptions to pass to the container runtimelist of stringrequired
    quarkus.native.enable-vm-inspectionIf the resulting image should allow VM introspectionbooleanfalse
    quarkus.native.full-stack-tracesIf full stack traces are enabled in the resulting imagebooleantrue
    quarkus.native.enable-reportsIf reporting on call paths should be enabledbooleanfalse
    quarkus.native.report-exception-stack-tracesIf exceptions should be reported with a full stack tracebooleantrue
    If errors should be reported at runtime. This is a more relaxed setting, however it is not recommended as it means your application may fail at runtime if an unsupported feature is used by accident.booleanfalse

    What’s next?

    We recommend continuing the journey with the .