skip to content

native-maven-plugin

The native-maven-plugin compiles the image under a profile, after Spring's AOT step, with build arguments and the metadata repository configured. Basic literacy for anyone who claims native experience with Maven.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

5

What is the GraalVM native-maven-plugin and how do you use it to build a native image of a Spring Boot app with Maven?

level: juniorimportance: must knowfreq 60%

answer

  1. org.graalvm.buildtools:native-maven-plugin
  2. mvn -Pnative native:compile
  3. process-aot runs first, then native-image
  4. needs GraalVM JDK, closed-world
  5. profile predeclared by spring-boot-starter-parent

basics

~10 s

It's the GraalVM native-build-tools Maven plugin that compiles your app into a native executable. In Spring Boot you run mvn -Pnative native:compile; the native profile plus its native:compile goal invoke GraalVM's native-image compiler.

solid answer

~40 s

The `native-maven-plugin` (groupId `org.graalvm.buildtools`) drives GraalVM `native-image` from Maven. Spring Boot's parent POM predeclares and configures it inside a `native` profile, so you build with `mvn -Pnative native:compile`. That produces an ahead-of-time-compiled standalone executable (no JVM at runtime) with fast startup and low memory. Under the hood the plugin first triggers Spring Boot's `process-aot` goal (AOT processing that generates bean-registration code and reachability hints), then hands the classpath to GraalVM's `native-image` builder. You need a GraalVM JDK installed. The plugin also has an `add-reachability-metadata` goal that pulls in community metadata. `native:compile` forks the lifecycle up to `package`; the profile also binds `compile-no-fork` to the `package` phase so `mvn -Pnative package` builds the binary too.

code

java · 17 lines
java
// pom.xml inherits the `native` profile from spring-boot-starter-parent:
//
//   <parent>
//     <groupId>org.springframework.boot</groupId>
//     <artifactId>spring-boot-starter-parent</artifactId>
//   </parent>
//
// Build the native executable:
//   $ mvn -Pnative native:compile
//
// Run it (no JVM needed):
//   $ ./target/myapp
//
// The build performs two stages automatically:
//   1) spring-boot:process-aot  -> generates bean code + META-INF/native-image hints
//   2) native:compile           -> GraalVM native-image links a standalone binary
public class App { /* standard @SpringBootApplication */ }

go deeper

for a junior

Know the command mvn -Pnative native:compile and that it produces a JVM-free executable with fast startup.

for a middle

Explain the two-stage build (process-aot then native-image) and that the profile is inherited from Spring Boot's parent.

for a senior

Discuss closed-world constraints, GraalVM JDK requirement, and platform-specific output / containerized Linux builds.

for a principal

Weigh native vs JVM tradeoffs at an architecture level (startup/memory vs throughput/iteration) and own the CI strategy for slow native builds.

