Docs menu
Customizing the build
How to shape a Java build on Primcloud: custom Maven goals and Gradle tasks, native dependencies, trusting a private CA, and Debian packages.
On this page
Most Java apps deploy on Primcloud with no custom build steps: install a JDK, run Maven or Gradle, and that's it. When you need more, the extension points live in the build tool itself (Maven lifecycle phases, Gradle tasks), a few environment variables, or a project.toml for OS-level packages.
Build hooks#
Pre- and post-build work belongs in the build tool's own extension points, so local and Primcloud builds stay identical.
Maven: bind to a lifecycle phase. Attach an execution to the phase you want to hook. For example, run a script during prepare-package:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>pre-package-script</id>
<phase>prepare-package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>./scripts/pre-package.sh</executable>
</configuration>
</execution>
</executions>
</plugin>
Gradle: declare a task and wire it into the graph. Chain from the stage task so it runs in the standard build:
task stampBuildInfo(type: Exec) {
commandLine './scripts/stamp-build-info.sh'
}
stage.dependsOn stampBuildInfo
Environment variables set on the environment are available to <exec> and Exec tasks at build time.
Custom install steps#
To change the build command itself (Maven goals, Gradle tasks, the JVM heap for the build), use these environment variables:
Maven:
MAVEN_CUSTOM_GOALS: replace the defaultclean installgoals. For example,package -pl web-module -amto package a single module.MAVEN_CUSTOM_OPTS: replace the default-DskipTestsoptions. For example,-DskipTests -P productionto activate a Maven profile.MAVEN_JAVA_OPTS: the JVM flags for the Maven process during the build. The default is-Xmx1024m; raise it if Maven runs out of heap on a large multi-module build.
Gradle:
GRADLE_TASK: replace the defaultstagetask. For example,bootJarto build a Spring Boot fat JAR directly.
Set these in the Secrets panel. They take effect on the next build. To choose the Maven version used when no wrapper is present, set maven.version in system.properties (see Package managers).
Native dependencies#
Two shapes of "native" your Java app can rely on:
JNI libraries bundled in JARs. Libraries like JavaCV, JNA, or Netty's native transports ship platform-specific .so files inside their JARs, extract them at runtime, and load them with System.loadLibrary. No extra setup; they work as long as the JAR carries a Linux x86-64 or ARM64 variant matching Primcloud's runtime image.
OS-level shared libraries your code calls through JNI/JNA/FFM, or that a JAR-bundled native depends on: libssl, libcrypto, libjemalloc, and the like. These come from the operating system, not a JAR. Install them with a project.toml, covered below.
Trusting a private CA#
If your Java code connects over HTTPS to a host whose certificate is signed by a private CA, bundle a trust store in your repo and point the JVM at it. Build the trust store locally by importing your CA certificate, then commit it:
keytool -importcert -alias my-private-ca -file my-ca.crt \
-keystore truststore.jks -storepass changeit -noprompt
Point your launch command at it in the Procfile:
web: java -Djavax.net.ssl.trustStore=truststore.jks -Djavax.net.ssl.trustStorePassword=changeit -Dserver.port=$PORT -jar target/myapp.jar
To keep every JVM on the process using it without editing the command, set the same flags in JAVA_TOOL_OPTIONS in the Secrets panel. Public CAs are trusted out of the box, so you only need this for a private or internal CA.
Debian packages#
When your app needs OS-level libraries or CLI tools that aren't in Primcloud's base image, declare them in a project.toml at the repo root. Primcloud installs them from the Ubuntu package archive during the build:
[_]
schema-version = "0.2"
[com.heroku.buildpacks.deb-packages]
install = [
"libjemalloc2",
"libpq5",
]
Each entry is a Debian package name. The packages are installed into a build layer and their libraries added to LD_LIBRARY_PATH for both build and runtime, so JNI calls at runtime resolve without extra config.