In today’s world of cloud-native and serverless applications, startup time and memory usage are critical. Traditional Java applications can be slow to boot and consume large amounts of memory, which makes them less ideal for microservices and serverless environments.
Quarkus, often called the “Supersonic Subatomic Java Framework,” solves this problem by optimizing Java for containers, cloud, and Kubernetes. Combined with GraalVM, you can compile your Quarkus applications into native executables that start in milliseconds and use significantly less memory.
In this tutorial, you’ll learn step by step how to:
-
Create a Quarkus application.
-
Run it in JVM mode.
-
Compile it into a native executable using GraalVM.
-
Compare performance between JVM and native modes.
By the end, you’ll have a blazing-fast Java app ready for modern cloud environments.
Prerequisites
Before we begin, make sure you have:
-
Java 21 (or the latest LTS).
-
Apache Maven 3.9+ installed.
-
GraalVM 24.x installed (with
native-image
installed). -
Quarkus CLI (optional but recommended).
-
A code editor like IntelliJ IDEA or VS Code.
Project Setup
You can create a new Quarkus project in two ways:
1. Using Quarkus CLI
quarkus create app com.djamware:quarkus-graalvm-app \
--extension=resteasy-reactive \
--no-code
cd quarkus-graalvm-app
2. Using Maven (without CLI)
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.5:create \
-DprojectGroupId=com.djamware \
-DprojectArtifactId=quarkus-graalvm-app \
-Dextensions="resteasy-reactive"
cd quarkus-graalvm-app
This creates a new Quarkus application with RESTEasy Reactive for building REST APIs.
Creating a REST Endpoint
Inside the generated project, Quarkus provides a default resource. Let’s create our own REST endpoint instead.
- Navigate to the source folder:
src/main/java/com/djamware
- Create a new file called
GreetingResource.java
:
package com.djamware;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from Quarkus with GraalVM!";
}
}
This simple REST API will respond with a greeting message when accessed at http://localhost:8080/hello
.
Running in JVM Mode
By default, Quarkus applications run in JVM mode, which is ideal for development.
Run the application with:
./mvnw quarkus:dev
Output:
-
Quarkus will start in development mode.
-
You should see something like:
INFO [io.quarkus] (Quarkus Main Thread) quarkus-graalvm-app 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.15.2) started in 1.234s. Listening on: http://localhost:8080
Testing the Endpoint
Open a browser or use curl
:
curl http://localhost:8080/hello
Response:
Hello from Quarkus with GraalVM!
That’s it — your Quarkus REST endpoint is running in JVM mode.
👉 Next, we can proceed with Installing and Configuring GraalVM to prepare for building the native executable.
Installing and Configuring GraalVM
Quarkus integrates seamlessly with GraalVM, but you’ll need GraalVM installed to compile your app into a native executable.
Step 1: Download GraalVM
-
Go to the GraalVM releases page.
-
Download GraalVM JDK 21 (LTS) for your operating system.
Step 2: Install GraalVM
Unpack the downloaded archive and set it as your Java home. For example:
Linux/macOS
tar -xzf graalvm-jdk-21_linux-x64_bin.tar.gz
export GRAALVM_HOME=$PWD/graalvm-jdk-21
export PATH=$GRAALVM_HOME/bin:$PATH
export JAVA_HOME=$GRAALVM_HOME
Windows (PowerShell)
setx GRAALVM_HOME "C:\graalvm-jdk-21"
setx PATH "%GRAALVM_HOME%\bin;%PATH%"
setx JAVA_HOME "%GRAALVM_HOME%"
Step 3: Install Native Image Tool
Once GraalVM is set up, install the native-image
tool:
gu install native-image
Verify installation:
java -version
native-image --version
Building a Native Executable
Now that GraalVM is ready, let’s compile the Quarkus app into a native executable.
Run the following Maven command:
./mvnw package -Pnative
Quarkus will:
-
Build your project.
-
Use GraalVM’s
native-image
tool to compile it. -
Generate an executable in the
target/
folder.
You’ll find it here:
target/quarkus-graalvm-app-1.0.0-SNAPSHOT-runner
Running the Native Application
Run the native binary directly (no JVM needed):
./target/quarkus-graalvm-app-1.0.0-SNAPSHOT-runner
You’ll see output similar to:
INFO [io.quarkus] (main) quarkus-graalvm-app 1.0.0-SNAPSHOT native (powered by Quarkus 3.15.2) started in 0.015s. Listening on: http://0.0.0.0:8080
👉 Notice how startup time is in milliseconds compared to seconds in JVM mode.
Testing the Native Endpoint
Use curl
again:
curl http://localhost:8080/hello
Response:
Hello from Quarkus with GraalVM!
✅ Now your Quarkus app is running as a native executable with lightning-fast startup and reduced memory footprint.
Performance Comparison: JVM vs Native
One of the biggest advantages of Quarkus with GraalVM is the dramatic difference in startup time and memory usage between a JVM-based app and a native executable. Let’s see it in action.
1. Startup Time
Run the application in JVM mode:
./mvnw quarkus:dev
Output (example):
quarkus-graalvm-app ... started in 1.234s. Listening on: http://localhost:8080
Now run the native executable:
./target/quarkus-graalvm-app-1.0.0-SNAPSHOT-runner
Output (example):
quarkus-graalvm-app ... started in 0.015s. Listening on: http://localhost:8080
✅ Native startup time is often 100x faster than JVM mode.
2. Memory Usage
Check memory usage with the ps
command (Linux/macOS) or tasklist
(Windows).
JVM mode:
ps -o pid,rss,comm -p $(pgrep -f quarkus-graalvm-app)
Example result:
PID RSS COMMAND
1234 120000 java
(~120 MB)
Native mode:
ps -o pid,rss,comm -p $(pgrep -f quarkus-graalvm-app-1.0.0-SNAPSHOT-runner)
Example result:
PID RSS COMMAND
5678 15000 quarkus-graalvm-app-1.0.0-SNAPSHOT-runner
(~15 MB)
✅ Native mode reduces memory consumption by up to 80–90%.
3. Container Image Size (Optional)
If you build container images:
-
JVM Mode Image: ~200 MB (including JVM).
-
Native Mode Image: ~30 MB (just the binary + minimal base image).
This makes native executables perfect for microservices and serverless platforms where cold starts and resource efficiency matter most.
Summary of JVM vs Native
Feature | JVM Mode | Native Mode |
---|---|---|
Startup Time | ~1–2 seconds | ~10–20 milliseconds |
Memory Usage | 100–200 MB | 10–30 MB |
Image Size | ~200 MB | ~30 MB |
👉 With these optimizations, Quarkus + GraalVM makes Java cloud-native ready without sacrificing developer productivity.
Conclusion
In this tutorial, you learned how to:
-
Create a Quarkus project and build a simple REST API.
-
Run the application in JVM mode for fast development.
-
Install and configure GraalVM to enable native builds.
-
Compile the application into a native executable.
-
Compare the performance between JVM vs Native modes.
The results are clear: Quarkus + GraalVM brings lightning-fast startup times, dramatically reduced memory usage, and smaller container images, making Java applications competitive in cloud-native and serverless environments.
Next Steps
Here are some ways to take this further:
-
Containerize your native app
Build a Docker image for your native binary using Quarkus’s preconfigured Dockerfiles:./mvnw package -Pnative -Dquarkus.native.container-build=true docker build -f src/main/docker/Dockerfile.native -t quarkus-graalvm-app . docker run -i --rm -p 8080:8080 quarkus-graalvm-app
-
Deploy to Kubernetes or OpenShift
Quarkus integrates smoothly with Kubernetes. Use thequarkus-kubernetes
extension to generate deployment manifests automatically. -
Add persistence and features
Explore Quarkus extensions such as Hibernate ORM with Panache, RESTEasy Reactive JSON, or SmallRye Health for microservice readiness. -
Optimize native builds further
Experiment with build flags and GraalVM options to optimize binary size and startup even more. -
Explore Quarkus Dev Services
Run databases, Kafka, or other services automatically in dev mode with zero config.
✅ With this foundation, you’re ready to build high-performance, cloud-native Java applications that take full advantage of Quarkus and GraalVM.
You can get the full source code on our GitHub.
That's just the basics. If you need more deep learning about Quarkus, Java, and Microservices, you can take the following cheap course:
-
(2025) Quarkus for beginners, everything you need to know.
-
Quarkus - Simple REST API and Unit Tests with JUnit 5
-
Cloud-native Microservices with Quarkus
-
Building Microservices with Quarkus
-
K8s Native Java Microservices on Quarkus - 2022 Edition
-
Deep Dive into AWS Lambda for Java Developers
-
Accessing Relational Databases with Quarkus
Thanks!