π₯ Why Multi-stage Docker Builds Matter
When deploying Spring Boot applications, using a traditional single-stage Dockerfile results in large, insecure images containing unnecessary build tools like Maven or Gradle. Multi-stage builds help you:
- πͺΆ Reduce image size
- π Improve security by excluding build dependencies
- π¦ Separate build and runtime concerns
This guide walks you through creating a clean, production-ready Docker image using multi-stage builds for your Spring Boot app.

π¦ Project Setup
We’ll continue using the same Spring Boot app with package:com.kscodes.springboot.containers
You can follow this structure:
1 2 3 4 5 6 7 8 |
springboot-app/ βββ src/ βββ pom.xml βββ Dockerfile βββ .dockerignore |
βοΈ Multi-stage Dockerfile
Hereβs a Dockerfile using two stages: builder and runtime.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# -------- Stage 1: Build the JAR -------- FROM eclipse-temurin:21-jdk AS builder WORKDIR /app # Copy everything and build COPY . . RUN ./mvnw clean package -DskipTests # -------- Stage 2: Slim runtime -------- FROM eclipse-temurin:21-jre WORKDIR /app # Copy only the JAR from the builder image COPY --from=builder /app/target/*.jar app.jar # Expose port and run the app EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] |
This results in a clean image with only the Spring Boot JAR file β no Maven, no source files, no unnecessary layers.
π .dockerignore
To avoid bloating your Docker context:
1 2 3 4 5 6 7 8 |
target/ .git/ .idea/ *.iml *.md |
π¨ Build & Run
1 2 3 4 5 |
docker build -t springboot-multistage . docker run -p 8080:8080 springboot-multistage |
Open http://localhost:8080 and verify your app is running.
π Compare Image Sizes
Letβs compare using the command below:
1 2 3 4 |
docker images | grep springboot |
Typical results:
Image Type | Size |
---|---|
Single-stage | 480 MB+ |
Multi-stage | ~150 MB |
Multi-stage builds often shrink the image by 60β70%.
π‘οΈ Security Benefits
Multi-stage builds also:
- Remove build tools (Maven, Gradle)
- Eliminate vulnerable dependencies
- Reduce attack surface
Great for passing container vulnerability scans (Snyk, Trivy, etc.)
β‘ Layer Caching Tip (Optional)
For faster rebuilds, separate dependency copy:
1 2 3 4 5 6 7 8 9 |
COPY pom.xml . COPY src/main/resources/ /app/resources RUN ./mvnw dependency:go-offline COPY src/ /app/src RUN ./mvnw clean package -DskipTests |
This allows Docker to cache dependencies if your code changes, speeding up builds.
β Best Practices
Practice | Why It Matters |
---|---|
Use Alpine base images | Smaller, faster |
Use jre in runtime image | Lower memory footprint |
Tag images semantically | Avoid latest in production |
Donβt hardcode secrets | Use environment variables instead |
π Summary
In this guide, you learned how to create a multi-stage Docker build for Spring Boot thatβs optimized, secure, and production-ready. By separating the build and runtime environments, you drastically reduce image size and attack surface β setting a strong foundation for deployment to Kubernetes or cloud platforms. Hope this helps.