How do you pass extra options to the GraalVM native-image compiler from the native-maven-plugin, and what are common buildArgs?
answer
- <buildArgs><buildArg>...</buildArg></buildArgs> forwarded to native-image
- -Ob quick build (dev/CI), -O3 release
- -march=native is a portability trap
- PGO / --gc=G1 = Oracle GraalVM only
- buildArgs affect link step, not process-aot
basics
~10 sConfigure 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.
solid answer
~40 sThe `native-maven-plugin` exposes a `<configuration><buildArgs>` block; each `<buildArg>` is passed through unchanged to GraalVM's `native-image` builder. You typically override it inside the `native` profile in your POM or via the `-Dnative.build.args=...` property. Common flags: `-Ob` for quick-build mode (much faster builds, slower runtime — for CI/dev), `-O3` for maximum optimization (release), `--verbose` to debug the build, `-march=native`/`-march=compatibility` to control target CPU features, `--enable-url-protocols=https`, `-H:+ReportExceptionStackTraces`, and memory settings. On Oracle GraalVM you can add PGO (`--pgo-instrument` then `--pgo`) and G1 (`--gc=G1`) for throughput. Prefer appending to Spring Boot's defaults rather than replacing them, and remember buildArgs affect only the native-image link step, not Spring's process-aot stage.
code
java · 28 lines// pom.xml — customizing the GraalVM native-image compile inside the native profile.
// (XML shown as a Java-comment block; buildArgs are forwarded verbatim to native-image.)
//
// <profiles>
// <profile>
// <id>native</id>
// <build>
// <plugins>
// <plugin>
// <groupId>org.graalvm.buildtools</groupId>
// <artifactId>native-maven-plugin</artifactId>
// <configuration>
// <buildArgs>
// <buildArg>-Ob</buildArg> <!-- quick build for CI -->
// <buildArg>--verbose</buildArg>
// <buildArg>-march=compatibility</buildArg> <!-- portable across CPUs -->
// <buildArg>-H:+ReportExceptionStackTraces</buildArg>
// </buildArgs>
// </configuration>
// </plugin>
// </plugins>
// </build>
// </profile>
// </profiles>
//
// Or ad hoc from the CLI:
// mvn -Pnative native:compile -Dnative.build.args="-O3,--gc=G1"
class BuildArgsDoc {}go deeper
Know buildArgs exist and pass flags to native-image; recognize -Ob as quick build.
Configure buildArgs in the profile and pick optimization levels for dev vs release.
Handle portability (-march), GC choice, static linking for minimal containers, and know PGO/G1 are Oracle-only.
Define org-wide native build policy: optimization tiers, arch pinning, PGO pipeline, and container base-image strategy.
## The buildArgs mechanism GraalVM's `native-image` is a command-line compiler with many flags. The `native-maven-plugin` surfaces them through a `<buildArgs>` list in its `<configuration>`; each `<buildArg>` string is concatenated onto the `native-image` invocation **verbatim**. You can also inject them from the CLI with `-Dnative.build.args=...` (comma/space-separated depending on version). Because Spring Boot already provides default buildArgs via the profile, you generally want to **add** to them; the plugin supports an `<arg>` append style and there's a boolean to combine with defaults — be careful not to clobber Spring's defaults by replacing the whole element. ## Categories of common flags **Build speed vs runtime performance (optimization level):** - `-Ob` — *quick build*. Dramatically faster native builds with lower runtime performance and larger binaries. Ideal for local iteration and CI smoke builds. - `-O2` — default balanced optimization. - `-O3` — maximum optimization for release binaries (slower build). **Diagnostics:** - `--verbose` — echo the full command and progress. - `-H:+ReportExceptionStackTraces` — fuller stack traces from the builder on failures. - `--native-image-info` — print image build stats. **Target/runtime knobs:** - `-march=native` (tune for the build machine's CPU — do NOT use if the run host may differ) vs `-march=compatibility` (portable baseline). - `--gc=G1` — use the G1 garbage collector (Oracle GraalVM/Linux) for better throughput on larger heaps; default is Serial GC, good for small footprints. - `--enable-url-protocols=http,https`, `--enable-http`, `--enable-https` — include protocol handlers. - `-R:MaxHeapSize=...` or `-J-Xmx...` (the latter sets the *builder* JVM heap, not the app's). **Static/mostly-static linking (for minimal containers):** - `--static --libc=musl` — fully static binary against musl for `scratch`/distroless images. - `-H:+StaticExecutableWithDynamicLibC` (mostly-static). **Profile-Guided Optimization (Oracle GraalVM only):** - `--pgo-instrument` builds an instrumented image; run representative workloads to produce an `.iprof`; then `--pgo=default.iprof` builds the optimized image. Meaningful throughput gains, extra build complexity. ## Where it fits in the two-stage build buildArgs influence **only the `native:compile`/native-image link step**. They do not change Spring's `process-aot` output. So flags about reflection/resources belong in `RuntimeHints`/AOT, not buildArgs; buildArgs are about *how* native-image compiles and links, not *what* metadata it sees. (There's also `<jvmArgs>` for the builder JVM and `<runtimeArgs>` for `native:run`/tests — don't confuse them with `buildArgs`.) ## Gotchas - **`-march=native` in CI containers** can produce a binary that SIGILLs on older production CPUs. Use `compatibility` or pin a specific arch. - **Replacing vs appending:** setting `<buildArgs>` wholesale can drop Spring Boot's needed defaults; prefer additive configuration. - **PGO and G1 are Oracle GraalVM features**, not in the free Community/GraalVM CE-derived builds — don't promise them if you ship on a community JDK. - **Quick-build (`-Ob`) is for speed, not production**: its binaries are slower and larger; gate it behind a dev profile. ## When to use what Use `-Ob` for the inner loop and PR builds, `-O3` (+ optionally PGO/G1) for release artifacts, `--static --libc=musl` when targeting `scratch`/distroless containers, and `--verbose`/`ReportExceptionStackTraces` only while debugging a failing build.
- You added -march=native in a CI container and the binary crashes with an illegal instruction in production. Why?`-march=native` tunes the binary to the CPU features of the *build* machine. If production runs on an older/different CPU without those instructions, it faults (SIGILL). Use `-march=compatibility` or pin a specific supported architecture so the binary is portable across your fleet.
- Should reflection configuration go in buildArgs?No. buildArgs control how native-image compiles/links; reflection/resource metadata comes from Spring AOT (RuntimeHints, @RegisterReflectionForBinding) or the reachability metadata repo, written as JSON under META-INF/native-image. buildArgs are the wrong layer for that.
saying these in an interview costs you the question
- Confusing buildArgs (native-image flags) with jvmArgs (builder JVM) or runtimeArgs
- Using -march=native and shipping the binary to heterogeneous hosts
- Promising PGO/G1 on a community GraalVM build that lacks them
- Putting reflection hints in buildArgs instead of RuntimeHints/AOT
- Shipping -Ob quick-build binaries to production