Packaging Java Applications with jmod and jpackage for Modular Deployments

Illustration for Packaging Java Applications with jmod and jpackage for Modular Deployments
By Last updated:

Many Java developers struggle with delivering applications in a way that is both user-friendly and production-ready. A common pain point arises when developers modularize their applications but still distribute them as plain JARs, leaving end-users to deal with the classpath or module path manually. Others ship fat JARs, which undermine the benefits of JPMS.

This is where jmod and jpackage step in. Together, they allow developers to distribute self-contained, modular, and installable applications. jmod lets you package reusable Java modules, while jpackage helps create native installers and application bundles for smooth distribution.

Think of it like preparing a meal for guests: jmod is about carefully preparing ingredients into sealed containers (modules), while jpackage is about serving the complete dish with plates, cutlery, and everything needed to consume it.


Understanding jmod

What is a jmod file?

A .jmod file is a packaging format introduced in Java 9. It is similar to a JAR, but designed for modules, containing:

  • Compiled classes
  • Native libraries
  • Configuration files
  • Man pages or legal notices

Unlike JARs, jmod files are not intended for runtime distribution, but rather for creating runtime images with tools like jlink.


Example: Creating a jmod file

Assume we have a module com.example.lib.

module-info.java

module com.example.lib {
    exports com.example.lib.api;
}

Compile the module:

javac -d out --module-source-path src $(find src -name "*.java")

Create a .jmod file:

jmod create   --class-path out/com.example.lib   --module-version 1.0   libs/com.example.lib.jmod

This produces com.example.lib.jmod, which can later be used in a custom runtime image.


Understanding jpackage

What is jpackage?

jpackage, introduced in Java 14, builds installable application bundles (e.g., .exe for Windows, .pkg for macOS, .deb for Linux). It ensures users can install and run your Java app like any native application.

Key benefits:

  • Simplifies installation for end-users
  • Packages a custom runtime with your application
  • Provides OS-native experience (icons, shortcuts, uninstallers)

Example: Using jpackage

Suppose we have a modular application com.example.app.

Command to package:

jpackage   --input out   --main-jar com.example.app.jar   --main-class com.example.app.Main   --name MyApp   --type dmg   --icon app-icon.icns

This creates a macOS .dmg installer for MyApp. Similar commands work for Windows (--type exe) and Linux (--type deb or --type rpm).


Best Practices & Pitfalls

Best Practices

  • Use jmod for reusable libraries, not direct deployment
  • Always include module-info.java to enforce strong encapsulation
  • Combine jmod + jlink + jpackage for full modular deployment
  • Test installers across OS platforms

Pitfalls

  • Using .jmod as a runtime artifact (incorrect usage)
  • Forgetting to bundle native dependencies in jmod
  • Overpacking installers with unnecessary modules → bloated installers
  • Assuming jpackage replaces jlink (they complement each other)

What's New in Java Versions?

  • Java 5–8 → N/A (Modules introduced in Java 9)
  • Java 9 → Introduction of JPMS and .jmod packaging
  • Java 11 → Refinements in jlink and jmod workflows
  • Java 14 → Introduction of jpackage as an incubator tool
  • Java 16jpackage stabilized and production-ready
  • Java 17 → Performance and usability improvements
  • Java 21 → No significant updates across Java versions for this feature

Real-World Analogy

  • jmod: Think of it as vacuum-sealed ingredients—well-structured, portable, and ready to be used in a recipe.
  • jpackage: Think of it as a ready-to-serve meal complete with packaging, cutlery, and delivery to the end user.

Summary & Key Takeaways

  • jmod is used for creating reusable module artifacts.
  • jpackage is used for distributing installable application bundles.
  • Together, they ensure clean modular workflows, from libraries to runtime images to end-user applications.
  • Avoid misusing jmod as a runtime deliverable.
  • Always test cross-platform installers before shipping.

FAQ: jmod and jpackage

1. What is the difference between JAR, jmod, and jpackage?
JARs are generic archives, .jmod is a module packaging format, and jpackage creates native installers.

2. Why shouldn’t I distribute jmod files directly?
They are intended for jlink and tooling, not as runtime deliverables.

3. Can jpackage include a custom runtime?
Yes, it bundles a jlink-generated runtime image automatically.

4. Does jpackage support icons and shortcuts?
Yes, it integrates OS-native features like icons, menus, and uninstallers.

5. When should I use jlink vs jpackage?
Use jlink to create custom runtimes, jpackage to ship installable applications.

6. Can I use jpackage for non-modular JARs?
Yes, but modular applications benefit the most.

7. Does jmod support native libraries?
Yes, you can include .so, .dll, or .dylib files inside a .jmod.

8. Can I migrate legacy fat JARs to jpackage?
Yes, but it’s best to modularize first for full benefits.

9. Does Spring Boot work with jpackage?
Yes, though Spring Boot apps may need tweaks for module-info compatibility.

10. Is jpackage cross-platform?
No, you must run jpackage on each target OS to build installers for that OS.