GraalVM + Quarkus Native
Quarkus native build, Mandrel vs upstream GraalVM, and image size optimization.
# CLAUDE.md — GraalVM + Quarkus Native
## Why Quarkus + native is a sweet spot
- Quarkus is **designed** for native compilation. Most extensions ship native hints out of the box.
- Build time is fast (relative to other Java frameworks) thanks to Quarkus' build-time framework processing.
- Cold-start on Lambda / Cloud Run drops from 5+ seconds (JVM Spring) to ~50 ms (native Quarkus).
## Mandrel vs upstream GraalVM
- **Mandrel** is Red Hat's downstream of GraalVM CE — focused exclusively on `native-image`.
- Quarkus tests against Mandrel by default. Pin a specific Mandrel image:
```
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.1-java21
```
- Use upstream GraalVM if you need polyglot, Truffle, or Oracle's advanced JIT optimizations alongside.
## Build commands
- JVM mode: `./mvnw package` (still produces an optimized AOT artifact in Boot/Quarkus).
- Native mode: `./mvnw package -Pnative`.
- Container build (no GraalVM/Mandrel locally): `./mvnw package -Pnative -Dquarkus.native.container-build=true`.
## Configuration knobs
- `quarkus.native.additional-build-args` for niche flags.
- `quarkus.native.resources.includes` for resources you load via `getResource`.
- `quarkus.native.compression.level=10` (Quarkus 3.5+) for smaller binaries (UPX-based).
- Disable monitoring extras you don't need: `--no-fallback`, `-H:-AddAllCharsets`.
## Reflection
- Most Quarkus extensions register reflection automatically via the build-time SPI.
- For custom code: `@RegisterForReflection(targets = MyDto.class)`.
- For JSON DTOs (Jackson, JSON-B): annotation is needed if the class is loaded reflectively. Records and standard JAX-RS DTOs usually work without it.
## Image size
- Typical Quarkus native image: 50–80 MB binary, ~100 MB Docker image with distroless.
- Strip what you don't use:
- `quarkus.native.enable-https-url-handler=false` if not needed
- `quarkus.native.enable-jni=false` if no JNI deps
- `quarkus.native.add-all-charsets=false` (default) — explicit charset list via `quarkus.native.include-charsets=UTF_8`
## Tests
- `@QuarkusIntegrationTest` runs against the packaged native binary. Add to CI; don't push without it.
- Use the same test classes for JVM and native — Quarkus picks the artifact based on the Maven profile.
- Slow native build means CI matrix: native tests on main; JVM-mode on PRs.
## Common pitfalls
- A library that uses `Class.forName(stringFromConfig)` at runtime — won't work in native unless every possible class is in the reachability metadata.
- Static initializers that touch the network or filesystem — they run at build time by default and can leak build-host state into the binary.
- JNI without explicit configuration — needs `jni-config.json` and the library binary in the image.
## Workflow
- Develop in JVM dev mode (`./mvnw quarkus:dev`). Live reload is invaluable.
- Run native tests on PR merges, not every push. Build is too slow for inner loop.
- Profile native binaries with `--enable-monitoring=jfr` and Java Flight Recorder.
## Don't
- Don't build native locally on every save. Use JVM dev mode.
- Don't skip `@QuarkusIntegrationTest` for native. Bugs that hide in JVM emerge in native.
- Don't pull in Spring/Quarkus mixed-stack libraries — Quarkus' native story works because every extension is on board.
- Don't deploy native binaries built on Mac for Linux containers without cross-compilation. Use the container builder image.
Other Quarkus templates
Modern Quarkus Rules
Quarkus 3+ defaults: dev mode, build-time optimization, configuration, and CDI.
Quarkus REST (RESTEasy Reactive)
JAX-RS endpoints with RESTEasy Reactive — request validation and response models.
Quarkus + Hibernate ORM with Panache
Active-record and repository styles with Panache, Flyway migrations, transactions.
Quarkus + GraalVM Native
Native compilation, reflection config, resource inclusion, and image-size tuning.
Quarkus Testing
@QuarkusTest, dev services, mocking, and isolated test profiles.