Links#
https://docs.docker.com/build/building/multi-stage/
https://docs.docker.com/build/building/secrets/
https://github.com/adoptium/containers
https://docs.spring.io/spring-boot/reference/packaging/container-images/dockerfiles.html
1. Important Points#
Java container rules:
build with JDK
run with JRE when possible
pass JVM options at runtime
pass Spring profile at runtime
do not bake secrets or env files into image
2. Runtime Only Dockerfile#
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN addgroup --system app && adduser --system --ingroup app app
COPY target/order-api.jar /app/app.jar
USER app
EXPOSE 8080
ENV JAVA_TOOL_OPTIONS=""
ENV APP_ARGS=""
ENTRYPOINT ["sh", "-c", "exec java $JAVA_TOOL_OPTIONS -jar /app/app.jar $APP_ARGS"]
docker build -f Dockerfile.runtime -t order-api:runtime .
docker run --rm -p 8080:8080 \
-e JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75 -Dfile.encoding=UTF-8" \
-e APP_ARGS="--spring.profiles.active=prod" \
order-api:runtime
3. Multi Stage Dockerfile#
# syntax=docker/dockerfile:1
FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY .mvn .mvn
COPY mvnw pom.xml ./
RUN ./mvnw -B -q -DskipTests dependency:go-offline
COPY src src
RUN ./mvnw -B clean package -DskipTests
FROM eclipse-temurin:21-jre AS runtime
WORKDIR /app
RUN addgroup --system app && adduser --system --ingroup app app
COPY --from=build /workspace/target/*.jar /app/app.jar
USER app
EXPOSE 8080
ENV JAVA_TOOL_OPTIONS=""
ENV SPRING_PROFILES_ACTIVE=""
ENV APP_ARGS=""
ENTRYPOINT ["sh", "-c", "exec java $JAVA_TOOL_OPTIONS -jar /app/app.jar --spring.profiles.active=${SPRING_PROFILES_ACTIVE:-default} $APP_ARGS"]
docker build -t order-api:local .
docker run --rm -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e JAVA_TOOL_OPTIONS="-XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=75" \
order-api:local
4. Multi Stage With Maven Cache And Private Repo#
# syntax=docker/dockerfile:1
FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY .mvn .mvn
COPY mvnw pom.xml settings.xml ./
RUN --mount=type=cache,target=/root/.m2/repository \
--mount=type=secret,id=maven_settings,target=/root/.m2/settings.xml \
./mvnw -B -DskipTests dependency:go-offline
COPY src src
RUN --mount=type=cache,target=/root/.m2/repository \
--mount=type=secret,id=maven_settings,target=/root/.m2/settings.xml \
./mvnw -B clean package -DskipTests
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN addgroup --system app && adduser --system --ingroup app app
COPY --from=build /workspace/target/*.jar /app/app.jar
USER app
EXPOSE 8080
ENV JAVA_TOOL_OPTIONS=""
ENV APP_ARGS=""
ENTRYPOINT ["sh", "-c", "exec java $JAVA_TOOL_OPTIONS -jar /app/app.jar $APP_ARGS"]
docker build \
--secret id=maven_settings,src="$HOME/.m2/settings.xml" \
-t order-api:ci .
secret mount:
settings.xml is available during build step
it is not copied into final image layer
5. Spring Boot Layered Jar#
FROM eclipse-temurin:21-jdk AS extract
WORKDIR /workspace
COPY target/order-api.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN addgroup --system app && adduser --system --ingroup app app
COPY --from=extract /workspace/dependencies/ ./
COPY --from=extract /workspace/spring-boot-loader/ ./
COPY --from=extract /workspace/snapshot-dependencies/ ./
COPY --from=extract /workspace/application/ ./
USER app
EXPOSE 8080
ENV JAVA_TOOL_OPTIONS=""
ENV SPRING_PROFILES_ACTIVE=""
ENTRYPOINT ["sh", "-c", "exec java $JAVA_TOOL_OPTIONS org.springframework.boot.loader.launch.JarLauncher --spring.profiles.active=${SPRING_PROFILES_ACTIVE:-default}"]
layered jar:
separates dependencies and application classes
improves rebuild cache when only application code changes
6. Runtime JVM Options#
recommended env:
JAVA_TOOL_OPTIONS:
JVM options recognized automatically by java launcher
APP_ARGS:
application arguments after -jar
SPRING_PROFILES_ACTIVE:
Spring Boot runtime profile
JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8"
APP_ARGS="--server.port=8080 --logging.level.root=INFO"
7. .dockerignore#
.git
target
*.log
.env
.env.*
.idea
.vscode
do not ignore:
pom.xml
mvnw
.mvn
src