GraalVM Native Image with Docker
GraalVM Native Image is a tool that takes your Java (or other JVM-based) application and compiles it ahead of time (AOT) into a standalone, platform-specific executable.
Instead of running on the JVM with JIT compilation at runtime, it produces a binary that:
- Starts instantly (milliseconds instead of seconds)
- Uses less memory
- Doesn’t require a JDK installed on the target machine
In this post, I will cover using GraalVM Native Image with Docker.
Installing GraalVM JDK
If you want to build a GraalVM native image on your host, you need to install a GraalVM JDK.
There are two types of GraalVM JDK: Oracle GraalVM and GraalVM Community Edition. You can choose a GraalVM JDK to install.
Install GraalVM JDK by SDKMAN:
sdk install java 21.0.8-graal |
Or download GraalVM in IntelliJ IDEA and add it to PATH.
After you have installed a GraalVM JDK, you can verify it by running the following command line:
$ java -version |
Output:
java version "21.0.8" 2025-07-15 LTS |
GraalVM Native Image Plugins
Before using GraalVM Native Image, you need to add the GraalVM Native Image plugin to your project.
Using Gradle
Add the GraalVM Native Image Plugin to build.gradle.kts
.
build.gradle.kts
... |
Note: Don’t add the following configuration in build.gradle.kts
if you want to build a Spring Boot Application to a GraalVM Native Image:
tasks.jar { enabled = false } |
Build a GraalVM Native Image
# Build a GraalVM Native Image |
Spring Boot Application with GraalVM Native Image
1. Initialize a Spring Boot web application in Spring Initializr
2. Add Dockerfile
Dockerfile
FROM ghcr.io/graalvm/native-image-community:21-ol9 AS builder |
native-image-community:21-ol9
native-image-community
: This is the GraalVM Community Edition container image that includes the Native Image tooling—used for ahead-of-time (AOT) compilation into native binaries—without bundling the full JDK runtime.21
: Refers to Java (JDK) version 21.ol9
: Indicates that this build uses Oracle Linux 9 as its base OS. GraalVM container images are available on multiple distributions (Oracle Linux versions 7, 8, or 9), and that suffix specifies the platform.
RUN apk add --no-cache gcompat
: GraalVM native images are typically built on systems usingglibc
, which is incompatible with musl libc without specific adjustments. 1. Addinggcompat
: Install thegcompat
package in your Alpine Dockerfile to provide a compatibility layer forglibc
binaries. 2. Or Use aglibc
compatible base image such as Ubuntu or Debian.
3. Build Docker Image
# Time cost: About 120s. |
Docker Image size:
- GraalVM native-image Docker images:
- The image size using
alpine:3
as runtime: 90MB. - The image size using
oraclelinux:9-slim
as runtime: 200MB. - The image size using
Buildpacks
to create an image (gradle bootBuildImage
): 125MB.
- The image size using
- Non GraalVM native-image Docker image: 230MB.
Build resources on my local PC:
- 24.18GB of memory (75.6% of 32.00GB system memory, determined at start)
- 8 thread(s) (100.0% of 8 available processor(s), determined at start)
4. Running Docker container
docker run -p 8080:8080 myapp |
5. Access the Spring Boot application
Visiting http://localhost:8080
6. Build and push to DockerHub
# Authenticate to the Registry |
- The format of IMAGE_NAME:
<dockerhub_username>/<application_name>:<tag>
Docker Image size on DockerHub
- The image compressed size on DockerHub using
alpine:3
as runtime: 30MB. - The image compressed size on DockerHub using
oraclelinux:9-slim
as runtime: 75MB - The image compressed size on DockerHub using
Buildpacks
to create an image: 40MB.
A Simple echo CLI Application
1. Create a Java application with the gradle init
command.
$ mkdir simple-echo-cli && cd simple-echo-cli |
2. Gradle configuration
app/build.gradle.kts
plugins { |
3. The application
com/taogen/demo/App.java
package com.taogen.demo; |
4. Build and run the application on your host
# Time cost: about 35 seconds |
5. Running the application with Docker
Add Dockerfile
to the root directory of the project
Dockerfile
FROM ghcr.io/graalvm/native-image-community:21-ol9 AS builder |
Build Docker Image
# Time cost: About 40 seconds. |
- The image size using
oraclelinux:9-slim
as runtime: 130MB. - The image size using
alpine:3
as runtime: 22MB.
Running Docker container
docker run -it simple-echo-cli |
6. Fully Static GraalVM Native Image
Dockerfile
FROM --platform="linux/amd64" ghcr.io/graalvm/native-image-community:21-muslib-ol9 AS builder |
- Using
ghcr.io/graalvm/native-image-community:21-muslib-ol9
instead ofghcr.io/graalvm/native-image-community:21-ol9
. - Add GraalVM native image buildArgs
--static --libc=musl
by./gradlew nativeCompile --build-args="--static" --build-args="--libc=musl"
Add GraalVM native image buildArgs
You can add GraalVM native image buildArgs in command line ./gradlew nativeCompile --build-args="--static" --build-args="--libc=musl"
Or you can add GraalVM native image buildArgs in src/main/resources/META-INF/native-image/native-image.properties
native-image.properties
Args=\ |
Or you can add GraalVM native image buildArgs in build.gradle.kts
build.gradle.kts
graalvmNative { |
Common errors
Error 1: Executing the docker run
command occurs an error “Fatal glibc error: CPU does not support x86-64-v3”
Solution:
Use oraclelinux:8-slim
or oraclelinux:9-slim
as the runtime base Docker image instead of oraclelinux:10-slim
.
Error 2: Use alpine:3
as the base image to run GraalVM Docker image occurs an error: exec /app/xxx: no such file or directory
The error “no such file or directory” when running a GraalVM native executable in an Alpine-based Docker image often indicates a compatibility issue between the executable and Alpine’s musl libc library. GraalVM native images are typically built on systems using glibc, which is incompatible with musl libc without specific adjustments.
Solution 1:
Adding gcompat
: Install the gcompat
package in your Alpine Dockerfile to provide a compatibility layer for glibc
binaries.
FROM alpine:3 |
Solution 2:
Use a glibc
compatible base image: The most straightforward solution is to switch from Alpine to a base image that uses glibc
, such as Ubuntu or Debian. This eliminates the musl
/glibc
incompatibility.
Appendixes
Running Simple Echo CLI GraalVM Native Image on Debian Linux
1. Install SDKMAN, GraalVM JDK 21, and Gradle 9.
sudo apt update && sudo apt upgrade -y |
2. Build and run Java JAR
# Build with Gradle |
3. Build and run GraalVM Native Image
# Add JAVA_HOME environment variable before build a GraalVM Native Image |
4. Run with Docker (GraalVM Native Image)
# Install Docker |
Resources
- GraalVM JDK
- GraalVM Native Image
- GraalVM Native Image Plugins
- Docker Images
- Dockerfile Example
- Spring Boot Native Image