This Maven plugin provides tools to use docker containers in a maven project. For example, it resolves a local project path so it can be used as a Docker volume path and exposes the result as a Maven property.
The typical use case is a Maven project that starts Docker containers with local volume mappings. On Linux, Docker can
use absolute paths directly. On Windows with Docker in WSL, a path like C:\myproject must be passed as /mnt/c/myproject.
docker-helper:resolve-volume-pathresolves a local path for Docker volume mappings.docker-helper:resolve-user-idresolves the current user's UID/GID as Maven properties.docker-helper:create-directoriescreates host directories for Docker volume mounts with correct ownership.docker-helper:cleanup-containersstops and removes matching containers.docker-helper:execruns a command in a container.
docker-helper:resolve-volume-path
The goal runs in the validate phase by default.
Without further configuration, ${project.basedir} is resolved and stored in the Maven property docker.volumes.resolvedPath.
Examples:
C:\myproject -> /mnt/c/myproject
/home/me/myproject -> /home/me/myproject
<plugin>
<groupId>de.eitco.cicd</groupId>
<artifactId>docker-helper-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>resolve-docker-volume-path</id>
<phase>validate</phase>
<goals>
<goal>resolve-volume-path</goal>
</goals>
</execution>
</executions>
</plugin>The property can then be used in later Maven phases:
${docker.volumes.resolvedPath}<plugin>
<groupId>de.eitco.cicd</groupId>
<artifactId>docker-helper-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>resolve-docker-volume-path</id>
<phase>validate</phase>
<goals>
<goal>resolve-volume-path</goal>
</goals>
<configuration>
<propertyName>my.docker.volume.path</propertyName>
</configuration>
</execution>
</executions>
</plugin>Usage:
${my.docker.volume.path}By default, ${project.basedir} is used. A different path can be configured with localPath.
<configuration>
<localPath>${project.basedir}/src/test/resources</localPath>
<propertyName>test.resources.docker.path</propertyName>
</configuration><volume>${docker.volumes.resolvedPath}:/app</volume>Make sure that resolve-volume-path runs in an earlier phase than the plugin that starts the Docker containers.
| Parameter | Maven Property | Default | Description |
|---|---|---|---|
localPath |
docker.volumes.localPath |
${project.basedir} |
Local path that is resolved for Docker. |
propertyName |
docker.volumes.propertyName |
docker.volumes.resolvedPath |
Name of the Maven property that receives the resolved path. |
docker-helper:resolve-user-id
The goal runs in the validate phase by default.
Without further configuration, the current user's numeric UID and GID are determined and stored in the Maven properties docker.user.uid and docker.user.gid.
- On Linux/Unix,
id -uandid -gare executed. - On Windows,
wsl id -uandwsl id -gare executed. WSL is required and must be installed on the Windows host; it is not required on Linux.
This is useful when running Docker containers with volume mappings from the local host — the container process can be run with the same UID/GID as the host user to maintain correct file permissions.
<plugin>
<groupId>de.eitco.cicd</groupId>
<artifactId>docker-helper-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>resolve-user-id</id>
<phase>validate</phase>
<goals>
<goal>resolve-user-id</goal>
</goals>
</execution>
</executions>
</plugin>The properties can then be used in later Maven phases, for example to pass them to a Docker build or container run command:
<buildArg>USER_ID=${docker.user.uid}</buildArg>
<buildArg>GROUP_ID=${docker.user.gid}</buildArg>Or in a Docker Compose file:
environment:
- USER_ID=${docker.user.uid}
- GROUP_ID=${docker.user.gid}<plugin>
<groupId>de.eitco.cicd</groupId>
<artifactId>docker-helper-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>resolve-user-id</id>
<phase>validate</phase>
<goals>
<goal>resolve-user-id</goal>
</goals>
<configuration>
<uidPropertyName>my.container.uid</uidPropertyName>
<gidPropertyName>my.container.gid</gidPropertyName>
</configuration>
</execution>
</executions>
</plugin>Usage:
${my.container.uid}
${my.container.gid}Advanced users can override the command used to determine UID/GID. For example, to target a specific WSL distribution on Windows:
<configuration>
<uidCommand>
<argument>wsl</argument>
<argument>-d</argument>
<argument>Ubuntu-22.04</argument>
<argument>id</argument>
<argument>-u</argument>
</uidCommand>
<gidCommand>
<argument>wsl</argument>
<argument>-d</argument>
<argument>Ubuntu-22.04</argument>
<argument>id</argument>
<argument>-g</argument>
</gidCommand>
</configuration>| Parameter | Maven Property | Default | Description |
|---|---|---|---|
uidPropertyName |
docker.user.uid.propertyName |
docker.user.uid |
Name of the Maven property that receives the resolved UID. |
gidPropertyName |
docker.user.gid.propertyName |
docker.user.gid |
Name of the Maven property that receives the resolved GID. |
uidCommand |
docker.user.uid.command |
— (auto: id -u on Linux, wsl id -u on Windows) |
Overrides the command used to determine the UID. |
gidCommand |
docker.user.gid.command |
— (auto: id -g on Linux, wsl id -g on Windows) |
Overrides the command used to determine the GID. |
skip |
docker.user.skip |
false |
Skips resolution of the UID/GID. |
docker-helper:create-directories
The goal runs in the validate phase by default.
Creates host directories that are configured to be used as Docker volume mount sources, ensuring they are owned by the current user. This solves a common issue where the Docker daemon auto-creates missing bind-mount source directories as root:root, preventing non-root container processes from writing into them.
The goal is only active when Maven's own JVM runs on Linux (including from within WSL). On Windows and macOS, it is silently skipped (since directory ownership automatically reflects the JVM's running user in those environments, making explicit creation unnecessary).
<plugin>
<groupId>de.eitco.cicd</groupId>
<artifactId>docker-helper-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>create-docker-volume-directories</id>
<phase>validate</phase>
<goals>
<goal>create-directories</goal>
</goals>
<configuration>
<directories>
<directory>${project.build.directory}/logs</directory>
<directory>${project.build.directory}/data</directory>
</directories>
</configuration>
</execution>
</executions>
</plugin><configuration>
<directories>
<directory>${project.basedir}/target/volumes/db</directory>
<directory>${project.basedir}/target/volumes/cache</directory>
</directories>
</configuration>Relative paths are resolved against ${project.basedir}. Missing parent directories are created automatically.
If a directory already exists but is owned by a different user (e.g., a leftover from a prior Docker auto-create or a manual container run):
- Empty directory: The directory is deleted and recreated with correct ownership.
- Non-empty directory: A warning is logged; the directory is not deleted to prevent accidental data loss. Manual remediation (via
chownormvn clean) is required.
If the recreate attempt fails (e.g., permission denied), a warning is logged and the build continues (this is a best-effort recovery for a legacy edge case, not a guaranteed contract).
| Parameter | Maven Property | Default | Description |
|---|---|---|---|
directories |
docker.volumes.directories |
— (empty) | Host directories to create (and their missing parents) before Docker containers start. One <directory> element per path. |
skip |
docker.volumes.directories.skip |
false |
Skips directory creation entirely. |
docker-helper:cleanup-containers lists all Docker containers, matches their names against a regular expression, and stops and removes every match. Docker's leading slash is removed before the name is matched. The Docker daemon is read from DOCKER_HOST by default; tcp:// values are used as HTTP URLs. The goal has no default lifecycle phase, so it can be bound where the build's containers are no longer needed. By default, all unused Docker volumes are removed after the containers are cleaned up; this can be disabled by setting removeUnusedVolumes to false.
<execution>
<id>remove-build-containers</id>
<phase>post-integration-test</phase>
<goals>
<goal>cleanup-containers</goal>
</goals>
<configuration>
<namePattern>my-build-.*</namePattern>
</configuration>
</execution>Failures while stopping or removing an individual matching container are logged as warnings and do not fail the build. Listing containers still fails the goal when the Docker daemon cannot be reached. Responses with which Docker reports an already-reached state are logged at debug level instead of as warnings, because they mean the work was already done: 304 (container was already stopped) and 404 (container no longer exists) when stopping, 404 and 409 (removal already in progress) when removing, and 409 (another prune operation already running) when pruning volumes.
To clean up after all Maven phases have completed, the cleanup can be registered as a JVM shutdown hook. In this mode the goal only registers the hook; it lists, stops, and removes containers when Maven's JVM exits.
<configuration>
<namePattern>my-build-.*</namePattern>
<registerShutdownHook>true</registerShutdownHook>
</configuration>In a multi-module build the hook is registered once per unique combination of Docker host and namePattern. Modules that inherit the configuration from a parent POM therefore produce a single cleanup when Maven's JVM exits, instead of one per module. A module that overrides namePattern or dockerHost registers its own hook, so no cleanup is lost. Duplicate registrations are reported at debug level with mvn -X. When two modules share a host and pattern but configure removeUnusedVolumes differently, the first execution decides.
Volume pruning is serialized within one Maven JVM, because the Docker daemon runs only one prune operation at a time and answers concurrent requests with 409. A 409 caused by another Maven module or external process is logged at debug level and loses nothing, since pruning is a daemon-global operation.
| Parameter | Maven Property | Default | Description |
|---|---|---|---|
dockerHost |
docker.host |
${env.DOCKER_HOST} |
HTTP URL or unix:// socket address of the Docker daemon. tcp:// is accepted and converted to HTTP. |
namePattern |
docker.cleanup.namePattern |
.* |
Regular expression matched against the container name. |
registerShutdownHook |
docker.cleanup.shutdownHook |
false |
Registers cleanup for Maven JVM shutdown instead of executing it immediately. One hook per Docker host and name pattern per build. |
removeUnusedVolumes |
docker.cleanup.removeUnusedVolumes |
true |
Removes unused Docker volumes after stopping and removing matching containers. |
skip |
docker.cleanup.skip |
false |
Skips cleanup completely, including shutdown-hook registration. |
docker-helper:exec executes a command through the Docker API, so no platform-specific shell executable is needed. Configure the container separately and supply every command token as its own argument; the values are passed unchanged and are not interpreted by a shell.
<execution>
<id>db-preparations</id>
<phase>pre-integration-test</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<container>${postgres.container.name}</container>
<arguments>
<argument>psql</argument>
<argument>--username=${database.user}</argument>
<argument>--file=/db-dump/preparations.sql</argument>
<argument>${database.name}</argument>
</arguments>
</configuration>
</execution>interactive maps to docker exec -i and tty to docker exec -t. The goal fails when Docker cannot execute the command or the command returns a non-zero exit code.
| Parameter | Maven Property | Default | Description |
|---|---|---|---|
dockerHost |
docker.host |
${env.DOCKER_HOST} |
HTTP URL or unix:// socket address of the Docker daemon. tcp:// is accepted and converted to HTTP. |
container |
docker.exec.container |
— | Required Docker container name or ID. |
arguments |
docker.exec.arguments |
— | Required command and command arguments; one XML argument per token. |
interactive |
docker.exec.interactive |
false |
Keeps stdin attached (-i). |
tty |
docker.exec.tty |
false |
Allocates a TTY (-t). |
timeoutSeconds |
docker.exec.timeoutSeconds |
10 |
Maximum time to wait for the command to finish, in seconds. |
skip |
docker.exec.skip |
false |
Skips the invocation. |
mvn clean verifyThe directories .github and deployment contain the CI. While the directory .github contains actions that build
each commit and release the project on demand, the directory deployment contains configuration for the release.
A lot of the build however is configured by the project object model (pom.xml).
The .mvn directory activates and configures the maven-git-versioning-extension. This extension changes the
projects version depending on the current branch. This way every branch can be deployed without their artifacts
overriding each other.