Containerize a Native Executable and Run in a Container

Containers provide the flexibility of development environments to match a production environment, to help isolate your application, and to minimize overhead. For self-contained executables, generated with GraalVM Native Image, containers are an obvious deployment choice.

To support container-based development, there are several GraalVM container images available, depending on the platform, the architecture, the Java version, and the edition:

This guide shows how to containerise a native executable for your Java application. You will use a GraalVM container image with Native Image to compile a Java application ahead-of-time into a native executable.

Download a Sample Application

This guide uses the Spring Boot 3 Native Image Microservice example. The example is a minimal REST-based API application, built on top of Spring Boot 3. If you call the HTTP endpoint /jibber, it will return some nonsense verse generated in the style of the Jabberwocky poem, by Lewis Carroll.

Prerequisite

Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.

  1. Install and run a Docker-API compatible container runtime such as Rancher Desktop, Docker, or Podman.

  2. Clone the GraalVM Demos repository:
     git clone https://github.com/graalvm/graalvm-demos
    
  3. Change directory to spring-native-image/:
     cd spring-native-image
    

Build and Run as a Native Executable

With the built-in support for GraalVM Native Image in Spring Boot 3, it has become much easier to compile a Spring Boot 3 application into a native executable.

  1. Build a native executable:
     ./mvnw native:compile -Pnative
    

    The -Pnative profile is used to generate a native executable for your platform. This will generate a native executable called benchmark-jibber in the target/ directory.

  2. Run the native executable and put it into the background by appending &:
     ./target/benchmark-jibber &
    
  3. Call the endpoint using curl:
     curl http://localhost:8080/jibber
    

    You should get a random nonsense verse.

  4. Bring the application to the foreground using fg, and then enter <CTRL-c> to terminate the application.

Containerise the Native Executable

The generated native executable is platform-dependent.

  1. Containerise the native executable using the following command:

  2. Run the application:
     docker run --rm --name native -p 8080:8080 jibber-benchmark:native.0.0.1-SNAPSHOT
    
  3. From a new terminal window, call the endpoint using curl:
     curl http://localhost:8080/jibber
    

    It should generate a random nonsense verse.

  4. To stop the application, first get the container id using docker ps, and then run:
     docker rm -f <container_id>
    
  5. To delete the container images, first get the image id using docker images, and then run:
     docker rmi -f <image_1_id> <image_n_id>
    

Summary

In this guide, you saw how to use GraalVM container images to containerize a native executable for your Java application.

With GraalVM Native Image you can build a statically linked native executable by packaging the native executable directly into tiny containers such as scratch or distroless images.