Multi-stage Docker Builds for Production-ready Spring Boot Images

πŸ”₯ 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.

spring boot multi-stage docker build

πŸ“¦ Project Setup

We’ll continue using the same Spring Boot app with package:
com.kscodes.springboot.containers

You can follow this structure:

βš™οΈ Multi-stage Dockerfile

Here’s a Dockerfile using two stages: builder and runtime.

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:

πŸ”¨ Build & Run

Open http://localhost:8080 and verify your app is running.

πŸ“‰ Compare Image Sizes

Let’s compare using the command below:

Typical results:

Image TypeSize
Single-stage480 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:

This allows Docker to cache dependencies if your code changes, speeding up builds.

βœ… Best Practices

PracticeWhy It Matters
Use Alpine base imagesSmaller, faster
Use jre in runtime imageLower memory footprint
Tag images semanticallyAvoid latest in production
Don’t hardcode secretsUse 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.