(Before I start: even if $JAVA_OPTS
were expanded when running a Maven build at compile time (it isn't expanded, obviously), <jvmFlag>$JAVA_OPTS<jvmFlag>
would still fail, because the entire string value of $JAVA_OPTS
containing multiple JVM flags would be passed as a single argument to the java
binary. For example, -Xms1024m -Xmx2048m
should be passed as two separate flags. The entire string including the whitespace as a single argument is not a valid JVM flag.)
First of all, it'd helpful to understand and figure out who can/should expand variables at which point in time. Variable expansion is not magic, and after all, it's just plain string replacement. Some program, be it Gradle, Maven, a shell program (like bash), JVM, Kubernetes, etc., should be directed to replace strings of the form $FOO
. It could be at container build time (i.e., replaced strings are baked into a built image) or at runtime (i.e., bash replaces strings when it runs).
So, you want to read and specify JVM arguments defined in the base image? If possible, have the base image define JAVA_TOOL_OPTIONS
instead (note not JAVA_TOOL_OPTS
nor JAVA_OPTS
). Most JVMs will honor JAVA_TOOL_OPTIONS
at runtime. See https://mcmap.net/q/745586/-jib-maven-spring-boot-profile for details. (Also note that, container runtimes (docker
, Kubernetes, etc.) can always provide environment variables (and/or override whatever variables defined at build time as container configuration) at runtime. That is, you can dynamically set arguments at runtime.)
Another option, which is a general solution to expand any variables at runtime (i.e., not limited to variables from a base image): define your own <entrypoint>
to use a shell. (Therefore, you need a base image that includes a shell binary (such as /bin/bash
). Note that the default base image prior to Jib 3.0 was Distroless and did not include a shell program. OTOH, Jib 3.0+ doesn't use Distroless.)
In this method, you'll need to know the right Java runtime classpath and the main class to use in your JVM launch command. To help this, starting with Jib >= 3.1, Jib creates two JVM argument files inside a built image; they will hold, respectively, the classpath and the main class inside a built image.
Knowing the entrypoint, you can write a shell script (my-entrypoint.sh
):
#!/bin/sh
# Assumes `java` is on PATH in the base image.
exec java $JAVA_OPTS \
-cp $( cat /app/jib-classpath-file ) \
$( cat /app/jib-main-class-file )
Alternatively, if you are on Java 9+, you can leverage the @-argument file:
exec java $JAVA_OPTS -cp @/app/jib-classpath-file @/app/jib-main-class-file
Place my-entrypoint.sh
under <project root>/src/main/jib
. This is the default directory for Jib's <extraDirectories>
feature, and Jib will place src/main/jib/my-entrypoint.sh
at the root directory in the container image. Then set the default <entrypoint>
to this script:
<container>
<!-- Assumes you have /bin/sh as specified at the top of /my-entrypoint.sh. -->
<entrypoint>/my-entrypoint.sh</entrypoint>
</container>
<!-- You also need to make the script executable. -->
<extraDirectories>
<permissions>
<permission>
<file>/my-entrypoint.sh</file>
<mode>755</mode>
</permission>
</permissions>
</extraDirectories>
Alternatively, if you invoke /bin/sh
as below, you don't have to configure <extraDirectories>
to make the file executable. This may not look customary; you would normally make the script executable and run it directly. But this is perfectly valid, and there is no difference in terms of actual execution (as long as the shebang of /entrypoint.sh
is the same #!/bin/sh
).
<container>
<entrypoint>
<arg>/bin/sh</arg>
<arg>/my-entrypoint.sh</arg>
</entrypoint>
</container>
It's also possible to do this without creating a script (basically embedding the entire script in pom.xml
and passing it to a shell program). In this case, you don't need to configure <extraDirectories>
.
<container>
<entrypoint>
<arg>/bin/sh</arg>
<arg>-c</arg>
<arg>exec java $JAVA_OPTS -cp $( cat /app/jib-classpath-file ) $( cat /app/jib-main-class-file )</arg>
</entrypoint>
</container>