Containerise a Native Executable and Run in a Docker Container

Docker 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.

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.

Prerequisites

  1. Download and install the latest Oracle GraalVM from Downloads. The easiest option is to use SDKMAN!. Run the following command to install Oracle GraalVM for JDK 17:

     sdk install java 17.0.8-graal 
    
  2. Install and run a Docker-API compatible container runtime such as Rancher Desktop, Docker, or Podman.

  3. Clone the GraalVM Demos repository:

     git clone https://github.com/graalvm/graalvm-demos
    
  4. Change directory to the demo directory:

     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. Continue to Build a Static or Mostly-Static Native Executable guide to learn more.