GraalVM Native Image by default builds dynamically linked binaries: at build time it first loads your application classes and interfaces, and then hooks them together in a process of dynamic linking.
However, you can create a statically linked or mostly-statically linked native executable, depending on your needs.
A static native executable is a statically linked binary that can be used without any additional library dependencies.
A static native executable is easy to distribute and deploy on a slim or distroless container (a scratch container).
You can create a static native executable by statically linking it against musl-libc, a lightweight, fast and simple libc
implementation.
A mostly-static native executable is a binary that links everything (zlib
, JDK shared libraries) except the standard C library, libc
. This is an alternative option to staticly linking everything. Also, depending on the user’s code, it may link libstdc+
and libgcc
.
This approach is ideal for deployment on a distroless container image.
Note: This currently only works when linked against
libc
.
This guide shows how you can take advantage of Native Image linking options including fully dynamic, fully static, and mostly static (except libc
) to generate an executable ideal for your deployment scenario.
The following prerequisites should be met:
musl
toolchain, make
, and configure
zlib
libraryInstall a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.
Next, you should install the musl
toolchain, compile and install zlib
into the toolchain.
Download the musl
toolchain from musl.cc.
(We recommend this one).
Extract the toolchain to a directory of your choice. This directory will be referred as $TOOLCHAIN_DIR
.
Download the latest zlib
library sources from zlib.net and extract them. (This documentation uses zlib-1.2.11
.)
CC
:
CC=$TOOLCHAIN_DIR/bin/gcc
zlib
directory, and then run the following commands to compile and install zlib
into the toolchain:
./configure --prefix=$TOOLCHAIN_DIR --static
make
make install
import java.util.Map;
public class EnvMap {
public static void main (String[] args) {
var filter = args.length > 0 ? args[0] : "";
Map<String, String> env = System.getenv();
for (String envName : env.keySet()) {
if(envName.contains(filter)) {
System.out.format("%s=%s%n",
envName,
env.get(envName));
}
}
}
}
This application iterates over your environment variables and prints out the ones that contain the String
of characters passed as a command line argument.
$TOOLCHAIN_DIR/bin
is present on your PATH
.
To verify this, run the following command:
x86_64-linux-musl-gcc
You should see output similar to the following:
x86_64-linux-musl-gcc: fatal error: no input files
compilation terminated.
javac EnvMap.java
native-image --static --libc=musl EnvMap
This produces a native executable with statically linked system libraries. You can pass other arguments before a class or JAR file.
With GraalVM Native Image you can build a mostly-static native executable that statically links everything except libc
.
Statically linking all your libraries except libc
ensures your application has all the libraries it needs to run on any Linux libc
-based distribution.
To build a mostly-static native executable, use this command:
native-image -H:+StaticExecutableWithDynamicLibC [other arguments] <Class>
To build a mostly-static native executable for the above EnvMap
demo, run:
native-image -H:+StaticExecutableWithDynamicLibC EnvMap
This produces a native executable that statically links all involved libraries (including JDK shared libraries) except for libc
.
This includes zlib
. Also, depending on the user’s code, it may link libstdc+
and libgcc
.
One way to check what dynamic libraries your application depends on is to run ldd
with the native executable, for example, ldd helloworld
.
A fully static native executable gives you the most flexibility to choose a base container image—it can run on anything including a FROM scratch
image.
A mostly-static native executable requires a container image that provides libc
, but has no additional requirements.
In both cases, choosing the base container image generally depends on your native executable’s specific requirements.