PORT environment variable, and other processes, such as background workers and jobs, run from the same image with their own start commands.
Framework guides
Spring Boot
Deploy Spring Boot 4.1 from Spring Initializr with Maven, Actuator health probes and HTTPS behind the proxy.
Quarkus
Deploy Quarkus 3.40 on the JVM from code.quarkus.io, with SmallRye Health and graceful shutdown.
Micronaut
Deploy Micronaut 5.2 from Micronaut Launch with Gradle, a health probe and graceful shutdown.
Ktor
Deploy a Ktor 3.6 server in Kotlin, built into one executable jar with Gradle and the Ktor Gradle plugin.
How Java applications run on Sevalla
Kotlin applications build the same way as Java ones: Ingot reads the Gradle Kotlin DSL, and the Kotlin Gradle plugin compiles the code during the build.
Pin the Java version
Ingot builds and runs on Java 25, its default line, unless the project names a version. Each project in these guides already names one in its build file, as Spring Initializr, code.quarkus.io and Micronaut Launch do. Ingot takes the version from the first of these:- The
INGOT_JAVA_VERSIONenvironment variable, marked Available during buildtime, orversions.javain ingot.json. - A Gradle toolchain:
jvmToolchain(25)orJavaLanguageVersion.of(25). Gradle compiles with the toolchain’s JDK and fails without it, so the toolchain wins over the version files, and Ingot warns when a version file names another release. .java-version, thenjava=in.sdkmanrc, then.tool-versionsand mise’s configuration.- The build’s own settings. For Maven: the
maven-compiler-pluginrelease,maven.compiler.release, Spring Boot’sjava.version, then the compiler plugin’ssourceandmaven.compiler.source. For Gradle:options.release, thensourceCompatibility. - The default line, Java 25, the newest long-term support release. When the project’s Gradle wrapper can’t run on it, Ingot uses the newest Java release that the wrapper’s Gradle runs on.
25, 25.0.4 and SDKMAN’s 25.0.4-tem all build on the Java 25 line, whose images follow its newest update. The build plan’s Java row shows the version and its source, for example pom.xml java.version "25". See Set the language version.
Size the JVM memory
The JVM reads the memory limit of the container it runs in, which is the memory of the process’s size, and makes the largest heap 25% of it by default. That leaves most of the memory unused. Ingot’s image setsJAVA_TOOL_OPTIONS, which every JVM in the container reads when it starts:
-XX:MaxRAMPercentage=75gives the heap 75% of the limit. The other 25% holds the memory the JVM uses outside the heap: loaded classes (metaspace), the compiled code cache, thread stacks and direct buffers.-XX:+ExitOnOutOfMemoryErrormakes the JVM exit when the heap is full, withTerminating due to java.lang.OutOfMemoryError: Java heap spacein the runtime logs. Sevalla restarts the instance, instead of leaving a JVM running whose threads fail.
Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError at the start of the runtime logs. The Dockerfiles in these guides set the same variable.
On H1, the smallest size (0.3 vCPU and 0.3 GB), the memory limit is 300 MiB, so the largest heap is 226 MiB, and with a limit this small the JVM uses the Serial garbage collector. Each guide’s application, measured on H1 after it started:
These are the guides’ small applications; yours load more classes and hold more data. Watch the Memory usage chart on the application’s Analytics tab, and choose a larger size, such as S1 with 1 GB, when it nears the limit. A slow start on H1 is why the guides add a readiness probe: without one, a new instance receives traffic before the server listens.
To change the flags, set
JAVA_TOOL_OPTIONS as an environment variable marked Available during runtime, and deploy. It replaces the image’s value, so include both flags, for example -XX:MaxRAMPercentage=60 -XX:+ExitOnOutOfMemoryError. If instances restart with exit code 137 instead, the process as a whole ran out of memory: lower the percentage or choose a larger size. See The process runs out of memory.
Build with a Dockerfile
A Dockerfile for a JVM application builds in one stage and runs in another. The build stage uses the official Maven or Gradle image on the JDK of the Java line the project pins, copies the build files first and downloads the dependencies in their own layer, then copies the source and builds the jar without the tests. The final stage starts fromeclipse-temurin:25-jre-resolute, copies in only what runs (the unpacked Spring Boot jar, Quarkus’s fast-jar folder or one executable jar) owned by root, sets JAVA_TOOL_OPTIONS, and runs as user 1000:1000 with an exec-form CMD ["java", ...]: the JVM is process 1 and handles SIGTERM itself, so the image needs no init process. The application reads PORT through its framework’s port property or its own code. Each guide has a tested Dockerfile: Spring Boot, Quarkus, Micronaut and Ktor. See Make the image run on Sevalla.