Week 11

Deployment Strategies

Creating Docker Images

Multi-stage builds

Docker Compose

Container Registries

Deploying to the Cloud

Database Migrations

Practice

Assignment

Backend Track

Optimising Docker image sizes

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.

Watch: Multi-stage build motivation

<aside> 💭

Watch only the 6th best practice explaining multi-stage builds.

</aside>

https://youtu.be/8vXoMqWgbQQ?si=iwL_nGqmpIDJn7g4&t=656

Multi-stage build example

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"]

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>

Faster rebuilds with dependency caching

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.

Extra resources


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.