[Feature] Provide GraalVM Native Image packaging for Seata Server
Check Ahead
I have searched the issues of this repository and believe that this is not a duplicate.
I am willing to try to implement this feature myself.
Why you need it?
Background
The Seata Server has been upgraded to Spring Boot 4.0.6 (see #8137), which is built on Spring Framework 7.0.7. As a result, the server module now requires JDK 17+ (previously JDK 8). This JDK version jump introduces a deployment friction: users must ensure the target environment has the correct JDK version installed.
Problem
JDK dependency management burden: Currently, users who deploy the Seata Server need to manually install and manage JDK 17+ on their servers or container images. This adds complexity to deployment and operation, especially in environments where multiple Java applications with different JDK requirements coexist.
Startup time and resource footprint: The standard JVM-based Seata Server has non-trivial startup time and memory overhead. For cloud-native and serverless scenarios (e.g., Kubernetes pods scaling up rapidly), faster startup and lower memory consumption are highly desirable.
Spring Boot 4 already supports it: Spring Boot 4 (via Spring Framework 7) provides first-class AOT (Ahead-of-Time) compilation and GraalVM Native Image support out of the box. The
spring-boot-maven-plugincan produce native executables with minimal configuration. However, Seata Server currently does not leverage this capability.
Benefits of Native Image
| Aspect | JVM Mode | Native Image Mode |
|---|---|---|
| JDK requirement | Must install JDK 17+ | Self-contained binary, no JDK needed |
| Startup time | Seconds (JVM warmup) | Milliseconds to sub-second |
| Memory footprint | Higher (JVM overhead) | Lower (no JVM, no class metadata) |
| Container image size | Larger (JDK base image) | Smaller (distroless / scratch base) |
| Deployment | Requires Java runtime | Single static binary |
How it could be?
Proposal: Add GraalVM Native Image packaging support to seata-server
Add a Maven profile (e.g., native) to the server/pom.xml that uses spring-boot-maven-plugin with the native classifier (or the native-maven-plugin) to produce a GraalVM native executable of the Seata Server.
High-level Steps
Add AOT/Native build configuration to
server/pom.xml:- Introduce a
nativeMaven profile that triggers GraalVM native image compilation viaspring-boot-maven-plugin'sprocess-aotgoal and GraalVM'snative-maven-plugin. - Configure native image build args (e.g.,
--enable-url-protocols=http,--add-opensfor reflection-heavy libraries).
- Introduce a
Provide GraalVM reachability metadata (if needed):
- Some Seata components (e.g., Netty, Kryo serializer, dynamic proxies, reflection-based config loading) may require explicit GraalVM
reflect-config.json,resource-config.json, orproxy-config.json. - Spring Boot 4's AOT engine can auto-generate most of these, but manual hints may be required for non-Spring-managed components.
- Some Seata components (e.g., Netty, Kryo serializer, dynamic proxies, reflection-based config loading) may require explicit GraalVM
Container image integration (optional but recommended):
- Extend the existing
jib-maven-pluginconfiguration (or add aDockerfile.native) to build a minimal container image containing only the native binary (e.g., based ongcr.io/distroless/cc-debian12orscratch).
- Extend the existing
CI/CD and distribution:
- Add a CI job to build and publish native binaries as release artifacts (e.g.,
seata-server-{version}-linux-amd64,seata-server-{version}-linux-arm64). - Optionally publish native container images (e.g.,
apache/seata-server:{version}-native).
- Add a CI job to build and publish native binaries as release artifacts (e.g.,
Example Pseudo-configuration
<!-- In pom.xml -->
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>${spotless-maven-plugin.version}</version>
<configuration>
<json>
<includes>
<include>src/main/resources/META-INF/native-image/${project.groupId}/${project.artifactId}/*.json
</include>
</includes>
<jackson/>
</json>
</configuration>
</plugin><!-- In build/pom.xml -->
<properties>
<native-maven-plugin.version>1.1.3</native-maven-plugin.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native-maven-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build><!-- In server/pom.xml -->
<profiles>
<profile>
<id>native-linux-amd64</id>
<activation>
<os>
<name>linux</name>
<arch>amd64</arch>
</os>
</activation>
<properties>
<native.platform>linux-amd64</native.platform>
</properties>
</profile>
<profile>
<id>native-linux-aarch64</id>
<activation>
<os>
<name>linux</name>
<arch>aarch64</arch>
</os>
</activation>
<properties>
<native.platform>linux-arm64</native.platform>
</properties>
</profile>
<profile>
<id>native-darwin-x86_64</id>
<activation>
<os>
<name>mac os x</name>
<arch>x86_64</arch>
</os>
</activation>
<properties>
<native.platform>darwin-amd64</native.platform>
</properties>
</profile>
<profile>
<id>native-darwin-aarch64</id>
<activation>
<os>
<name>mac os x</name>
<arch>aarch64</arch>
</os>
</activation>
<properties>
<native.platform>darwin-arm64</native.platform>
</properties>
</profile>
<profile>
<id>native-windows-amd64</id>
<activation>
<os>
<name>windows</name>
<arch>amd64</arch>
</os>
</activation>
<properties>
<native.platform>windows-amd64</native.platform>
</properties>
</profile>
<profile>
<id>native</id>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Spring-Boot-Native-Processed>true</Spring-Boot-Native-Processed>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.apache.seata.server.ServerApplication</mainClass>
</configuration>
<executions>
<execution>
<id>process-aot</id>
<goals>
<goal>process-aot</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<mainClass>org.apache.seata.server.ServerApplication</mainClass>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<imageName>${project.artifactId}-${project.version}-${native.platform}</imageName>
<metadataRepository>
<enabled>true</enabled>
</metadataRepository>
<buildArgs>
<arg>--enable-url-protocols=http</arg>
<arg>-H:+ReportExceptionStackTraces</arg>
</buildArgs>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</exclusion>
</exclusions>
</configuration>
<executions>
<execution>
<id>add-reachability-metadata</id>
<goals>
<goal>add-reachability-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>nativeTest</id>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.apache.seata.server.ServerApplication</mainClass>
</configuration>
<executions>
<execution>
<id>process-test-aot</id>
<goals>
<goal>process-test-aot</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<mainClass>org.apache.seata.server.ServerApplication</mainClass>
<imageName>${project.artifactId}-${project.version}-${native.platform}</imageName>
<metadataRepository>
<enabled>true</enabled>
</metadataRepository>
<buildArgs>
<arg>--enable-url-protocols=http</arg>
<arg>-H:+ReportExceptionStackTraces</arg>
</buildArgs>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</exclusion>
</exclusions>
</configuration>
<executions>
<execution>
<id>native-test</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>Users would then build the native image with:
mvn clean package -Pnative -pl serverAnd run it directly without a JDK:
./seata-server/target/seata-serverOther related information
Compatibility Considerations
Reflection-heavy libraries that Seata depends on may need special handling in native mode:
- Netty (used for RPC communication): Netty has existing GraalVM metadata. Spring Boot 4 should handle this via its reachability metadata repository.
- Kryo / other serializers: Serialization frameworks often rely on reflection. AOT hints or
@RegisterReflectionForBindingmay be required. - Dynamic proxies (e.g.,
java.lang.reflect.Proxy): Seata's configuration binding and interceptor chains may use proxies. - JCommander: Command-line argument parsing may need reflection config.
Profile-guided optimization (PGO) can be added later to further improve native image performance.
Not all features need to work in native mode initially — a phased approach is acceptable:
- Phase 1: Basic server startup, configuration loading, and core transaction coordination in native mode.
- Phase 2: Full feature parity with JVM mode (all serializers, all store modes, all discovery modes).
Related Links
- Introducing GraalVM Native Images
- GraalVM Native Image Documentation
- GraalVM Reachability Metadata Repository
- PR #8137: Spring Boot 4 upgrade for server
Source: apache/incubator-seata