Skip to main content

Docker: Multi-stage Dockerfile

In the previous exercise, we created a Dockerfile for a Spring Boot application.

We will reuse the existing Dockerfile and modify it to use multi-stage builds. The goal of multi-stage builds is to reduce the size of the final Docker image by separating the build environment from the runtime environment.

Goal:

The goal in this exercise, is to create a multi-stage Dockerfile for a Spring Boot application, to reduce the final image size.

Step 1: Check image size

Before we start, let's check the size of the existing Docker image:

docker images spring-api

This command will show the size of the spring-api image, you will get an ouput like this:

REPOSITORY TAG IMAGE ID CREATED SIZE
spring-api latest 35556a893585 2 minutes ago 1.23GB

Notice that the image size is 1.23GB.

Step 2: Multi-stage

We will split the build process into two stages (a build stage and a runtime stage), to reduce the final image size.

# Build stage
FROM FROM maven:3.9.12-eclipse-temurin-25 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# Runtime stage
FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

The build stage is explained in the previous exercise. The Runtime stage uses a smaller base image (JRE Alpine). It copies the JAR file from the build stage, and rename it to app.jar.

Then we set the ENTRYPOINT for the container.

Only the final stage will be included in the final image (i.e. the runtime stage).

Step 3: Build the image

Now that we have our multi-stage Dockerfile, we can build the Docker image.

docker build -t spring-multi .

Check the size of the newly created image:

docker images

You should get a similar output:

REPOSITORY TAG IMAGE ID CREATED SIZE
spring-multi latest be4d5436413c 3 seconds ago 391MB
spring-api latest 35556a893585 5 minutes ago 1.23GB

Notice that the size of the spring-multi image is significantly smaller than the spring-api image. This is because the final image only contains the JRE and the application JAR file, while the previous image contained the entire build environment, including all dependencies and build tools.

Step 4: Run the Docker container

You can now run the Docker container using the following command:

docker run -p 8081:8080 spring-multi

This command maps port 8080 of the container to port 8081 on your host machine. You can now access the Spring Boot application at http://localhost:8081.

Step 5: Cleanup

After you are done, you can stop the container and remove the images to free up space:

docker stop <container_id>
docker rm <container_id>
docker rmi spring-multi spring-api

Replace <container_id> with the actual container ID or name. You can find the container ID by running docker ps to list all running containers.