Add a blog post: Armeria Hyperfocal #1 — Building a Native Image with Aremria and GraalVM#6720
Add a blog post: Armeria Hyperfocal #1 — Building a Native Image with Aremria and GraalVM#6720
Conversation
…with Aremria and GraalVM
📝 WalkthroughWalkthroughA new MDX blog post documents building a GraalVM native image for an Armeria service: Gradle toolchain and Palantir plugin configuration, example annotated service, runtime reflection metadata generation, build/run steps, and JVM vs native-image metrics. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx`:
- Around line 151-157: The four Markdown image tags use empty alt text which
harms accessibility; update each tag by replacing the empty alt with meaningful
descriptive alt strings that reflect the screenshot content (e.g., "Armeria
project structure screenshot", "GraalVM native-image build output", "Server
running with native image", "Benchmark results or logs") referencing the
existing image filenames hyperfocal1-1.png, hyperfocal1-2.png, hyperfocal1-3.png
and hyperfocal1-4.png so reviewers can locate and verify edits (replace 
with e.g. , etc.).
- Line 8: Update the post H1 string that currently reads "Armeria Hyperfocal `#1`:
Building a Native Image with Aremria and GraalVM" by correcting the typo
"Aremria" to "Armeria" so the header becomes "Armeria Hyperfocal `#1`: Building a
Native Image with Armeria and GraalVM"; locate and edit the top-level markdown
header line to replace the misspelled word.
- Line 90: The current guidance tells readers to "safely ignore" GraalVM Native
Image build warnings; update the sentence to instead advise reviewing these
warnings (especially those mentioning reflection, dynamic proxies, resources,
and JNI) and taking corrective action by generating or adding the appropriate
reachability metadata (e.g., reflect-config.json, proxy-config.json,
jni-config.json, resource-config.json) or following GraalVM docs to avoid
runtime errors like MissingReflectionRegistrationError; modify the paragraph
around the GraalVM build warnings to recommend investigation and configuration
rather than dismissal.
- Around line 40-59: The snippet uses Groovy DSL and wrong plugin block/property
names; convert to Kotlin DSL by using plugins { id("com.palantir.graal") version
"0.12.0" } and Kotlin string syntax (double quotes), keep the java.toolchain
as-is, then replace the wrong nativeImage block with the Palantir plugin's graal
block and use outputName instead of executableName and set mainClass with a
Kotlin string (e.g., graal { mainClass = "com.linecorp.armeria.examples.Main";
outputName = "armeria-native-image" }).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: af125148-e16a-4cbd-bb71-31df8a3bb48c
⛔ Files ignored due to path filters (4)
site-new/static/img/hyperfocal1-1.pngis excluded by!**/*.pngsite-new/static/img/hyperfocal1-2.pngis excluded by!**/*.pngsite-new/static/img/hyperfocal1-3.pngis excluded by!**/*.pngsite-new/static/img/hyperfocal1-4.pngis excluded by!**/*.png
📒 Files selected for processing (1)
site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx
| In your `build.gradle.kts`, we use a specialized plugin from **Palantir** to streamline the native image build process. | ||
|
|
||
| ```java | ||
| plugins { | ||
| id 'com.palantir.graal' version '0.12.0' | ||
| } | ||
|
|
||
| java { | ||
| toolchain { | ||
| languageVersion = JavaLanguageVersion.of(17) | ||
| vendor = JvmVendorSpec.matching("GraalVM Community") | ||
| } | ||
| } | ||
|
|
||
| nativeImage { | ||
| mainClass = 'com.linecorp.armeria.examples.Main' | ||
| executableName = 'armeria-native-image' | ||
| // Optional: Add specific GraalVM flags here | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For the Palantir Graal Gradle plugin, what is the correct Kotlin DSL (build.gradle.kts`) syntax for:
- plugin declaration
- setting
mainClassandexecutableNameinnativeImage?
Please cite official plugin docs/examples.`
💡 Result:
- Plugin declaration in Kotlin DSL: plugins { id("com.palantir.graal") version "0.13.0" } 2) Setting mainClass and executableName in nativeImage: graal { mainClass = "com.example.Main" outputName = "my-executable" } Note: The plugin creates a 'graal' extension block for configuration. The executableName corresponds to 'outputName' as per official README. No official Kotlin DSL examples found in repo tests (which use Groovy), but standard Gradle Kotlin DSL syntax applies to property setters. The plugin is unmaintained per GitHub warning, latest release March 2026.
Citations:
- 1: https://github.com/palantir/gradle-graal
- 2: https://oreil.ly/3eY3Y
- 3: https://plugins.gradle.org/plugin/com.palantir.graal
- 4: https://github.com/palantir/gradle-graal/blob/develop/src/test/groovy/com/palantir/gradle/graal/GradleGraalEndToEndSpec.groovy
build.gradle.kts example has three critical issues: Groovy syntax, incorrect configuration block name, and wrong property name.
The snippet uses Groovy syntax (single quotes, id 'com.palantir.graal') which is invalid for Kotlin DSL. Additionally, the configuration block should be graal not nativeImage, and the executable property should be outputName not executableName per the official Palantir plugin documentation.
Corrected Kotlin DSL snippet
plugins {
- id 'com.palantir.graal' version '0.12.0'
+ id("com.palantir.graal") version "0.12.0"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.matching("GraalVM Community")
}
}
-nativeImage {
- mainClass = 'com.linecorp.armeria.examples.Main'
- executableName = 'armeria-native-image'
+graal {
+ mainClass = "com.linecorp.armeria.examples.Main"
+ outputName = "armeria-native-image"
// Optional: Add specific GraalVM flags here
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| In your `build.gradle.kts`, we use a specialized plugin from **Palantir** to streamline the native image build process. | |
| ```java | |
| plugins { | |
| id 'com.palantir.graal' version '0.12.0' | |
| } | |
| java { | |
| toolchain { | |
| languageVersion = JavaLanguageVersion.of(17) | |
| vendor = JvmVendorSpec.matching("GraalVM Community") | |
| } | |
| } | |
| nativeImage { | |
| mainClass = 'com.linecorp.armeria.examples.Main' | |
| executableName = 'armeria-native-image' | |
| // Optional: Add specific GraalVM flags here | |
| } | |
| ``` | |
| In your `build.gradle.kts`, we use a specialized plugin from **Palantir** to streamline the native image build process. | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx`
around lines 40 - 59, The snippet uses Groovy DSL and wrong plugin
block/property names; convert to Kotlin DSL by using plugins {
id("com.palantir.graal") version "0.12.0" } and Kotlin string syntax (double
quotes), keep the java.toolchain as-is, then replace the wrong nativeImage block
with the Palantir plugin's graal block and use outputName instead of
executableName and set mainClass with a Kotlin string (e.g., graal { mainClass =
"com.linecorp.armeria.examples.Main"; outputName = "armeria-native-image" }).
There was a problem hiding this comment.
This actually does look like groovy,
perhaps either migrate documentation to build.gradle, settings.gradle,
or modify to
plugins {
id("com.palantir.graal") version "0.12.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.matching("GraalVM Community"))
}
}
nativeImage {
mainClass("com.linecorp.armeria.examples.Main")
executableName("armeria-native-image")
}There was a problem hiding this comment.
🧩 Analysis chain
@jrhee17, great catch — the code is indeed Groovy syntax and needs to be updated. However, there's a small correction to your suggested Kotlin DSL: the configuration block for the Palantir com.palantir.graal plugin is graal { ... }, not nativeImage { ... } (which is just the name of the Gradle task, not the extension). Additionally, the property for the executable name is outputName, not executableName.
The ./gradlew nativeImage task creates the native image, but the plugin is configured through the graal Gradle extension block. Kotlin DSL examples for this plugin show graal { mainClass("...") outputName("...") } method-call style.
The corrected Kotlin DSL for build.gradle.kts should be:
plugins {
id("com.palantir.graal") version "0.12.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.matching("GraalVM Community"))
}
}
graal {
mainClass("com.linecorp.armeria.examples.Main")
outputName("armeria-native-image")
// Optional: Add specific GraalVM flags here
}Alternatively, if the author prefers Groovy DSL, migrating the examples to plain build.gradle/settings.gradle would also be a clean solution — both options resolve the syntax issue.
🌐 Web query:
palantir gradle-graal plugin nativeImage graal block Kotlin DSL mainClass executableName
💡 Result:
With the Palantir gradle-graal plugin (com.palantir.graal), the native-image entry point and produced binary name are configured on the graal extension as:
mainClass(...)(required fornativeImage)outputName(...)(this is the “executableName”; don’t pass-H:Name=...viaoption(...)) (github.com)
Gradle Kotlin DSL (build.gradle.kts)
import com.palantir.gradle.graal.GraalExtension
plugins {
id("com.palantir.graal") version "0.13.0"
}
extensions.configure<GraalExtension>("graal") {
mainClass("com.example.MainKt")
outputName("my-cli")
// extra native-image args:
option("--no-fallback")
option("-H:+ReportExceptionStackTraces")
}Then build with:
./gradlew nativeImageThe plugin writes outputs under build/graal/. (github.com)
Citations:
@jrhee17, great catch — the code is indeed Groovy syntax and needs to be updated. However, there's a small correction needed in your suggested Kotlin DSL snippet. The configuration block for the Palantir com.palantir.graal plugin is graal { ... }, not nativeImage { ... } — nativeImage is only the name of the Gradle task used to trigger the build (./gradlew nativeImage). Additionally, the produced binary name is configured via outputName(...), not executableName.
The Kotlin DSL configuration looks like this:
import com.palantir.gradle.graal.GraalExtension
plugins {
id("com.palantir.graal") version "0.12.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.matching("GraalVM Community"))
}
}
extensions.configure<GraalExtension>("graal") {
mainClass("com.linecorp.armeria.examples.Main")
outputName("armeria-native-image")
// Optional: Add specific GraalVM flags here
}Alternatively, if the author prefers Groovy DSL, migrating the examples to plain build.gradle/settings.gradle would also cleanly resolve the syntax issue — either option works.
|
|
||
| **💡 A Pro-tip for the Build Process:** | ||
|
|
||
| When you run this command, GraalVM performs a deep static analysis of your code. You might see a lot of warnings flashing by in your terminal during this stage. Don't panic! This is perfectly normal. GraalVM is simply reporting on code paths it can't fully "see" through. As long as the build finishes and your binary is generated, you can safely ignore these warnings. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GraalVM Native Image docs, are build-time warnings generally safe to ignore, or do docs recommend investigating specific categories (reflection/dynamic proxies/resources/JNI)?
💡 Result:
GraalVM Native Image docs do not explicitly state that build-time warnings are generally safe to ignore. Instead, they recommend investigating and configuring specific categories like reflection, dynamic proxies, resources, and JNI, as these often indicate missing reachability metadata required for runtime behavior. Official docs emphasize that Native Image performs static analysis but cannot always detect dynamic features (e.g., Reflection API calls, Proxy.newProxyInstance, JNI lookups, resource loading). When analysis fails, manual configuration files (reflect-config.json, proxy-config.json, jni-config.json, resource-config.json) or reachability-metadata.json must be provided during build. Build-time warnings, such as "WARNING: Could not register reflection metadata" or similar for proxies/resources/JNI, signal potential missing registrations that could cause runtime errors like MissingReflectionRegistrationError. To diagnose, use options like --exact-reachability-metadata, -H:ThrowMissingRegistrationErrors, or -XX:MissingRegistrationReportingMode=Warn. Build reports (--emit=build-report) and output breakdowns help identify issues. Examples from Spring Native/Quarkus show warnings for unresolvable reflection entries, resolved by refining hints/metadata rather than ignoring. While some warnings (e.g., deprecated options, module exports) may be harmless, those tied to dynamic features require investigation to ensure closed-world assumption holds and avoid runtime failures.
Citations:
- 1: https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/native-image/Limitations/
- 2: https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/native-image/Reflection/
- 3: https://www.graalvm.org/latest/reference-manual/native-image/overview/build-report/
- 4: https://docs.oracle.com/en/graalvm/jdk/24/docs/reference-manual/native-image/guides/troubleshoot-run-time-errors/
- 5: https://docs.oracle.com/en/graalvm/jdk/21/docs/reference-manual/native-image/dynamic-features/Reflection/
- 6: https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/native-image/DynamicProxy/
- 7: https://graalvm.org/jdk21/reference-manual/native-image/metadata
- 8: https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildOutput/
- 9: https://www.graalvm.org/22.0/reference-manual/native-image/BuildOutput/
Revise warning guidance to recommend investigating warnings rather than ignoring them.
GraalVM Native Image documentation explicitly recommends investigating build-time warnings—particularly those related to reflection, dynamic proxies, resources, and JNI—as they typically indicate missing reachability metadata. Warnings can signal missing registrations that cause runtime failures (e.g., MissingReflectionRegistrationError). Suggest updating the sentence to recommend reviewing warnings and configuring necessary metadata via reflect-config.json, proxy-config.json, jni-config.json, or resource-config.json rather than dismissing them as safe to ignore.
🧰 Tools
🪛 LanguageTool
[style] ~90-~90: Consider using a synonym to be more concise.
Context: ...ic analysis of your code. You might see a lot of warnings flashing by in your terminal d...
(A_LOT_OF)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx`
at line 90, The current guidance tells readers to "safely ignore" GraalVM Native
Image build warnings; update the sentence to instead advise reviewing these
warnings (especially those mentioning reflection, dynamic proxies, resources,
and JNI) and taking corrective action by generating or adding the appropriate
reachability metadata (e.g., reflect-config.json, proxy-config.json,
jni-config.json, resource-config.json) or following GraalVM docs to avoid
runtime errors like MissingReflectionRegistrationError; modify the paragraph
around the GraalVM build warnings to recommend investigation and configuration
rather than dismissal.
|  | ||
|
|
||
|  | ||
|
|
||
|  | ||
|
|
||
|  |
There was a problem hiding this comment.
Add meaningful alt text for screenshots.
All four images use empty alt text (), which reduces accessibility and context for non-visual readers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx`
around lines 151 - 157, The four Markdown image tags use empty alt text which
harms accessibility; update each tag by replacing the empty alt with meaningful
descriptive alt strings that reflect the screenshot content (e.g., "Armeria
project structure screenshot", "GraalVM native-image build output", "Server
running with native image", "Benchmark results or logs") referencing the
existing image filenames hyperfocal1-1.png, hyperfocal1-2.png, hyperfocal1-3.png
and hyperfocal1-4.png so reviewers can locate and verify edits (replace 
with e.g. , etc.).
There was a problem hiding this comment.
♻️ Duplicate comments (3)
site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx (3)
151-157:⚠️ Potential issue | 🟡 MinorAdd descriptive alt text for all screenshots.
Lines 151-157 use empty alt text (
), which hurts accessibility and context for assistive technologies.Proposed doc fix
- + - + - + - +🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx` around lines 151 - 157, Replace the four image markdowns that currently use empty alt text with descriptive alt attributes that summarize each screenshot's content (e.g., change  to something like ); update each occurrence of the image references (hyperfocal1-1.png, hyperfocal1-2.png, hyperfocal1-3.png, hyperfocal1-4.png) in the MDX so the alt text concisely conveys the visual information for accessibility and context.
40-59:⚠️ Potential issue | 🟠 MajorFix invalid
build.gradle.ktssnippet (Kotlin DSL + Palantir block names).At Line 44 and Lines 54-57, the snippet is still using Groovy-style syntax and the wrong extension/property names for the Palantir plugin, so readers copying this into
build.gradle.ktswill fail.For com.palantir.graal in Gradle Kotlin DSL, what are the correct block and property names for configuring main class and output executable name? Please cite official plugin docs/examples.Proposed doc fix
```java plugins { - id 'com.palantir.graal' version '0.12.0' + id("com.palantir.graal") version "0.12.0" } @@ -nativeImage { - mainClass = 'com.linecorp.armeria.examples.Main' - executableName = 'armeria-native-image' +graal { + mainClass = "com.linecorp.armeria.examples.Main" + outputName = "armeria-native-image" // Optional: Add specific GraalVM flags here }</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx
around lines 40 - 59, The Gradle Kotlin DSL snippet uses Groovy syntax and the
wrong Palantir extension: replace id 'com.palantir.graal' with Kotlin DSL
id("com.palantir.graal") version "0.12.0", switch the Groovy nativeImage block
to the Palantir Kotlin DSL graal extension, and rename properties executableName
-> outputName (keep mainClass as the mainClass property); update the
build.gradle.kts to use these exact symbols (id("com.palantir.graal"), graal {
mainClass = "...", outputName = "..." }) so the Kotlin DSL compiles correctly.</details> --- `90-90`: _⚠️ Potential issue_ | _🟠 Major_ **Don’t tell readers to ignore GraalVM warnings.** Line 90 currently says warnings are safe to ignore; this is risky guidance, especially for reflection/proxy/resource/JNI metadata warnings that can break runtime behavior. ```web Do GraalVM Native Image docs recommend ignoring build-time warnings, or investigating warnings related to reflection, dynamic proxies, resources, and JNI? Please provide official references.Proposed doc fix
-When you run this command, GraalVM performs a deep static analysis of your code. You might see a lot of warnings flashing by in your terminal during this stage. Don't panic! This is perfectly normal. GraalVM is simply reporting on code paths it can't fully "see" through. As long as the build finishes and your binary is generated, you can safely ignore these warnings. +When you run this command, GraalVM performs a deep static analysis of your code. Warnings are common, but you should review them—especially ones about reflection, dynamic proxies, resources, and JNI—because they often indicate missing reachability metadata that can cause runtime failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx`:
- Around line 151-157: Replace the four image markdowns that currently use empty
alt text with descriptive alt attributes that summarize each screenshot's
content (e.g., change  to something like );
update each occurrence of the image references (hyperfocal1-1.png,
hyperfocal1-2.png, hyperfocal1-3.png, hyperfocal1-4.png) in the MDX so the alt
text concisely conveys the visual information for accessibility and context.
- Around line 40-59: The Gradle Kotlin DSL snippet uses Groovy syntax and the
wrong Palantir extension: replace id 'com.palantir.graal' with Kotlin DSL
id("com.palantir.graal") version "0.12.0", switch the Groovy nativeImage block
to the Palantir Kotlin DSL graal extension, and rename properties executableName
-> outputName (keep mainClass as the mainClass property); update the
build.gradle.kts to use these exact symbols (id("com.palantir.graal"), graal {
mainClass = "...", outputName = "..." }) so the Kotlin DSL compiles correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 09d721cf-764e-4a71-99f1-41321d7b29ba
📒 Files selected for processing (1)
site-new/src/content/blog/en/2026-04-10-armeria-hyperfocal-1-building-a-native-image-with-armeria-and-graalvm.mdx
| In your `build.gradle.kts`, we use a specialized plugin from **Palantir** to streamline the native image build process. | ||
|
|
||
| ```java | ||
| plugins { | ||
| id 'com.palantir.graal' version '0.12.0' | ||
| } | ||
|
|
||
| java { | ||
| toolchain { | ||
| languageVersion = JavaLanguageVersion.of(17) | ||
| vendor = JvmVendorSpec.matching("GraalVM Community") | ||
| } | ||
| } | ||
|
|
||
| nativeImage { | ||
| mainClass = 'com.linecorp.armeria.examples.Main' | ||
| executableName = 'armeria-native-image' | ||
| // Optional: Add specific GraalVM flags here | ||
| } | ||
| ``` |
There was a problem hiding this comment.
This actually does look like groovy,
perhaps either migrate documentation to build.gradle, settings.gradle,
or modify to
plugins {
id("com.palantir.graal") version "0.12.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.matching("GraalVM Community"))
}
}
nativeImage {
mainClass("com.linecorp.armeria.examples.Main")
executableName("armeria-native-image")
}| vendor = JvmVendorSpec.matching("GraalVM Community") | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
I understood that we assumed users would configure Armeria dependencies themselves.
There was a problem hiding this comment.
I assumed that since we provide the whole demo code here: https://github.com/line/armeria-hyperfocal/blob/main/01/native-image/build.gradle.kts
Motivation:
Result:

