Ahead-of-Time (AOT) Caching
This guide explains how to leverage OpenJDK’s Ahead-of-Time (AOT) caching capabilities in Quarkus to achieve significantly faster application startup times.
Overview
Starting with JDK 24, Project Leyden introduces powerful AOT caching capabilities (JEP 483) that can significantly reduce Java application startup time. Quarkus provides first-class support for these features, making it easy to build and deploy AOT-optimized applications.
|
Leyden AOT requires JDK 24 or newer. For older JDK versions, Quarkus can fall back to AppCDS (see AppCDS (Legacy)). |
Quick start
To build a Quarkus application with AOT caching enabled:
./mvnw verify -Dquarkus.package.jar.aot.enabled=true -DskipITs=false (1)
| 1 | If you are using a project generated by our tooling, adding -DskipITs=false makes sure the integration tests will be run. |
./gradlew build quarkusIntTest buildAotEnhancedImage -Dquarkus.package.jar.aot.enabled=true
When enabling AOT via quarkus.package.jar.aot.enabled=true, Quarkus will automatically switch to the aot-jar JAR packaging if none is explicitly configured via quarkus.package.jar.type.
|
Then run the application with:
cd target/quarkus-app
java -XX:AOTCache=app.aot -jar quarkus-run.jar
cd build/quarkus-app
java -XX:AOTCache=app.aot -jar quarkus-run.jar
AOT-optimized JAR packaging
Quarkus provides a specialized aot-jar packaging type designed to work optimally with OpenJDK’s AOT features.
This packaging type:
-
Structures the application for optimal AOT class loading
-
Delegates all class loading to the JDK’s class loaders (required for AOT to work effectively as Leyden currently does not support custom class loaders)
-
Caches frequently-accessed resources (like service loader files) to avoid classpath scanning at runtime
It is not recommended to use the aot-jar packaging outside of the AOT use case, as the fast-jar packaging offers more optimizations that result in better performance compared to the aot-jar when Leyden’s AOT cache is not used.
|
Generating the AOT cache
Enable AOT cache generation with:
./mvnw package -Dquarkus.package.jar.aot.enabled=true
./gradle build -Dquarkus.package.jar.aot.enabled=true
On JDK 24+, this generates an app.aot file in target/quarkus-app/ for Maven and build/quarkus-app/ for Gradle.
Training with integration tests
For optimal results, the AOT cache should be "trained" by running actual application workloads.
Quarkus can use your @QuarkusIntegrationTest test suite for this training:
./mvnw verify -Dquarkus.package.jar.aot.enabled=true -DskipITs=false (1)
| 1 | If you are using a project generated by our tooling, adding -DskipITs=false makes sure the integration tests will be run. |
./gradlew build quarkusIntTest buildAotEnhancedImage -Dquarkus.package.jar.aot.enabled=true
With this approach:
-
Quarkus builds the application
-
Integration tests (i.e. tests that are annotated with
@QuarkusIntegrationTest) run against the application with AOT training enabled -
The JVM captures profiling data during the test run
-
An optimized
app.aotcache is generated
The more representative your integration tests are of production traffic, the more effective the AOT cache will be.
|
For this approach to work, the integration tests must not be skipped. With a Maven project generated by our tooling, integration tests are disabled by default and only run for native builds.
Enable them with |
Controlling the training phase
The quarkus.package.jar.aot.phase configuration controls when training occurs:
-
auto(default): Usesintegration-testsfor Leyden AOT -
build: Generates the cache during build (very limited training, faster builds) -
integration-tests: Generates during integration tests execution (better optimization). For Leyden AOT, this has the same effect asauto
# Explicit integration test training
./mvnw verify -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.phase=integration-tests
# Build-time only (builds faster, less optimal)
./mvnw package -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.phase=build
# Explicit integration test training
./mvnw build quarkusIntTest buildAotEnhancedImage -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.phase=integration-tests
# Build-time only (builds faster, less optimal)
./mvnw build -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.phase=build
Running with AOT cache
Launch your application with the -XX:AOTCache JVM flag:
cd target/quarkus-app
java -XX:AOTCache=app.aot -jar quarkus-run.jar
cd build/quarkus-app
java -XX:AOTCache=app.aot -jar quarkus-run.jar
|
You can obtain more information about AOT cache usage with the You can collect even more information in an |
Container images
Quarkus can automatically build container images that include AOT caches, giving you fast startup times out of the box.
Building AOT-enhanced container images
When using quarkus-container-image-jib, quarkus-container-image-docker, or quarkus-container-image-podman:
./mvnw verify -Dquarkus.package.jar.aot.enabled=true -Dquarkus.container-image.build=true -DskipITs=false (1)
| 1 | If you are using a project generated by our tooling, adding -DskipITs=false makes sure the integration tests will be run. |
./gradlew build quarkusIntTest buildAotEnhancedImage -Dquarkus.package.jar.aot.enabled=true -Dquarkus.container-image.build=true
This will:
-
Build your application with
aot-jarpackaging -
Create a base container image
-
Run integration tests to train the AOT cache
-
Create a new container image with
-aotsuffix in the version of the image that includes the AOT cache, pre-configured to use it
Adding the container image extension
Add one of the following dependencies to your project:
<!-- Jib (recommended for most use cases) -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
<!-- Or Docker -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<!-- Or Podman -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-podman</artifactId>
</dependency>
// Jib (recommended for most use cases)
implementation("io.quarkus:quarkus-container-image-jib")
// Or Docker
implementation("io.quarkus:quarkus-container-image-docker")
// Or Podman
implementation("io.quarkus:quarkus-container-image-podman")
Requirements and limitations
-
JDK 24 or newer is required for Leyden AOT features
-
The AOT cache is JDK version specific - you must use the exact same JDK version at runtime as was used to generate the cache
-
Generating the AOT file from the integration tests is currently only supported with the Maven extension. Support will be added for Gradle in the future
-
The feature is untested on Windows environments
Configuration reference
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Configuration property |
Type |
Default |
|---|---|---|
Whether to automate the creation of an AOT file. Environment variable: Show more |
boolean |
|
The type of AOT file to generate If Environment variable: Show more |
|
|
The phase in which the AOT file should be generated. For Leyden AOT, For AppCDS, Environment variable: Show more |
|
AppCDS (Legacy)
For JDK versions prior to 24, Quarkus supports Application Class Data Sharing (AppCDS) as a fallback. AppCDS provides more modest startup improvements compared to Leyden AOT, but works on older JDKs.
Generating AppCDS archive
./mvnw package -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.type=app-cds
./gradlew build -Dquarkus.package.jar.aot.enabled=true -Dquarkus.package.jar.aot.type=app-cds
This generates an app-cds.jsa file.
Running with AppCDS
cd target/quarkus-app
java -XX:SharedArchiveFile=app-cds.jsa -jar quarkus-run.jar
cd build/quarkus-app
java -XX:SharedArchiveFile=app-cds.jsa -jar quarkus-run.jar
AppCDS limitations
-
The paths to the JAR and archive must match exactly between build and runtime
-
The JDK version must be exactly the same
-
AppCDS only caches class metadata, not method profiles or verification results
-
Training during integration tests is not supported for AppCDS (only build-time generation)
|
The legacy |