Below is an example Dockerfile
that we recommend at Depot for building images for Java applications with Maven.
# syntax=docker/dockerfile:1
FROM eclipse-temurin:21-jdk AS build
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=50.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication" \
MAVEN_HOME=/opt/maven \
MAVEN_CONFIG=/root/.m2 \
MAVEN_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
ARG MAVEN_VERSION=3.9.11
RUN wget -q https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& tar -xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt \
&& ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \
&& rm apache-maven-${MAVEN_VERSION}-bin.tar.gz
ENV PATH="${MAVEN_HOME}/bin:${PATH}"
WORKDIR /app
COPY pom.xml ./
RUN --mount=type=cache,target=/root/.m2 \
mvn dependency:go-offline -B -q
COPY src/ src/
RUN --mount=type=cache,target=/root/.m2 \
mvn clean package -B -DskipTests
FROM eclipse-temurin:21-jre AS runtime
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser
WORKDIR /app
COPY --from=build --chown=appuser:appgroup /app/target/*.jar app.jar
ENV JAVA_OPTS="-server \
-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseG1GC \
-Djava.security.egd=file:/dev/./urandom"
USER appuser
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
At a high level, here are the things we're optimizing in our Docker build for a Java application with Maven:
FROM eclipse-temurin:21-jdk AS build
FROM eclipse-temurin:21-jdk AS build
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=50.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication" \
MAVEN_HOME=/opt/maven \
MAVEN_CONFIG=/root/.m2 \
MAVEN_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
We use Eclipse Temurin 21 JDK for the build stage and configure JVM options for optimal build performance:
UseContainerSupport
enables container-aware memory settingsMaxRAMPercentage=75.0
limits heap to 75% of container memoryUseG1GC
enables the G1 garbage collector for better performanceTieredCompilation
with TieredStopAtLevel=1
speeds up build timesARG MAVEN_VERSION=3.9.11
RUN wget -q https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& tar -xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt \
&& ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \
&& rm apache-maven-${MAVEN_VERSION}-bin.tar.gz
ENV PATH="${MAVEN_HOME}/bin:${PATH}"
We install a specific Maven version for reproducible builds and clean up the downloaded archive to keep the layer small.
WORKDIR /app
COPY pom.xml ./
RUN --mount=type=cache,target=/root/.m2 \
mvn dependency:go-offline -B -q
We copy only the pom.xml
first to leverage Docker layer caching. The dependency:go-offline
goal downloads all dependencies to the local repository, with a cache mount to persist between builds.
COPY src/ src/
RUN --mount=type=cache,target=/root/.m2 \
mvn clean package -B -DskipTests
After copying the source code, we build the application with the same cache mount. The -B
flag enables batch mode, and -DskipTests
skips running tests during the build (tests should be run in CI/CD pipeline).
FROM eclipse-temurin:21-jre AS runtime
FROM eclipse-temurin:21-jre AS runtime
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser
WORKDIR /app
COPY --from=build --chown=appuser:appgroup /app/target/*.jar app.jar
The runtime stage uses Eclipse Temurin JRE for a reliable runtime environment. We create a non-root user for security and copy only the built JAR file from the build stage.
ENV JAVA_OPTS="-server \
-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseG1GC \
-Djava.security.egd=file:/dev/./urandom"
USER appuser
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
We configure production JVM settings:
-server
enables server mode for better long-running performanceUseContainerSupport
and MaxRAMPercentage
for container-aware memory managementUseG1GC
enables the G1 garbage collector for better performancejava.security.egd
uses /dev/urandom
for faster startupCache mounts are one of the most powerful features for optimizing Docker builds with Depot. This Dockerfile uses the following cache mount syntax:
RUN --mount=type=cache,target=/root/.m2 \
mvn dependency:go-offline -B -q
type=cache
: Specifies this is a cache mount that persists across builds.target=/root/.m2
: The mount point for Maven's local repository where all downloaded JARs, POMs, and metadata are stored.For more information regarding Maven cache mounts, please visit the official Maven documentation.