dockerdocker-multi-stage-build

What benefits does having a separate "builder" Docker layer have?


Consider this Dockerfile:

FROM amazoncorretto:17-alpine-jdk AS builder

WORKDIR /app

COPY . .

RUN apk add --no-cache maven && \
    mvn package -Dmaven.test.skip=true

FROM amazoncorretto:17-alpine-jdk

WORKDIR /app

COPY --from=builder /app/target/dynamic-gateway-0.0.1-SNAPSHOT.jar .

EXPOSE 8080

CMD ["java", "-jar", "dynamic-gateway-0.0.1-SNAPSHOT.jar"]

Why do I need these two separate layers? Why can't it be just

FROM amazoncorretto:17-alpine-jdk

WORKDIR /app

COPY . .

RUN apk add --no-cache maven && \
    mvn package -Dmaven.test.skip=true

CMD ["java", "-jar", "dynamic-gateway-0.0.1-SNAPSHOT.jar"]

I read some Docker posts on SO (namely, 1, 2, 3), but I still don't understand. Will the first version's "builder" half be reused for later builds? If so, what and when exactly will be reused? Every time a Dockerfile needs a JDK 17 with Maven? Does the first image even make sense 'cause, I'll admit, generative AI gave me a hand with that (it works, though)?


Solution

  • The purpose of a multi-stage build is to reduce the size of the final image produced by docker build.

    Images that come equipped with build tools are usually larger than the ones required to run your code. In your case it's the same image. You could search the web to try and find a Java JRE image that's smaller than 140MB and use that one instead of amazoncorretto:17-alpine-jdk.

    In addition, you don't want all your code and intermediary files generated by the build in your final image. You typically only need the executable JAR file. So what do you do? You only copy that file from the previous stage (called "builder" in your case), thus further reducing the final image size.