## What it is `native-maven-plugin` is part of **GraalVM native-build-tools** (Maven coordinates `org.graalvm.buildtools:native-maven-plugin`). It is the Maven front-end for GraalVM's `native-image` tool, which performs **AOT (ahead-of-time) compilation**: instead of shipping bytecode that a JVM JIT-compiles at runtime, it statically analyzes your closed-world application and compiles it into a single OS-native executable. The result starts in tens of milliseconds and uses far less memory than a JVM, at the cost of longer build times and a closed-world assumption (all reflection/resources/proxies must be known at build time). ## How Spring Boot wires it You do **not** configure the plugin yourself for the common case. `spring-boot-starter-parent` (and the Spring Boot BOM) declares a Maven **`native` profile** that: 1. Configures `org.graalvm.buildtools:native-maven-plugin` with sensible defaults. 2. Binds Spring Boot's `spring-boot-maven-plugin:process-aot` goal so AOT processing runs first. 3. Enables the GraalVM reachability **metadataRepository**. So the canonical commands are: - `mvn -Pnative native:compile` — build the native executable. - `mvn -Pnative package` — same via the `compile-no-fork` binding on the `package` phase. - `mvn -PnativeTest test` — run tests as a native image. ## The two-step build (why order matters) A Spring Boot native build is really two stages: 1. **`process-aot`** (Spring Boot's AOT engine) runs the application context at build time, then emits generated Java source for bean definitions plus `reflect-config.json`, `resource-config.json`, proxy config, etc. under `META-INF/native-image`. 2. **`native:compile`** feeds the compiled classes + generated code + metadata to GraalVM `native-image`. Step 1 must precede step 2, otherwise the image lacks the hints and fails at runtime. Spring Boot's profile arranges this ordering for you. ## Goals of the plugin - `native:compile` — build the executable (forks lifecycle to `package`). - `native:compile-no-fork` — same without forking (bound in the profile). - `native:add-reachability-metadata` — pull metadata from the GraalVM Reachability Metadata Repository into the build. - `native:test` / build-and-run tests natively. - `native:metadata-copy` — copy collected metadata (from the agent) into your project. ## Prerequisites & gotchas - You must build with a **GraalVM JDK** (or a distribution bundling `native-image`); a plain OpenJDK cannot compile a native image. - Native builds are slow (minutes) and memory-hungry — don't run them on every commit. - Reflection/resources not discovered by Spring AOT or the metadata repo must be supplied via `RuntimeHints` / `@RegisterReflectionForBinding` or via a tracing-agent run. - The executable is platform-specific: build on/for the target OS+arch (commonly inside a container for Linux images). ## When to use Use native images when startup latency and memory footprint dominate (serverless/FaaS, CLI tools, scale-to-zero). Stick with the JVM when you need peak sustained throughput, dynamic class loading, or fast iteration, since JIT beats AOT on long-running throughput and native builds slow the dev loop.

  • Why can't you build a native image with a regular OpenJDK?
    GraalVM `native-image` is the AOT compiler, and it ships only with a GraalVM (or GraalVM-based) JDK. A stock OpenJDK has no `native-image` binary, so the plugin cannot compile. You install a GraalVM JDK (e.g. via a build like Liberica NIK or Oracle GraalVM) so `native-image` is on the toolchain.
  • What runs before native:compile in a Spring Boot native build, and why?
    Spring Boot's `spring-boot-maven-plugin:process-aot` goal. It executes AOT processing to generate bean-registration source and reachability metadata (reflect/resource config) so the closed-world native-image build has all the hints it needs; running it after compilation would be too late.

saying these in an interview costs you the question

  • Thinking `native:compile` alone works without Spring's AOT processing step
  • Believing you can produce a native image with a plain OpenJDK
  • Assuming the native executable still needs a JVM installed to run
  • Thinking native images are always faster than the JVM (they win on startup/memory, not sustained throughput)

context

open as a page

Explain how spring-boot process-aot and native:compile are bound in the Maven lifecycle, and why the ordering matters.

level: middleimportance: must knowfreq 45%

basics

~20 s

In the native profile, Spring Boot binds its process-aot goal to run during the build's earlier phases; native:compile (bound around the package phase) runs after it. process-aot generates bean code and reachability hints that native-image needs, so it must run first.

open as a page

How do you pass extra options to the GraalVM native-image compiler from the native-maven-plugin, and what are common buildArgs?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Configure the plugin's <buildArgs> element (each option in a <buildArg>). These flags are forwarded verbatim to native-image. Common ones: -Ob (quick build), -O3 (max optimization), --verbose, -march=..., and PGO flags in Oracle GraalVM.

open as a page

What is the metadataRepository configuration in native-maven-plugin, and how does it relate to the GraalVM Reachability Metadata Repository?

level: seniorimportance: should knowfreq 30%

basics

~20 s

metadataRepository enables the plugin to pull reachability metadata (reflection, resources, JNI, proxies) for third-party libraries from the GraalVM Reachability Metadata Repository, so libraries that don't ship their own hints still work in a native image. Spring Boot enables it by default.

open as a page

You own a Spring Boot service moving to native images. How do you structure the Maven native build in CI/CD, and what are the operational tradeoffs?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Keep JVM builds on the fast path; run native builds (mvn -Pnative native:compile) on a separate, slower CI stage using a GraalVM toolchain, usually inside a Linux container for the target arch. Use quick-build in PRs, full optimization for releases, and run native tests before shipping.

open as a page