Mastering jlink: How to Create Custom Java Runtime Images for Modular Applications

Illustration for Mastering jlink: How to Create Custom Java Runtime Images for Modular Applications
By Last updated:

A common frustration Java developers face when deploying applications is shipping the entire JDK or JRE, even when their program uses only a fraction of the modules. This not only increases deployment size but also creates security risks by including unnecessary modules. With the Java Platform Module System (JPMS), introduced in Java 9, developers can leverage the jlink tool to build custom runtime images containing only the modules their applications require.

Think of this like packing for travel: instead of carrying your entire wardrobe, you carefully select only the clothes you’ll actually wear. jlink allows you to travel light in production, improving performance, security, and maintainability.

jlink is a command-line tool included with the JDK that assembles and optimizes a set of modules into a custom runtime image. This runtime image looks like a stripped-down JRE, but it’s tailored specifically for your application.

Key benefits:

  • Smaller runtime footprint
  • Faster startup times
  • Reduced attack surface by excluding unused modules
  • Easy deployment without requiring a full JDK/JRE

Example: Creating a Custom Runtime Image

Step 1: Create a Modular Project

Suppose we have a module com.example.app that depends on java.sql:

module-info.java

module com.example.app {
    requires java.sql;
}

Step 2: Compile the Module

javac -d out --module-source-path src $(find src -name "*.java")
jlink   --module-path $JAVA_HOME/jmods:out   --add-modules com.example.app   --output custom-runtime

This creates a custom-runtime/ directory containing the required runtime image.

Step 4: Run the Application

custom-runtime/bin/java -m com.example.app/com.example.app.Main

Best Practices & Pitfalls

Best Practices

  • Always verify module dependencies before linking
  • Use jdeps to analyze module usage
  • Include only what you need to minimize runtime size
  • Automate runtime image creation in CI/CD pipelines

Pitfalls

  • Forgetting to include transitive dependencies → runtime errors
  • Using automatic modules → reduces reliability of the image
  • Attempting to run non-modular applications directly with jlink

What's New in Java Versions?

  • Java 5–8 → N/A (Modules introduced in Java 9)
  • Java 9 → Introduction of JPMS and jlink
  • Java 11 → Better tooling, long-term support for jlink workflows
  • Java 17 → Security and performance improvements for modular builds
  • Java 21 → No significant updates across Java versions for this feature

Real-World Analogy

Think of jlink as a meal prep kit: instead of buying an entire grocery store, you get only the pre-measured ingredients you need to cook one dish. This makes your life simpler, faster, and more efficient.


Summary & Key Takeaways

  • jlink builds custom runtime images tailored to your app
  • Greatly reduces deployment size and improves performance
  • Use jdeps to analyze dependencies before linking
  • Avoid automatic modules when possible
  • Perfect for microservices, containers, and embedded systems

1. What is the difference between the classpath and module path?
Classpath loads everything blindly, while module path enforces module boundaries and visibility.

2. Why do I get “package is not visible” errors with modules?
Because JPMS enforces strong encapsulation — you must explicitly exports and requires.

3. What is the purpose of requires transitive?
It allows dependent modules to implicitly access transitive dependencies.

4. How do open and opens differ in reflection?
open opens the whole module, while opens targets specific packages.

5. What are automatic modules, and should I use them?
JARs on the module path without module-info become automatic modules. They help migration but should not be used long-term.

6. How does JPMS improve security compared to the classpath?
By enforcing encapsulation and preventing accidental access to internal APIs.

7. When should I use jlink vs jmod?
jmod packages modular artifacts, while jlink creates a full runtime image.

8. Can I migrate a legacy project to modules incrementally?
Yes, start with modularizing small components and using automatic modules as a bridge.

9. How do I handle third-party libraries that are not modularized?
Place them on the classpath or module path as automatic modules.

10. Do frameworks like Spring and Hibernate support modules?
Yes, but adoption varies. Many work well with modules but may need configuration.