dhi.io/maven
Apache Maven is a software project management and comprehension tool.
All examples in this guide use the public image. If you’ve mirrored the repository for your own use (for example, to your Docker Hub namespace), update your commands to reference the mirrored image instead of the public one.
For example:
dhi.io/<repository>:<tag><your-namespace>/dhi-<repository>:<tag>For the examples, you must first use docker login dhi.io to authenticate to the registry to pull the images.
Run the following command to execute Maven commands using a Docker Hardened Image. Replace <tag> with the image
variant you want to run.
$ docker run --rm dhi.io/maven:<tag>-dev --version
Important: Maven DHI images have mvn as their ENTRYPOINT. When using docker run, omit mvn from your commands.
In Dockerfiles, use RUN mvn ... as normal since RUN commands execute in shell context.
Build your Maven project by mounting your source code and running Maven commands:
$ docker run --rm -v "$(pwd)":/app -w /app dhi.io/maven:<tag>-dev clean compile
Create application artifacts like JAR or WAR files by building your Maven project:
$ docker run --rm -v "$(pwd)":/app -w /app dhi.io/maven:<tag>-dev clean package
Important: Maven Docker Hardened Images are build-only tools. They contain no runtime variants because Maven builds applications but doesn't run them. You must use multi-stage Dockerfiles to copy build artifacts to appropriate runtime images.
This example demonstrates building the Spring Pet Clinic application using Maven Docker Hardened Images. Pet Clinic is a canonical Spring Boot sample application that showcases typical enterprise Java development patterns.
# Clone the Spring Pet Clinic repository
$ git clone https://github.com/spring-projects/spring-petclinic.git
$ cd spring-petclinic
# Build using Maven DHI
docker run --rm \
-v "$(pwd)":/app -w /app \
-v maven-repo:/root/.m2 \
dhi.io/maven:<tag>-dev \
clean package -DskipTests
Create a Dockerfile in the Pet Clinic directory:
# syntax=docker/dockerfile:1
# Build stage - Maven DHI for building Pet Clinic
FROM dhi.io/maven:<tag>-dev AS build
WORKDIR /app
# Copy Maven files for dependency caching
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
# Download dependencies (cached layer)
RUN --mount=type=cache,target=/root/.m2 \
mvn dependency:go-offline -B
# Copy source code
COPY src ./src
# Build the Pet Clinic application
RUN --mount=type=cache,target=/root/.m2 \
mvn clean package -DskipTests -B
# Runtime stage - JRE for running Pet Clinic
FROM eclipse-temurin:<tag> AS runtime
# Create non-root user for security
RUN addgroup -g 1001 petclinic && \
adduser -u 1001 -G petclinic -s /bin/sh -D petclinic
WORKDIR /app
# Copy the built JAR from build stage
COPY --from=build /app/target/spring-petclinic-*.jar app.jar
# Change ownership to petclinic user
RUN chown petclinic:petclinic app.jar
USER petclinic
# Pet Clinic runs on port 8080 by default
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# Build the Docker image
$ docker build -t petclinic-hardened .
# Run Pet Clinic
$ docker run --rm -p 8080:8080 --name petclinic petclinic-hardened
# Access Pet Clinic at http://localhost:8080
| Feature | Docker Official Maven | Docker Hardened Maven |
|---|---|---|
| Security | Standard base with common utilities | Custom hardened Debian with security patches |
| Shell access | Direct shell access | Full shell access (requires ENTRYPOINT override) |
| Package manager | Full package managers (apt, dpkg) | No package managers (completely removed) |
| User | Runs as root by default | Runs as root (build environment) |
| Attack surface | Large (424+ utilities, full Ubuntu/Debian) | Minimal (129 utilities, 70% fewer than standard) |
| Runtime variants | Available for some use cases | Not available - build-only tool |
| Debugging | Traditional shell debugging | Use Docker Debug or ENTRYPOINT override |
| Utilities | Full development toolchain (curl, wget, git, vim, tar, make) | Extremely minimal (no curl, wget, git, vim, nano, tar, gzip, unzip, make) |
Docker Hardened Maven images prioritize security through aggressive minimalism:
The hardened images focus exclusively on providing a secure, minimal Maven build environment. After Maven compiles and packages your application, you run the resulting artifacts with appropriate runtime environments.
Docker Hardened Maven images are build-time only. All variants include dev in the tag name and are designed for
use in build stages of multi-stage Dockerfiles.
Maven DHI images follow this tag pattern: <maven-version>-jdk<jdk-version>-<os>-dev
Maven versions:
3.9.16 - Specific patch version (recommended for production)3.9 - Latest patch of 3.9 series3 - Latest minor and patch versionJDK versions:
jdk17 - Java 17 LTS (mature, stable)jdk21 - Java 21 LTS (recommended for new projects)jdk25 - Java 25 LTS (latest)Operating systems:
debian13 - Debian-based (default, ~647MB uncompressed)alpine3.22 - Alpine-based (~578MB uncompressed, ~69MB smaller)alpine3.23 - Alpine-basedalpine3.24 - Alpine-basedFIPS variants include fips in the variant name and tag. Because Maven Docker Hardened Images are build-time only, FIPS
variants are available as dev variants only, for the debian13, alpine3.23, and alpine3.24 operating systems.
These variants use cryptographic modules that have been validated under FIPS 140, a U.S. government standard for secure
cryptographic operations. For example, usage of MD5 fails in FIPS variants.
The FIPS variants configure the JVM through two environment variables: JDK_JAVA_OPTIONS loads the BouncyCastle FIPS
providers (bootstrap class path plus FIPS security properties), and JAVA_TOOL_OPTIONS applies the FIPS security
configuration. Setting your own JAVA_TOOL_OPTIONS (for example, heap flags) is safe — FIPS stays active through
JDK_JAVA_OPTIONS. Do not replace JDK_JAVA_OPTIONS: without it the JVM cannot load the FIPS providers, and TLS
connections fail.
To migrate your Maven builds to Docker Hardened Images, you must update your Dockerfile and build process. Since Maven DHI images are build-only, you must use multi-stage builds.
| Item | Migration note |
|---|---|
| Base image | Replace Maven base images with Docker Hardened Maven dev images in build stages only |
| Multi-stage required | Maven DHI images are build-only. Use multi-stage builds to copy artifacts to runtime images |
| Package management | Package managers are completely removed (in all dev variants) |
| Build user | Maven DHI images run as root during build (appropriate for build environments) |
| Dependency caching | Use Docker cache mounts for /root/.m2 to persist Maven local repository |
| Settings files | Copy or mount Maven settings.xml if using custom repositories |
| Runtime image selection | Choose appropriate JRE/JDK runtime images that match your build JDK version |
| Entry point | Runtime images define entry points; Maven DHI build images use mvn as ENTRYPOINT |
Identify your build requirements
Choose the appropriate Maven DHI dev variant based on your needs:
Convert to multi-stage build
Update your Dockerfile to use Maven DHI dev variant in the build stage:
# Build stage
FROM dhi.io/maven:<tag>-dev AS build
# ... Maven build commands ...
# Runtime stage
FROM eclipse-temurin:<tag> AS runtime
COPY --from=build /app/target/app.jar .
# ... runtime configuration ...
Optimize dependency caching
Copy pom.xml before source code and use cache mounts:
COPY pom.xml .
RUN --mount=type=cache,target=/root/.m2 mvn dependency:go-offline
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn package
Select appropriate runtime image
Choose runtime images that match your build environment:
The following are common issues that you may encounter during migration.
Maven DHI images are build-only tools and contain shell access via ENTRYPOINT override. The recommended method for debugging applications built with Docker Hardened Images is to use Docker Debug to attach to these containers. Docker Debug provides a shell, common debugging tools, and lets you install other tools in an ephemeral, writable layer that only exists during the debugging session.
Maven DHI images run as the root user during builds (appropriate for build environments). When copying build artifacts to runtime stages, ensure that necessary files and directories have appropriate permissions for the runtime image's user context, as runtime images typically run as nonroot users.
Applications built with Maven DHI will typically run in runtime images that use nonroot users by default. As a result, your applications can't bind to privileged ports (below 1024) when running in Kubernetes or in Docker Engine versions older than 20.10. To avoid issues, configure your application to listen on port 1025 or higher inside the container, even if you map it to a lower port on the host. For example, docker run -p 80:8080 my-app will work because the port inside the container is 8080, and docker run -p 80:81 my-app won't work because the port inside the container is 81.
Use dev images in build stages to run shell commands and then copy any necessary artifacts into the runtime stage. In addition, use Docker Debug to debug containers with no shell.
Docker Hardened Images may have different entry points than images such as Docker Official Images. Use docker inspect to inspect entry points for Docker Hardened Images and update your Dockerfile if necessary.