How to Optimize Spring Boot Docker Images with Layered JARs and Multi‑Stage Builds
This article explains why a plain Dockerfile for a Spring Boot fat JAR is inefficient, introduces Spring Boot's layered JAR format, and provides a multi‑stage Dockerfile that leverages layer caching to speed up builds and reduce runtime overhead.
Why a Simple Dockerfile Is Problematic
Building a Docker image directly from a Spring Boot fat JAR adds unnecessary runtime overhead and makes rebuilds slow because the JAR and its dependencies are packed into a single layer. Each code change forces the entire layer to be rebuilt, preventing Docker cache reuse.
Spring Boot Layered JAR
Since Spring Boot 2.3, the Maven/Gradle plugins produce JAR files that contain explicit layer metadata.
The default layers are:
dependencies : regular compile‑time dependencies.
spring-boot-loader : classes under org/springframework/boot/loader.
snapshot-dependencies : snapshot versions of dependencies.
application : the application’s own classes and resources.
When the JAR is built, a layers.idx file records the order of these layers: dependencies, spring-boot-loader, snapshot-dependencies, application.
Inspecting Layers with Layertools
You can extract the layers locally to see their contents:
java -Djarmode=layertools -jar application.jar extractRecommended Multi‑Stage Dockerfile
The following Dockerfile separates the extraction of each layer into its own build stage, allowing Docker to cache unchanged layers:
FROM eclipse-temurin:21-jre AS builder
ARG JAR_FILE=target/application.jar
COPY ${JAR_FILE} app.jar
# Extract layers
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre
COPY --from=builder /layers/dependencies/ /app/dependencies/
COPY --from=builder /layers/spring-boot-loader/ /app/spring-boot-loader/
COPY --from=builder /layers/snapshot-dependencies/ /app/snapshot-dependencies/
COPY --from=builder /layers/application/ /app/application/
EXPOSE 8080
ENTRYPOINT ["java","-cp","/app/*","com.example.Main"]Build the image with:
docker build --build-arg JAR_FILE=target/application.jar .Each COPY corresponds to a layer extracted by layertools, so modifying only the application code invalidates the application layer while reusing the cached dependency layers.
Observing Cache Behaviour
After changing source code, rebuild the image and watch Docker reuse the cached layers for dependencies and the loader, dramatically reducing build time.
Reference
Container Images :: Spring Boot – https://docs.spring.io/spring-boot/reference/packaging/container-images/index.html
Eric Tech Circle
Backend team lead & architect with 10+ years experience, full‑stack engineer, sharing insights and solo development practice.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
