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 you can use 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 all the shared libraries on which the native executable relies (zlib
, JDK-shared static libraries) except the standard C library, libc
. This is an alternative option to statically linking everything. Also, depending on the user’s code, it may link libstdc+
and libgcc
.
This approach is useful for deployment on a distroless container image.
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.
musl
toolchain, make
, and configure
zlib
libraryThe easiest way to install GraalVM is with SDKMAN!. For other installation options, visit the Downloads section.
To create statically linked applications with Native Image, you require a musl
toolchain with the zlib
library.
For the best compatibility, use musl-1.2.4 or later.
We recommend building musl
from source as shown below:
# Specify an installation directory for musl:
export MUSL_HOME=$PWD/musl-toolchain
# Download musl and zlib sources:
curl -O https://musl.libc.org/releases/musl-1.2.4.tar.gz
curl -O https://zlib.net/fossils/zlib-1.2.13.tar.gz
# Build musl from source
tar -xzvf musl-1.2.4.tar.gz
pushd musl-1.2.4
./configure --prefix=$MUSL_HOME --static
# The next operation may require privileged access to system resources, so use sudo
sudo make && make install
popd
# Install a symlink for use by native-image
ln -s $MUSL_HOME/bin/musl-gcc $MUSL_HOME/bin/x86_64-linux-musl-gcc
# Extend the system path and confirm that musl is available by printing its version
export PATH="$MUSL_HOME/bin:$PATH"
x86_64-linux-musl-gcc --version
# Build zlib with musl from source and install into the MUSL_HOME directory
tar -xzvf zlib-1.2.13.tar.gz
pushd zlib-1.2.13
CC=musl-gcc ./configure --prefix=$MUSL_HOME --static
make && make install
popd
With the requirements set up, create the demo.
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.
javac EnvMap.java
native-image --static --libc=musl EnvMap
This produces a native executable with statically linked system libraries.
Run it with ./envmap
.
You can confirm the application is fully statically linked using the ldd
command:
ldd EnvMap
The output should be “not a dynamic executable”.
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 --static-nolibc [other arguments] <Class>
To build a mostly-static native executable for the above EnvMap
demo, run:
native-image --static-nolibc EnvMap
This produces a native executable that statically links all involved libraries (including JDK-shared static 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 envmap
.
A fully static native executable gives you the most flexibility to choose a base container image—it can even run on a scratch
image.
A mostly-static native executable requires a container image that provides libc
, specifically glibc
, but has no additional requirements.
In both cases, choosing the base container image generally depends on your native executable’s specific requirements.