January 2026 · Java 25 / Jlink
Custom Java Runtimes with jlink — Slashing Container Footprints
How to build hyper-optimized Docker images using Java 25 and jlink, contrasting modular Plain Java micro-runtimes against Spring Boot container setups to slash image sizes from hundreds of megabytes down to tens.
The article was written by (a never version of) the same model that generated the code.
The Bloated Container Dilemma
Deploying Java applications into cloud-native Kubernetes clusters frequently suffers from the "fat container" tax. Standard production deployments often rely on full JRE base images—such as eclipse-temurin:25.0.1_8-jre—which weigh in at over 450MB before application artifacts are even copied.
For modern microservices and serverless functions, carrying hundreds of megabytes of unused JDK modules (legacy desktop GUI toolkits, sound drivers, XML bind implementations, and management instrumentation) results in:
- High registry transfer latency: Slower cold pulls across autoscaling nodes.
- Expanded attack surface: Hundreds of unneeded classes vulnerable to automated CVE scanners.
- Excess memory footprints: Unnecessary runtime metadata resident in RAM.
Using jlink (the Java Linker), we can assemble only the platform modules strictly necessary for our application, generating a custom JRE that drops container footprints by 70% to 90%.
Modular Plain Java: The Micro-Runtime Dream
For modular applications with a clean module-info.java descriptor, jlink offers complete, end-to-end runtime assembly. Because module boundaries and transitive dependencies are explicitly declared, the linker can combine both the application JAR and the JVM runtime into a single, cohesive bundle with a native executable launcher.
In the reference demonstrator's plain-java-jlink project, a multi-stage Docker build achieves this in two concise phases:
FROM eclipse-temurin:25.0.1_8-jdk AS builder
COPY build/libs/plain-java-jlink-1.0.0-SNAPSHOT.jar /app.jar
# Run jlink with optimization flags
RUN jlink \
--module-path /app.jar:$JAVA_HOME/jmods \
--add-modules lazarius.borg.plain \
--launcher runapp=lazarius.borg.plain/lazarius.borg.plain.App \
--output /javaruntime \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress zip-2
FROM debian:stable-slim
COPY --from=builder /javaruntime /javaruntime
ENTRYPOINT ["/javaruntime/bin/runapp"]
By stripping debug symbols, documentation, and header files, and applying level-2 zip compression, the entire operating runtime plus application shrinks to approximately 55 MB—a tiny fraction of standard Java images.
Taming Spring Boot: A Custom JRE for Non-Modular Apps
Enterprise applications frequently leverage frameworks like Spring Boot. Because Spring Boot relies heavily on dynamic classpath scanning, reflection, and third-party transitive libraries that lack module-info.java descriptors, fully modularizing a Spring Boot application is often impractical.
However, you do not need a fully modular application to reap the benefits of jlink. The winning strategy is to use jlink to assemble a bespoke, slim JRE base containing only the platform modules that Spring Boot Web infrastructure requires at runtime, and then execute the standard fat JAR on top of it:
FROM eclipse-temurin:25.0.1_8-jdk AS builder
# Assemble a custom JRE containing only modules needed by Spring Boot Web
RUN jlink \
--add-modules java.base,java.logging,java.naming,java.desktop,\
java.management,java.security.jgss,java.instrument,java.sql,jdk.unsupported \
--output /javaruntime \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress zip-2
FROM debian:stable-slim
COPY --from=builder /javaruntime /javaruntime
COPY build/libs/springboot-jlink-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT ["/javaruntime/bin/java", "-jar", "/app.jar"]
This approach bypasses module-path complexities while still purging hundreds of megabytes of superfluous JDK modules from the production image.
The Repository
The complete demonstrator source code—including Maven and Gradle build configurations, Dockerfiles, and benchmarking scripts—is published on GitHub:
github.com/lazarius-borg/jlink_demonstrator
Demonstration projects built for Java 25 with Eclipse Temurin JDK and Docker multi-stage builds.