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.
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-layersoutputs 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.
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.
