Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/spring-projects/spring-boot/llms.txt

Use this file to discover all available pages before exploring further.

GraalVM Native Images are standalone, platform-specific executables produced by compiling a Java application ahead-of-time. Unlike a JVM application, a native image bundles everything it needs — no separate JVM installation is required at runtime. The result typically starts faster and uses less memory, making native images especially attractive for containerized workloads and serverless (FaaS) platforms.

How native images differ from JVM deployments

Native image compilation applies a closed-world assumption: only code reachable from the main entry point is included in the executable. This unlocks significant runtime efficiency gains but also introduces constraints that JVM deployments do not have.
The GraalVM compiler analyses your application from the main entry point at build time. Code that is unreachable from that entry point is excluded from the final executable entirely.
The application classpath is determined at build time and cannot change at runtime. Dynamic class loading is not supported.
All classes shipped in the executable are loaded into memory at startup. There is no on-demand class loading as you find in a standard JVM.
GraalVM is not directly aware of reflection, resource loading, serialization, or dynamic proxies. Spring’s AOT processing generates the required hint files automatically for most cases, but you may need to supply additional hints for third-party code.
Spring’s AOT processing converts @Configuration classes and @Bean methods into direct source code at build time. As a result:
  • @Profile annotations and profile-specific configuration have limitations.
  • Condition-driven beans such as those using @ConditionalOnProperty and .enabled properties are not supported at runtime.

Requirements

  • GraalVM 22+ (or a compatible distribution such as Liberica Native Image Kit)
  • The native-image tool installed and available on PATH
  • Java 17 or later (Java 21+ recommended)
You can install GraalVM and the native-image tool via SDKMAN!:
sdk install java 21.0.2-graalce

Building a native image

1

Add the native build plugin

The Spring Boot parent POM and Gradle plugin include a native profile / task out of the box when you use the Spring Boot starter. Verify your build file includes the native tools plugin.
<profiles>
  <profile>
    <id>native</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.graalvm.buildtools</groupId>
          <artifactId>native-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>
2

Compile the native image

Run the native compilation from your project root. This triggers Spring’s AOT processing and then invokes GraalVM’s native-image tool.
./mvnw -Pnative native:compile
The compilation can take several minutes. The resulting native executable is placed in:
  • Maven: target/<artifact-name>
  • Gradle: build/native/nativeCompile/<artifact-name>
3

Run the native executable

Execute the native binary directly — no JVM flag or java command required:
./target/myapplication
You should see startup times measured in milliseconds rather than seconds.

Building a native container image with Buildpacks

If you prefer a Docker-based workflow, Cloud Native Buildpacks can compile a native image inside a container. This means you do not need a local GraalVM installation.
You must build your application with at least JDK 25, because Buildpacks use the same GraalVM native-image version as the Java version used for compilation.
./mvnw -Pnative spring-boot:build-image
You can also convert an existing AOT-processed executable JAR into a native container image using pack:
pack build --builder paketobuildpacks/builder-noble-java-tiny \
    --path target/myproject-0.0.1-SNAPSHOT.jar \
    --env 'BP_NATIVE_IMAGE=true' \
    my-application:0.0.1-SNAPSHOT
Then run the resulting image:
docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT

What works differently in native mode

Reflection and resource hints

Spring AOT automatically generates GraalVM hint files under META-INF/native-image/{groupId}/{artifactId}/ for the reflection, resource, serialization, proxy, and JNI usage it can detect. For code paths Spring cannot discover statically, you register hints manually using RuntimeHintsRegistrar. See Ahead-of-Time processing for how to write custom hints with @ImportRuntimeHints and RuntimeHintsRegistrar.

Nested configuration properties

Reflection hints for @ConfigurationProperties classes are generated automatically. However, nested configuration property types that are not inner classes must be annotated with @NestedConfigurationProperty, or they will not be bindable in native mode.
@ConfigurationProperties("my.properties")
public class MyProperties {

    private String name;

    @NestedConfigurationProperty
    private Nested nested;

    // getters and setters ...
}
Without @NestedConfigurationProperty on the nested field, my.properties.nested.number would not be bindable when running as a native image.

Using the GraalVM tracing agent

When hints are missing, the GraalVM tracing agent can intercept reflection and resource usage on the JVM and generate the corresponding hint files automatically:
java -Dspring.aot.enabled=true \
    -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ \
    -jar target/myproject-0.0.1-SNAPSHOT.jar
Exercise the code paths you want covered, stop the application, and copy the generated files to src/main/resources/META-INF/native-image/. GraalVM picks them up on the next native compilation.

Testing native images

./mvnw -Pnative test
This compiles a native test binary and executes your test suite against it.
./gradlew nativeTest
If you already have an AOT-processed executable JAR, you can convert it using the native-image tool directly (Linux/macOS):
rm -rf target/native
mkdir -p target/native
cd target/native
jar -xvf ../myproject-0.0.1-SNAPSHOT.jar
native-image -H:Name=myproject @META-INF/native-image/argfile -cp .:BOOT-INF/classes:`find BOOT-INF/lib | tr '\n' ':'`
mv myproject ../
The native-image -cp flag does not accept wildcards. The command above uses find and tr to list all JARs explicitly. Adapt it for Windows environments.

Known limitations

GraalVM native image support is continuously evolving. Not all third-party libraries provide native-image-compatible metadata. The GraalVM Reachability Metadata repository community maintains metadata for libraries that do not yet ship their own.

Report an issue

The spring-aot-smoke-tests project tracks common application types. Contribute failing scenarios here.

Missing library metadata

If a third-party library doesn’t work with GraalVM, raise an issue in the reachability metadata project.

Build docs developers (and LLMs) love