We still have the "works on my machine" problem: building the JAR depends on your local Java and Maven. The fix: build the JAR inside Docker too.
But if we used one image containing Maven, the JDK and our app, we would ship all those build tools to production — hundreds of megabytes that are never used at runtime.
Multi-stage builds solve this. A Dockerfile can contain multiple FROM instructions. Each FROM starts a new stage, and only the last stage becomes the final image. Earlier stages are used and thrown away.
<aside> 💭
Watch only the 6th best practice explaining multi-stage builds.
</aside>
https://youtu.be/8vXoMqWgbQQ?si=iwL_nGqmpIDJn7g4&t=656
The following Dockerfile demonstrates a simple multi-stage build for a Java application.
# Stage 1: build the JAR (needs JDK, Maven and your source code)
FROM maven:3-eclipse-temurin-25 AS build
WORKDIR /app
COPY . .
RUN mvn package -DskipTests
# Stage 2: run the JAR (JRE is enough)
FROM eclipse-temurin:25-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
AS build gives the first stage a name.COPY --from=build copies the JAR out of the first stage into the final image.-DskipTests skips the tests during the image build. Tests should run before you build the image, not inside it.One more thing: COPY . . copies everything in your project — including target/ and .git/. Exclude them with a .dockerignore file in your project root:
target/
.git/
<aside> ⌨️
Hands on: Build the multi-stage image and run docker images. Compare its size with the JDK base image you used for hello-java — expect roughly 500 MB or more with build tools included, versus around 300–400 MB for the multi-stage result.
</aside>
With COPY . ., every code change invalidates the cache and Maven downloads all dependencies again. Copy pom.xml first and download dependencies as a separate, cached layer:
FROM maven:3-eclipse-temurin-25 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
Now the dependency layer is only rebuilt when pom.xml changes. Code changes reuse it, and rebuilds go from minutes to seconds.
The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with ❤️ by the HackYourFuture community · Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.