Skip to main content
A Java or Kotlin web application runs on Sevalla as an application: Sevalla builds an image from your Git repository with Maven or Gradle, then runs the application’s jar on the JVM as processes. The web process serves HTTP on the port in the 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.
Each guide builds the application with Ingot, which needs no configuration, and with a Dockerfile of your own.

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:
  1. The INGOT_JAVA_VERSION environment variable, marked Available during buildtime, or versions.java in ingot.json.
  2. A Gradle toolchain: jvmToolchain(25) or JavaLanguageVersion.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.
  3. .java-version, then java= in .sdkmanrc, then .tool-versions and mise’s configuration.
  4. The build’s own settings. For Maven: the maven-compiler-plugin release, maven.compiler.release, Spring Boot’s java.version, then the compiler plugin’s source and maven.compiler.source. For Gradle: options.release, then sourceCompatibility.
  5. 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.
Every source names a feature release: 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 sets JAVA_TOOL_OPTIONS, which every JVM in the container reads when it starts:
  • -XX:MaxRAMPercentage=75 gives 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:+ExitOnOutOfMemoryError makes the JVM exit when the heap is full, with Terminating due to java.lang.OutOfMemoryError: Java heap space in the runtime logs. Sevalla restarts the instance, instead of leaving a JVM running whose threads fail.
The JVM prints 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 from eclipse-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.