docker
maven
dockerize
containerization
software-development

How to dockerize a Maven project? How many ways to accomplish it?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

There are several valid ways to dockerize a Maven project, and each one optimizes a different tradeoff such as control, speed, or simplicity. The most common options are a multi-stage Dockerfile, Jib image builds from Maven, and Cloud Native Buildpacks. The useful comparison is not which one is universally best, but which one fits your team's workflow and runtime constraints.

Method 1: Multi Stage Dockerfile

A multi stage Dockerfile gives maximum control over build steps and runtime image content. You compile in one stage and copy only the final artifact into a slim runtime image.

dockerfile
1# Build stage
2FROM maven:3.9.8-eclipse-temurin-17 AS build
3WORKDIR /workspace
4
5COPY pom.xml .
6RUN mvn -q -DskipTests dependency:go-offline
7
8COPY src ./src
9RUN mvn -q -DskipTests package
10
11# Runtime stage
12FROM eclipse-temurin:17-jre
13WORKDIR /app
14COPY --from=build /workspace/target/demo-app.jar app.jar
15
16EXPOSE 8080
17ENTRYPOINT ["java", "-jar", "app.jar"]

Build and run locally:

bash
docker build -t demo-app:latest .
docker run --rm -p 8080:8080 demo-app:latest

This method is ideal when you need custom OS packages, custom startup scripts, or a non-standard filesystem layout inside the final image.

Method 2: Jib Maven Plugin

Jib builds OCI images directly from your Maven project without writing a Dockerfile. It layers dependencies and classes automatically for better cache reuse in iterative builds.

xml
1<plugin>
2  <groupId>com.google.cloud.tools</groupId>
3  <artifactId>jib-maven-plugin</artifactId>
4  <version>3.4.4</version>
5  <configuration>
6    <from>
7      <image>eclipse-temurin:17-jre</image>
8    </from>
9    <to>
10      <image>registry.example.com/apps/demo-app:1.0.0</image>
11    </to>
12    <container>
13      <mainClass>com.example.Application</mainClass>
14      <ports>
15        <port>8080</port>
16      </ports>
17    </container>
18  </configuration>
19</plugin>

Build image into local Docker daemon:

bash
mvn -DskipTests jib:dockerBuild

Push directly to a registry:

bash
mvn -DskipTests jib:build

Jib is excellent for Java centered teams that want less container boilerplate.

Method 3: Cloud Native Buildpacks

Buildpacks detect project type and assemble an image from curated build and runtime layers.

bash
pack build demo-app:latest --builder paketobuildpacks/builder-jammy-base

If your platform already supports buildpacks, this can standardize builds across multiple services with minimal per repo setup.

The tradeoff is reduced low level control compared with handwritten Dockerfiles.

Choosing the Right Approach

Use this decision model:

  • Choose multi stage Dockerfile when you need full customization and explicit control.
  • Choose Jib when your service is Java only and you want fast, repeatable layering.
  • Choose Buildpacks when platform consistency and low setup overhead are top priorities.

All three methods are production capable when paired with the right security and CI practices.

CI Optimization and Reproducibility

No matter which method you pick, fast and reproducible builds depend on a few habits:

  • Pin base images and plugin versions.
  • Cache Maven dependencies between CI runs.
  • Separate test execution from image build steps.
  • Build with immutable tags for release artifacts.

For Dockerfile flows, copying pom.xml before src improves layer caching because dependency downloads are reused when code changes but dependencies do not.

Runtime and Security Hardening

A successful image build is only the first milestone. Also harden runtime behavior:

  • Prefer JRE runtime images instead of full JDK for smaller attack surface.
  • Run as a non root user.
  • Scan images in CI for known vulnerabilities.
  • Keep dependencies and base images updated on a scheduled cadence.

Example non root setup in Dockerfile:

dockerfile
RUN useradd -u 10001 appuser
USER appuser

For Java in containers, tune memory with JAVA_TOOL_OPTIONS so services do not overconsume memory under orchestration limits.

Common Pitfalls

  • Shipping images that still contain Maven cache and build toolchain in runtime stage.
  • Skipping explicit version pinning, which makes build outputs change unexpectedly.
  • Mixing test failures with image failures so debugging becomes noisy.
  • Pushing images with mutable tags only, making rollback difficult.
  • Hardcoding credentials in Dockerfiles or image layers.

Summary

  • Maven projects can be dockerized with Dockerfiles, Jib, or Buildpacks.
  • Multi stage Dockerfiles provide the highest level of customization.
  • Jib removes Dockerfile overhead and gives efficient Java layers.
  • Buildpacks are strong when platform level standardization is the goal.
  • Reproducibility, security scanning, and runtime hardening matter more than tool choice alone.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.