Native build
A normal Java application runs on the JVM: the virtual machine loads classes, warms up and only then runs fast. A native build (native image) turns the application into a plain executable for a particular operating system — like a program in C or Go. No JVM is needed to run it. The build is done by the native-image tool from GraalVM or its Quarkus distribution, Mandrel. Quarkus was designed for native builds from the start, so most extensions work there without extra effort.
A jar or a native executable
| Jar on the JVM | Native executable | |
|---|---|---|
| Startup | Seconds | Tens of milliseconds |
| Memory after startup | Hundreds of MB | Tens of MB |
| Build time | Tens of seconds | Several minutes |
| Peak speed under load | Higher thanks to the JIT | Usually lower |
| Running | Needs Java | Needs only the file |
A native executable pays off where a service starts and stops often: serverless functions, autoscaling in Kubernetes, small containers. For a long-running service under constant load, a normal jar is no worse and often better.
Building with GraalVM installed
Install GraalVM for Java 25 (or Mandrel) and put its path into the GRAALVM_HOME variable. The generator has already added the native profile to pom.xml, so the command is short:
./mvnw package -DnativeThe build takes several minutes and needs a lot of memory — 8 GB of free RAM is best. The result is the target/blog-1.0.0-SNAPSHOT-runner file (with an .exe extension on Windows), which you run directly:
./target/blog-1.0.0-SNAPSHOT-runnerThe startup line says native instead of on JVM, and the startup time is in milliseconds.
Building in a container
If you do not want to install GraalVM but have Docker or Podman, Quarkus builds the file inside a container with Mandrel:
./mvnw package -Dnative -Dquarkus.native.container-build=trueNote that this produces an executable for Linux. It fits a Docker image (the next lesson) but does not run directly on Windows or macOS.
Limits
native-image analyses in advance which code is reachable and throws the rest away. So what Java normally does at run time needs hints in a native executable:
- Reflection. Classes your code creates or reads through reflection are marked with
@RegisterForReflection. Quarkus registers entities, resources and JSON records itself. - Resources. Files from
src/main/resourcesthat you read by hand are listed inquarkus.native.resources.includes. - Libraries. Use Quarkus extensions instead of bare libraries: an extension already contains the settings the native build needs.
A native build is checked with @QuarkusIntegrationTest tests: the generator created GreetingResourceIT, and following it you can write PostResourceIT extends PostResourceTest. The ./mvnw verify -Dnative command builds the executable and runs the tests right against it.
Common mistake
If you run -Dnative without GraalVM, the build fails at the very end:
[ERROR] Failed to execute goal io.quarkus.platform:quarkus-maven-plugin:3.40.1:build (default-build) on project blog:
Failed to build quarkus application: ... Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception:
java.lang.RuntimeException: Cannot find the `native-image.cmd` in the GRAALVM_HOME, JAVA_HOME and System PATH.
This message comes from Windows, where the tool is called native-image.cmd. Install GraalVM or Mandrel and set GRAALVM_HOME, or build in a container with -Dquarkus.native.container-build=true. On Windows native-image also needs the Visual Studio build tools.