Skip to main content

GraalVM Native Images

GraalVM native image compilation transforms your Ballerina integration into a platform-specific binary that starts in milliseconds and uses significantly less memory than JVM-based deployments. This is ideal for serverless, CLI tools, and resource-constrained environments.

JVM vs native image

MetricJVM (JAR)GraalVM Native
Startup Time2-5 seconds20-100 ms
Memory Footprint150-300 MB30-80 MB
Package Size20-50 MB40-80 MB
Peak ThroughputHigherSlightly lower
Build TimeSeconds2-5 minutes

Prerequisites

RequirementDetails
GraalVM JDKGraalVM for JDK 21 (Community or Enterprise Edition)
Native Imagegu install native-image (included in newer distributions)
OS Toolsgcc, zlib headers (Linux), Xcode Command Line Tools (macOS)
Memory8 GB+ RAM recommended for compilation
Docker8 GB+ memory allocated to Docker (for container builds)

Install GraalVM

# Using SDKMAN (recommended)
sdk install java 21.0.2-graalce

# Verify installation
java -version
native-image --version

Set the GRAALVM_HOME environment variable:

Linux/macOS:

export GRAALVM_HOME=$HOME/.sdkman/candidates/java/current

Windows:

set GRAALVM_HOME=C:\path\to\graalvm

If using SDKMAN, you can alternatively use JAVA_HOME instead of GRAALVM_HOME.

Platform-specific requirements

Windows:

  • Install Visual Studio with Microsoft Visual C++ (MSVC)
  • Initialize the x64 Native Tools Command Prompt before running bal build --graalvm

For more details, see Prerequisites for Native Image on Windows.

macOS (Apple Silicon):

  • GraalVM native-image support for Apple M1/M2 (darwin-aarch64) is experimental
  • Most features work, but some edge cases may have compatibility issues

For more details, see GraalVM on macOS.

Linux:

For distribution-specific requirements, see GraalVM on Linux.

Building a native image

Basic build

bal build --graalvm

This produces a native binary in the target/bin/ directory:

target/
bin/
my_integration # Native executable (no JVM required to run)

Run the native binary

./target/bin/my_integration

Test native compatibility

Test your integration with GraalVM to verify runtime compatibility of dependencies:

bal test --graalvm
note

Code coverage and runtime debugging features are not supported with GraalVM native image testing.

Build with Docker isolation

Build inside a Docker container for consistent, reproducible builds:

bal build --graalvm --cloud=docker

This generates a minimal Docker image containing only the native binary.

Configuration for native image

Reflection configuration

Some libraries require reflection metadata. Add a reflect-config.json in your project:

[
{
"name": "com.example.MyClass",
"allDeclaredConstructors": true,
"allPublicMethods": true
}
]

Reference it in Ballerina.toml:

[package]
org = "myorg"
name = "my_integration"
version = "1.0.0"

[build-options]
graalvmBuildOptions = "-H:ReflectionConfigurationFiles=reflect-config.json"

Build options

OptionDescription
--no-fallbackFail the build if native compilation is incomplete
--initialize-at-build-timeInitialize classes during build for faster startup
-H:+ReportExceptionStackTracesInclude full stack traces in errors
--enable-url-protocols=httpsEnable HTTPS support in the native image
-march=nativeOptimize for the current CPU architecture

Set additional options in Ballerina.toml:

[build-options]
graalvmBuildOptions = "--no-fallback --initialize-at-build-time -march=native"

Troubleshooting

IssueSolution
Build fails with "missing reflection metadata"Add classes to reflect-config.json
Build runs out of memoryIncrease build machine RAM or set -J-Xmx8g
Runtime ClassNotFoundExceptionEnsure all dynamic classes are registered in reflection config
HTTPS connections failAdd --enable-url-protocols=https to build options
Slow build timesUse build caching, increase CPU cores available to the build

Limitations

  • Dynamic class loading is not supported; all classes must be known at build time.
  • Reflection must be explicitly configured for libraries that use it.
  • Build time is significantly longer than JVM builds (minutes vs. seconds).
  • Debugging is more limited compared to JVM-based execution.
  • Some Ballerina libraries may not yet have full GraalVM compatibility -- check the Ballerina library documentation for native image support status.

What's next