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.

Benefits

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 17 or later (Community or Enterprise)
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

Install GraalVM

# Using SDKMAN (recommended)
sdk install java 17.0.9-graal

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

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

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"

Deploying native images

Standalone binary on VM

Copy the binary directly -- no JVM installation needed:

scp target/bin/my_integration user@production-vm:/opt/integrations/
ssh user@production-vm "chmod +x /opt/integrations/my_integration"

Create a systemd service:

[Unit]
Description=WSO2 Integration (Native)
After=network.target

[Service]
Type=simple
User=ballerina
ExecStart=/opt/integrations/my_integration
Restart=on-failure
RestartSec=5
Environment=BAL_CONFIG_FILES=/opt/integrations/Config.toml

[Install]
WantedBy=multi-user.target

Minimal Docker image

Use a distroless or scratch base image for the smallest possible container:

FROM gcr.io/distroless/base-debian12
COPY target/bin/my_integration /app/my_integration
COPY Config.toml /app/Config.toml
WORKDIR /app
EXPOSE 9090
ENTRYPOINT ["./my_integration"]

Build and run:

docker build -t my-integration:native .
docker run -p 9090:9090 my-integration:native

AWS lambda with native image

bal build --graalvm --cloud=aws_lambda

Deploy the generated ZIP -- cold start drops from seconds to under 100ms.

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