Speed Up Spring Boot Docker Builds with Jarmode Layered Images

Learn how to dramatically reduce Docker build times for Spring Boot applications by using the official Jarmode=tools layered jar feature, extracting layers, and employing multi‑stage Dockerfiles with optimized runtime settings, caching strategies, and optional security and performance enhancements.

Tech Musings
Tech Musings
Tech Musings
Speed Up Spring Boot Docker Builds with Jarmode Layered Images

During iterative development, frequent code changes and Docker rebuilds can become a bottleneck. Using Spring Boot’s built‑in jarmode=tools you can split the fat jar into separate layers, allowing Docker to cache unchanged parts and rebuild only the topmost layer.

What is Docker Layering?

Docker images consist of read‑only layers stacked like a cake. When only the application code changes, you only need to rebuild the top “application” layer instead of the whole image.

Spring Boot Jarmode Layered Jars

Spring Boot 2.3+ introduces jarmode=tools. Running

java -Djarmode=tools -jar my-app.jar list-layers

outputs the extractable layers:

dependencies : third‑party libraries

spring-boot-loader : Spring Boot loader

snapshot-dependencies : frequently changing snapshot libs

application : your own business code

Multi‑Stage Dockerfile

The first stage extracts the layers from the jar, the second stage assembles the final image using only the needed layers.

# Builder stage
FROM amazoncorretto:25.0.1-alpine3.21 AS builder
WORKDIR /builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted

# Runtime stage
FROM amazoncorretto:25.0.1-alpine3.21
WORKDIR /app
ENV TZ=Asia/Shanghai
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk update && \
    apk add --no-cache tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone && \
    mkdir -p /app/logs /app/temp && \
    rm -rf /var/cache/apk/*
COPY --from=builder /builder/extracted/dependencies/ ./
COPY --from=builder /builder/extracted/spring-boot-loader/ ./
COPY --from=builder /builder/extracted/snapshot-dependencies/ ./
COPY --from=builder /builder/extracted/application/ ./
EXPOSE 8080
ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/heapdump.hprof"
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} -jar application.jar"]

Note that the application.jar used in the runtime stage contains only business code; all dependencies are provided by the copied layer directories.

Why Layered Copying Improves Caching

Each COPY creates a new Docker layer. If the dependencies layer does not change (e.g., pom.xml stays the same), Docker reuses that layer, avoiding unnecessary rebuilds and reducing image size.

Potential Enhancements

Run the container as a non‑root user for security.

Add a HEALTHCHECK to verify service health.

Enable AOT caching ( -XX:AOTCache) for faster startup on Java 24+.

Use Class Data Sharing ( -XX:ArchiveClassesAtExit) for additional startup speed.

DockerSpring Bootmulti-stage-buildJarmodeLayered Images
Tech Musings
Written by

Tech Musings

Capturing thoughts and reflections while coding.

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.