WarpBuild LogoWarpBuild Docs

Java with Maven

Best practices for Dockerfile for Java with Maven

🐳 Annotated Dockerfile for Java with Maven:

# Stage 1: Build the application
FROM eclipse-temurin:21 AS builder
 
# Set working directory
WORKDIR /app
 
# Copy the Maven wrapper files
COPY .mvn/ .mvn/
COPY mvnw mvnw.cmd pom.xml ./
 
# Download dependencies and cache them (will be cached by Docker if pom.xml doesn't change)
RUN --mount=type=cache,target=/root/.m2 \
    ./mvnw dependency:go-offline -B
 
# Copy source code after dependencies to leverage build caching
COPY src/ ./src/
 
# Build the application
RUN --mount=type=cache,target=/root/.m2 \
    ./mvnw package -DskipTests -B
 
# Stage 2: Create runtime image
FROM eclipse-temurin:21-alpine
 
# Add a non-root user to run the app
RUN addgroup -S javauser && \
    adduser -S -G javauser -u 1001 javauser
 
# Set working directory
WORKDIR /app
 
# Copy the built JAR from the builder stage
COPY --from=builder /app/target/*.jar app.jar
 
# Set ownership of the application files to non-root user
RUN chown -R javauser:javauser /app
 
# Switch to non-root user
USER javauser
 
# Expose application port
EXPOSE 8080
 
# Configure JVM options (optimized for containers)
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
 
# Run the application
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar app.jar" ]

🔍 Why these are best practices:

✅ Multi-stage builds

  • Reduces final image size by separating build environment from runtime.
  • Eliminates build tools and dependencies from the final image.
  • Creates a cleaner, smaller, and more secure production image.

✅ Maven dependency caching

  • Uses Docker's build cache to avoid downloading dependencies repeatedly.
  • Dramatically speeds up builds by caching Maven artifacts.
  • Improves CI/CD pipeline efficiency and reduces network usage.

✅ Optimized JRE base image

  • Uses JRE for runtime instead of full JDK to reduce image size.
  • Eclipse Temurin provides a reliable, secure, and enterprise-ready OpenJDK distribution.

✅ Container-optimized Java options

  • XX:+UseContainerSupport ensures JVM recognizes container memory limits.
  • XX:MaxRAMPercentage=75.0 prevents JVM from using all available memory.

✅ Security best practices

  • Runs as a non-root user to enhance container security.
  • Follows the principle of least privilege to limit potential damage from vulnerabilities.
  • Prevents privilege escalation attacks.

🚀 Additional Dockerfile best practices you can adopt:

Use the Maven Wrapper for version consistency

Enforce consistent Maven versions across environments:

# Copy Maven wrapper files
COPY .mvn/ .mvn/
COPY mvnw mvnw.cmd ./
 
# Use Maven wrapper instead of system Maven
RUN ./mvnw clean package

Enable layered JARs (for Spring Boot applications)

Create more granular layers for better cache utilization:

# Extract layered JAR
COPY --from=builder /app/target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
 
# Create layers in order of change frequency
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
 
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

Add health checks

Monitor application health for better container orchestration:

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

Use .dockerignore

Exclude unnecessary files from your Docker build context:

target/
!target/*.jar
.git/
.github/
.gitignore
README.md
Dockerfile
docker-compose.yml

Consider GraalVM Native Image for faster startup and lower memory

For optimal performance in containerized environments:

FROM ghcr.io/graalvm/native-image-community:21 AS builder
WORKDIR /app
COPY . .
RUN ./mvnw -Pnative native:compile -DskipTests
 
FROM oraclelinux:8-slim
COPY --from=builder /app/target/application /app/application
ENTRYPOINT ["/app/application"]

Set appropriate Spring Boot/Java memory settings

Optimize memory usage for containers:

ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0 -Xss512k -XX:+UseG1GC -XX:+UseStringDeduplication"

By following these practices, you'll create Docker images for your Java Maven applications that are secure, efficient, and optimized for both development and production environments. These techniques help minimize build times, reduce image sizes, improve security, and ensure consistent behavior across different deployment environments.

Last updated on