How to Package Spring Boot Apps into Native Installers with JPackage

This guide explains how to use the JPackage tool introduced in JDK 14/16 to create platform‑specific native installers for Spring Boot applications, covering environment setup, custom JRE creation with jlink, Maven integration, and examples for Windows, macOS, and Linux.

macrozheng
macrozheng
macrozheng
How to Package Spring Boot Apps into Native Installers with JPackage

Introduction

Since JDK 14 (standard from JDK 16), the jpackage tool can bundle a Java application into a native installer with an embedded custom JRE, removing the need for a pre‑installed Java runtime.

jpackage Overview

What is jpackage

jpackage resides in $JAVA_HOME/bin and can generate native installers (.exe/.msi for Windows, .dmg/.pkg for macOS, .deb/.rpm for Linux). It can also create a custom runtime image via jlink.

Advantages

No pre‑installed JRE/JDK required.

Consistent runtime version.

Automatic launcher generation.

One‑click generation for all platforms.

Environment Preparation

JDK version

Use JDK 17 or newer; jpackage becomes standard in JDK 16.

jpackage --version

Platform‑specific tools

Windows: Install WiX Toolset 3.11+ and add its bin directory to PATH.

macOS: Install Xcode command‑line tools with xcode-select --install.

Linux:

Debian/Ubuntu: sudo apt-get install fakeroot RedHat/CentOS:

sudo yum install rpm-build

Spring Boot Project Preparation

Project structure example

my-springboot-app/
├── src/
│   └── main/
│       ├── java/
│       └── resources/
├── pom.xml
└── target/
    └── my-app-1.0.0.jar

Build executable JAR

# Maven
mvn clean package

# Gradle
gradle clean build

The resulting JAR is executable by default in Spring Boot.

Packaging with jpackage

Basic command (Windows example)

jpackage \
  --input target \
  --name MySpringBootApp \
  --main-jar my-app-1.0.0.jar \
  --main-class org.springframework.boot.loader.JarLauncher \
  --type msi \
  --app-version 1.0.0 \
  --vendor "My Company" \
  --description "Enterprise Spring Boot application" \
  --icon src/main/resources/app-icon.ico \
  --win-dir-chooser \
  --win-menu \
  --win-shortcut

Parameter description

--input

: Directory containing the JAR and its dependencies. --name: Application name. --main-jar: Main JAR file. --main-class: Entry class; for Spring Boot use org.springframework.boot.loader.JarLauncher. --type: Installer type (msi, exe, dmg, pkg, deb, rpm). --app-version: Version number. --icon: Platform‑specific icon file. --win-dir-chooser, --win-menu, --win-shortcut: Windows UI options.

Custom JRE with jlink

To reduce installer size, create a trimmed runtime containing only required modules.

Step 1: List required modules

jdeps --list-deps target/my-app-1.0.0.jar

Step 2: Create custom JRE

jlink \
  --add-modules java.base,java.logging,java.sql,java.naming,java.desktop,java.xml,java.management \
  --output custom-jre \
  --strip-debug \
  --no-header-files \
  --no-man-pages \
  --compress=2

Step 3: Package with custom runtime

jpackage \
  --input target \
  --name MySpringBootApp \
  --main-jar my-app-1.0.0.jar \
  --main-class org.springframework.boot.loader.JarLauncher \
  --type msi \
  --runtime-image custom-jre \
  --app-version 1.0.0 \
  --vendor "My Company"
Note: Spring Boot applications often depend on many modules; it is advisable to first package without JRE trimming and only optimise after confirming functionality.

Platform‑specific examples

Windows (MSI)

jpackage \
  --input target \
  --name MyApp \
  --main-jar my-app-1.0.0.jar \
  --main-class org.springframework.boot.loader.JarLauncher \
  --type msi \
  --app-version 1.0.0 \
  --icon src/main/resources/app.ico \
  --win-dir-chooser \
  --win-menu \
  --win-shortcut \
  --win-menu-group "My Application"

macOS (DMG)

jpackage \
  --input target \
  --name MyApp \
  --main-jar my-app-1.0.0.jar \
  --main-class org.springframework.boot.loader.JarLauncher \
  --type dmg \
  --app-version 1.0.0 \
  --icon src/main/resources/app.icns \
  --mac-package-name "com.mycompany.myapp" \
  --mac-package-identifier "com.mycompany.myapp"

Linux (DEB)

jpackage \
  --input target \
  --name myapp \
  --main-jar my-app-1.0.0.jar \
  --main-class org.springframework.boot.loader.JarLauncher \
  --type deb \
  --app-version 1.0.0 \
  --icon src/main/resources/app.png \
  --linux-shortcut \
  --linux-menu-group "Development"

Integration into Maven build

Add an exec-maven-plugin configuration to pom.xml so that jpackage runs during the package phase.

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.1.0</version>
      <executions>
        <execution>
          <id>jpackage</id>
          <phase>package</phase>
          <goals>
            <goal>exec</goal>
          </goals>
          <configuration>
            <executable>jpackage</executable>
            <arguments>
              <argument>--input</argument>
              <argument>target</argument>
              <argument>--name</argument>
              <argument>MySpringBootApp</argument>
              <argument>--main-jar</argument>
              <argument>${project.build.finalName}.jar</argument>
              <argument>--main-class</argument>
              <argument>org.springframework.boot.loader.JarLauncher</argument>
              <argument>--type</argument>
              <argument>msi</argument>
              <argument>--app-version</argument>
              <argument>${project.version}</argument>
              <argument>--vendor</argument>
              <argument>My Company</argument>
              <argument>--win-dir-chooser</argument>
              <argument>--win-menu</argument>
              <argument>--win-shortcut</argument>
            </arguments>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Run mvn clean package; the installer will be created in the project root.

Conclusion

jpackage enables distribution of Java applications as native installers, from simple command‑line usage to full Maven integration, allowing developers to balance installer size, deployment convenience, and runtime performance.

mavenSpring BootJLinkJava Packagingjpackagenative installer
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.