Docker cookbook · Intermediate
Build a layered image for a Spring Boot app
Package a Java service so dependency layers are cached and only your classes change on rebuild.
Approach
Build the jar in a JDK stage, extract Spring Boot's layers (dependencies, loader, snapshot dependencies, application) and copy them separately into a JRE runtime image, so dependency layers are reused across builds.
Solution
FROM eclipse-temurin:25-jdk AS build
WORKDIR /src
COPY . .
RUN ./mvnw -q -DskipTests package && \
java -Djarmode=tools -jar target/app.jar extract --layers --launcher --destination /extracted
FROM eclipse-temurin:25-jre
WORKDIR /app
RUN useradd --uid 10001 --no-create-home app
COPY --from=build /extracted/dependencies/ ./
COPY --from=build /extracted/spring-boot-loader/ ./
COPY --from=build /extracted/snapshot-dependencies/ ./
COPY --from=build /extracted/application/ ./
USER 10001
ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75"
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Pitfalls
- One fat-jar layer: every code change re-uploads all dependencies.
- Heap settings ignoring the container memory limit; use
MaxRAMPercentage. - Running Maven without a dependency cache in CI, making builds slow.
Variations
- Cloud Native Buildpacks (
mvn spring-boot:build-image) produce layered images without a Dockerfile. - A
jlinkcustom runtime for smaller images.
Further reading: Spring Boot: Dockerfiles
Reference: https://docs.spring.io/spring-boot/reference/packaging/container-images/dockerfiles.html
More Docker recipes
- Add a healthcheck to an HTTP service
- Build a production image for a Node.js service
- Build a production image for a Python web app
- Build, cache and push images in GitHub Actions
- Build one image tag for amd64 and arm64
- Configure nginx from environment variables
- Develop with Compose and live reload
- Make containers cope with dependencies that start slowly