2024-08-14dockerjavadevops
Multi-stage Dockerfiles that actually shrink your image
A Spring Boot image cut from 480MB to 190MB — the tricks that worked.
Multi-stage Dockerfiles that actually shrink your image
Every Java image starts life fat. Here's the pattern that consistently gets a Spring Boot service under 200MB without giving up JIT or debugging.
# --- build stage ---
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
COPY gradle gradle
COPY gradlew build.gradle settings.gradle ./
RUN ./gradlew --no-daemon dependencies
COPY src src
RUN ./gradlew --no-daemon bootJar
# --- extract layers ---
FROM eclipse-temurin:21-jre AS layers
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
# --- runtime ---
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=layers /app/dependencies/ ./
COPY --from=layers /app/spring-boot-loader/ ./
COPY --from=layers /app/snapshot-dependencies/ ./
COPY --from=layers /app/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Why it works:
- Layer extraction puts dependencies (which barely change) in their own layer, so a code-only rebuild pushes a tiny diff.
- Alpine JRE shaves ~120MB vs the default JRE image.
- No build tools in the final stage — no gradle, no jdk, no source.