Optimising Spring Boot Docker Images: A Guide to Lean, Secure, and Efficient Containers
If you’re deploying Spring Boot apps in Docker containers, you’ve probably faced the dreaded 1GB+ image size, slow CI/CD pipelines, or even worse – security vulnerabilities in production.
But here’s the good news: with a few best practices, you can shrink your images, speed up deployments, and lock down your containers. Let’s dive in.
Why Optimise Your Docker Images?
- Smaller Images: Faster deployments, lower cloud storage costs.
- Faster Builds: Improved developer productivity.
- Enhanced Security: Fewer vulnerabilities and reduced attack surface.
(Fun fact: A well-optimised Spring Boot image can be under 200MB!)
The Optimisation Checklist
1. Use Multi-Stage Builds
Multi-stage builds allow you to separate the build environment from the runtime environment, ensuring only the necessary artifacts make it into the final image.
# Build stage
FROM maven:3.9.6-eclipse-temurin-21 AS builder
WORKDIR /app
COPY . .
RUN mvn clean package
# Runtime stage
FROM eclipse-temurin:21-jre-jammy
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
Key Benefit: Removes build tools and dependencies, reducing image size by 300MB+.
2. Leverage Spring Boot Layer Tools
Spring Boot 2.3+ introduced layer tools, which split your JAR into logical layers:
- Dependencies (rarely change)
- Snapshot dependencies (change occasionally)
- Application code (changes frequently)
This allows Docker to cache layers independently, speeding up rebuilds.
Enable in pom.xml:
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
Result: Up to 70% faster rebuilds when only application code changes.
3. Choose the Right Base Image
The base image you choose has a huge impact on size and security. Here’s a quick comparison:
| Base Image | Size | Use Case |
|------------------|--------|---------------------------|
| eclipse-temurin | 150MB | General-purpose |
| distroless | 75MB | Security-critical apps |
Recommended by LinkedIn
| Alpine Linux | 100MB | Avoid if using JNI/native |
Pro Tip: Use distroless for production – it’s minimal and secure.
4. Run as a Non-Root User
Running containers as root is a major security risk. Always create and switch to a non-root user:
RUN addgroup --system spring && adduser --system --ingroup spring spring
USER spring:spring
5. Optimise JVM Memory Settings
Configure the JVM to respect container memory limits:
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75%"
This ensures your app plays nicely with Kubernetes and avoids out-of-memory crashes.
Tools to Supercharge Your Workflow
- `dive`: Analyse image layers and identify bloat.
- `trivy`: Scan images for vulnerabilities.
- `spring-boot-build-image`: Automate optimisations with Paketo Buildpacks.
Real-World Impact
A recent project saw:
- 📉 **85% smaller images** (1.2GB → 180MB)
- ⚡ **65% faster CI/CD pipelines**
- 🔐 **0 critical CVEs** in production images
Your Action Plan
1. Audit your current images with docker scout.
2. Implement multi-stage builds.
3. Switch to a slim base image like distroless.
4. Add non-root users and JVM memory tweaks.
IT | Java | Python | Cloud | Data Science [ML - AI]
6mofurther, add, 6) Optimize Dockerfile Layers, compact the layers using && 7) Docker Caching 8) SpringBoot Native Images (GraalVM)
Very helpful