Multi-Stage Dockerfile for Spring Boot: Smaller, Faster Images
Fast recall
Quick revision
Core answer
Whether you know Docker image optimization techniques and understand why large images are a problem in CI/CD pipelines
Using a JDK image in the final stage (includes compiler, tools not needed at runtime) or copying the fat JAR without layer splitting
Explain Spring Boot's layered JAR format (application code changes more often than dependencies), show COPY --from=builder for selective artifact copying, and mention .dockerignore
Interview-ready guide
Interview answer
A multi-stage Dockerfile builds your application in one Docker image and then copies only the compiled output into a minimal second image. The result: a production image that has no JDK, no Maven binary, and no source code — just the JRE and the artifact.
Stage 1 — build stage
- The build stage uses a full JDK image (e.g.,
eclipse-temurin:21-jdk) with Maven or Gradle; you copy the POM and download dependencies first, before copying source files — Docker caches the dependency layer and only re-downloads when the POM changes - A common optimization:
COPY pom.xml .thenRUN mvn dependency:go-offlinebeforeCOPY src ./src— a code-only change hits the cached dependency layer and the build skips the download phase entirely - The build stage image grows to 400–600 MB with all build tooling included — it never reaches production; only the
FROM scratchor minimal second stage does
Stage 2 — runtime stage
- Use a JRE-only image for the runtime stage (e.g.,
eclipse-temurin:21-jre-alpine), which is 60–80 MB versus 200+ MB for the full JDK — a smaller image means faster pulls, a smaller attack surface, and less container startup time COPY --from=build /app/target/app.jar /app/app.jarcopies only the JAR from the build stage; the entire Maven cache, source tree, and JDK binaries never enter the final image- For Spring Boot, enable layered JARs in
pom.xmland extract layers in the Dockerfile — Spring Boot splits the JAR into dependency, Spring Boot loader, and application layers; Docker caches the stable layers and rebuilds only the application layer on code changes
Security and production hardening
- Add
USER appuserin the runtime stage to run the JVM as a non-root user — a container compromise escalates less far if the process does not have root inside the container - Pin the base image to a specific digest in production, not just a tag —
eclipse-temurin:21-jre-alpinecan change when a new Alpine version ships; pinning the digest guarantees reproducibility
Multi-Stage Dockerfile for Spring Boot: Smaller, Faster Images
Direct Answer
Use a multi-stage Dockerfile: stage 1 uses a JDK image to build the JAR with Maven/Gradle, stage 2 uses a minimal JRE image (eclipse-temurin:21-jre-alpine) to run only the extracted layers. This reduces image size from 500MB+ to ~150MB by excluding build tools and keeping only runtime dependencies.
Common Mistakes & Pitfalls
Using a JDK image in the final stage (includes compiler, tools not needed at runtime) or copying the fat JAR without layer splitting
Check your understanding
Answer these without looking back at the article.
- How does Spring Boot's layered JAR format improve Docker layer caching?
- What JVM flags should you set when running inside a Docker container with memory limits?
- How does BuildKit improve Docker build performance compared to the classic builder?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.